From e65206f752758dbd9786f5c1e137ab1fbf98f904 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Wed, 6 Nov 2013 11:23:30 -0500 Subject: [PATCH] Begin using btcws. This change begins using the btcws package for marshaling custom commands used for websocket connections to btcd. --- cmd.go | 94 +++++++++++++++++++++++++++--------------------- cmdmgr.go | 2 +- sockets.go | 13 ++++--- wallet/wallet.go | 28 +++++++++++++-- 4 files changed, 87 insertions(+), 50 deletions(-) diff --git a/cmd.go b/cmd.go index 4bdb70a..6c1a790 100644 --- a/cmd.go +++ b/cmd.go @@ -18,7 +18,6 @@ package main import ( "bytes" - "encoding/json" "errors" "fmt" "github.com/conformal/btcjson" @@ -26,6 +25,7 @@ import ( "github.com/conformal/btcwallet/tx" "github.com/conformal/btcwallet/wallet" "github.com/conformal/btcwire" + "github.com/conformal/btcws" "os" "path/filepath" "sync" @@ -244,12 +244,13 @@ func GetCurBlock() (bs wallet.BlockStamp, err error) { } n := <-NewJSONID - msg := btcjson.Message{ - Jsonrpc: "1.0", - Id: fmt.Sprintf("btcwallet(%v)", n), - Method: "getbestblock", + cmd := btcws.NewGetBestBlockCmd(fmt.Sprintf("btcwallet(%v)", n)) + mcmd, err := cmd.MarshalJSON() + if err != nil { + return wallet.BlockStamp{ + Height: int32(btcutil.BlockHeightUnknown), + }, errors.New("cannot ask for best block") } - m, _ := json.Marshal(msg) c := make(chan *struct { hash *btcwire.ShaHash @@ -294,7 +295,7 @@ func GetCurBlock() (bs wallet.BlockStamp, err error) { replyHandlers.Unlock() // send message - btcdMsgs <- m + btcdMsgs <- mcmd // Block until reply is ready. if reply := <-c; reply != nil { @@ -402,17 +403,17 @@ func (w *BtcWallet) RescanToBestBlock() { } n := <-NewJSONID - params := []interface{}{ - beginBlock, - w.ActivePaymentAddresses(), + cmd, err := btcws.NewRescanCmd(fmt.Sprintf("btcwallet(%v)", n), + beginBlock, w.ActivePaymentAddresses()) + if err != nil { + log.Errorf("cannot create rescan request: %v", err) + return } - m := &btcjson.Message{ - Jsonrpc: "1.0", - Id: fmt.Sprintf("btcwallet(%v)", n), - Method: "rescan", - Params: params, + mcmd, err := cmd.MarshalJSON() + if err != nil { + log.Errorf("cannot create rescan request: %v", err) + return } - msg, _ := json.Marshal(m) replyHandlers.Lock() replyHandlers.m[n] = func(result interface{}, e *btcjson.Error) bool { @@ -443,21 +444,36 @@ func (w *BtcWallet) RescanToBestBlock() { } replyHandlers.Unlock() - btcdMsgs <- msg + btcdMsgs <- mcmd } -// ActivePaymentAddresses returns the second parameter for all rescan -// commands. The returned slice maps between payment address strings and -// the block height to begin rescanning for transactions to that address. -func (w *BtcWallet) ActivePaymentAddresses() []string { +// SortedActivePaymentAddresses returns a slice of all active payment +// addresses in an account. +func (w *BtcWallet) SortedActivePaymentAddresses() []string { + w.mtx.RLock() + defer w.mtx.RUnlock() + + infos := w.GetSortedActiveAddresses() + addrs := make([]string, len(infos)) + + for i, addr := range infos { + addrs[i] = addr.Address + } + + return addrs +} + +// ActivePaymentAddresses returns a set of all active pubkey hashes +// in an account. +func (w *BtcWallet) ActivePaymentAddresses() map[string]struct{} { w.mtx.RLock() defer w.mtx.RUnlock() infos := w.GetActiveAddresses() - addrs := make([]string, len(infos)) + addrs := make(map[string]struct{}, len(infos)) - for i := range infos { - addrs[i] = infos[i].Address + for _, info := range infos { + addrs[info.Address] = struct{}{} } return addrs @@ -472,15 +488,14 @@ func (w *BtcWallet) ReqNewTxsForAddress(addr string) { n := w.NewBlockTxSeqN w.mtx.RUnlock() - m := &btcjson.Message{ - Jsonrpc: "1.0", - Id: fmt.Sprintf("btcwallet(%d)", n), - Method: "notifynewtxs", - Params: []interface{}{addr}, + cmd := btcws.NewNotifyNewTXsCmd(fmt.Sprintf("btcwallet(%d)", n), + []string{addr}) + mcmd, err := cmd.MarshalJSON() + if err != nil { + log.Errorf("cannot request transaction notifications: %v", err) } - msg, _ := json.Marshal(m) - btcdMsgs <- msg + btcdMsgs <- mcmd } // ReqSpentUtxoNtfn sends a message to btcd to request updates for when @@ -493,18 +508,15 @@ func (w *BtcWallet) ReqSpentUtxoNtfn(u *tx.Utxo) { n := w.SpentOutpointSeqN w.mtx.RUnlock() - m := &btcjson.Message{ - Jsonrpc: "1.0", - Id: fmt.Sprintf("btcwallet(%d)", n), - Method: "notifyspent", - Params: []interface{}{ - u.Out.Hash.String(), - u.Out.Index, - }, + cmd := btcws.NewNotifySpentCmd(fmt.Sprintf("btcwallet(%d)", n), + (*btcwire.OutPoint)(&u.Out)) + mcmd, err := cmd.MarshalJSON() + if err != nil { + log.Errorf("cannot create spent request: %v", err) + return } - msg, _ := json.Marshal(m) - btcdMsgs <- msg + btcdMsgs <- mcmd } // spentUtxoHandler is the handler function for btcd spent UTXO notifications diff --git a/cmdmgr.go b/cmdmgr.go index cea05b4..3195c5c 100644 --- a/cmdmgr.go +++ b/cmdmgr.go @@ -139,7 +139,7 @@ func GetAddressesByAccount(reply chan []byte, msg *btcjson.Message) { var result []string if w := wallets.m[account]; w != nil { - result = w.ActivePaymentAddresses() + result = w.SortedActivePaymentAddresses() } else { ReplyError(reply, msg.Id, &btcjson.ErrWalletInvalidAccountName) return diff --git a/sockets.go b/sockets.go index 74bb4fc..dd1c50a 100644 --- a/sockets.go +++ b/sockets.go @@ -25,6 +25,7 @@ import ( "github.com/conformal/btcjson" "github.com/conformal/btcwallet/wallet" "github.com/conformal/btcwire" + "github.com/conformal/btcws" "net" "net/http" "sync" @@ -562,11 +563,13 @@ func BtcdConnect(reply chan error) { // be tracked against chain notifications from this btcd connection. func BtcdHandshake(ws *websocket.Conn) { n := <-NewJSONID - msg := btcjson.Message{ - Method: "getcurrentnet", - Id: fmt.Sprintf("btcwallet(%v)", n), + cmd := btcws.NewGetCurrentNetCmd(fmt.Sprintf("btcwallet(%v)", n)) + mcmd, err := cmd.MarshalJSON() + if err != nil { + log.Errorf("Cannot complete btcd handshake: %v", err) + ws.Close() + return } - m, _ := json.Marshal(&msg) correctNetwork := make(chan bool) @@ -594,7 +597,7 @@ func BtcdHandshake(ws *websocket.Conn) { } replyHandlers.Unlock() - btcdMsgs <- m + btcdMsgs <- mcmd if !<-correctNetwork { log.Error("btcd and btcwallet running on different Bitcoin networks") diff --git a/wallet/wallet.go b/wallet/wallet.go index 66bcd5b..dda17df 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -775,14 +775,16 @@ func (w *Wallet) addr160ForIdx(idx int64) (addressHashKey, error) { // a complete wallet. type AddressInfo struct { Address string + AddrHash string FirstBlock int32 Compressed bool } -// GetActiveAddresses returns all wallet addresses that have been +// GetSortedActiveAddresses returns all wallet addresses that have been // requested to be generated. These do not include pre-generated -// addresses. -func (w *Wallet) GetActiveAddresses() []*AddressInfo { +// addresses. Use this when ordered addresses are needed. Otherwise, +// GetActiveAddresses is preferred. +func (w *Wallet) GetSortedActiveAddresses() []*AddressInfo { addrs := make([]*AddressInfo, 0, w.highestUsed+1) for i := int64(-1); i <= w.highestUsed; i++ { addr160, err := w.addr160ForIdx(i) @@ -798,6 +800,25 @@ func (w *Wallet) GetActiveAddresses() []*AddressInfo { return addrs } +// GetActiveAddresses returns a map between active payment addresses +// and their full info. These do not include pre-generated addresses. +// If addresses must be sorted, use GetSortedActiveAddresses. +func (w *Wallet) GetActiveAddresses() map[string]*AddressInfo { + addrs := make(map[string]*AddressInfo) + for i := int64(-1); i <= w.highestUsed; i++ { + addr160, err := w.addr160ForIdx(i) + if err != nil { + return addrs + } + addr := w.addrMap[addr160] + info, err := addr.info(w.Net()) + if err == nil { + addrs[info.Address] = info + } + } + return addrs +} + type walletFlags struct { useEncryption bool watchingOnly bool @@ -1181,6 +1202,7 @@ func (a *btcAddress) info(net btcwire.BitcoinNet) (*AddressInfo, error) { return &AddressInfo{ Address: address, + AddrHash: string(a.pubKeyHash[:]), FirstBlock: a.firstBlock, Compressed: a.flags.compressed, }, nil