diff --git a/account.go b/account.go index 0b38a40..3506bda 100644 --- a/account.go +++ b/account.go @@ -224,6 +224,39 @@ func (a *Account) ListTransactions(from, count int) ([]map[string]interface{}, e return txInfoList, nil } +// ListAddressTransactions returns a slice of maps with details about a +// recorded transactions to or from any address belonging to a set. This is +// intended to be used for listaddresstransactions RPC replies. +func (a *Account) ListAddressTransactions(pkHashes map[string]struct{}) ( + []map[string]interface{}, error) { + + // Get current block. The block height used for calculating + // the number of tx confirmations. + bs, err := GetCurBlock() + if err != nil { + return nil, err + } + + var txInfoList []map[string]interface{} + a.mtx.RLock() + a.TxStore.RLock() + + for i := range a.TxStore.s { + rtx, ok := a.TxStore.s[i].(*tx.RecvTx) + if !ok { + continue + } + if _, ok := pkHashes[string(rtx.ReceiverHash[:])]; ok { + info := rtx.TxInfo(a.name, bs.Height, a.Net()) + txInfoList = append(txInfoList, info) + } + } + a.mtx.RUnlock() + a.TxStore.RUnlock() + + return txInfoList, nil +} + // ListAllTransactions returns a slice of maps with details about a recorded // transaction. This is intended to be used for listalltransactions RPC // replies. diff --git a/cmdmgr.go b/cmdmgr.go index 83ec6b0..c944c24 100644 --- a/cmdmgr.go +++ b/cmdmgr.go @@ -61,10 +61,11 @@ var rpcHandlers = map[string]cmdHandler{ // Extensions exclusive to websocket connections. var wsHandlers = map[string]cmdHandler{ - "getaddressbalance": GetAddressBalance, - "getbalances": GetBalances, - "listalltransactions": ListAllTransactions, - "walletislocked": WalletIsLocked, + "getaddressbalance": GetAddressBalance, + "getbalances": GetBalances, + "listaddresstransactions": ListAddressTransactions, + "listalltransactions": ListAllTransactions, + "walletislocked": WalletIsLocked, } // ProcessRequest checks the requests sent from a frontend. If the @@ -514,7 +515,66 @@ func ListTransactions(frontend chan []byte, icmd btcjson.Cmd) { } } -// ListAllTransactions replies to a listtransactions request by returning +// ListAddressTransactions replies to a listaddresstransactions request by +// returning an array of JSON objects with details of spent and received +// wallet transactions. The form of the reply is identical to +// listtransactions, but the array elements are limited to transaction +// details which are about the addresess included in the request. +func ListAddressTransactions(frontend chan []byte, icmd btcjson.Cmd) { + // Type assert icmd to access parameters. + cmd, ok := icmd.(*btcws.ListAddressTransactionsCmd) + if !ok { + ReplyError(frontend, icmd.Id(), &btcjson.ErrInternal) + return + } + + a, err := accountstore.Account(cmd.Account) + switch err { + case nil: + break + + case ErrAcctNotExist: + ReplyError(frontend, cmd.Id(), + &btcjson.ErrWalletInvalidAccountName) + return + + default: // all other non-nil errors + e := &btcjson.Error{ + Code: btcjson.ErrWallet.Code, + Message: err.Error(), + } + ReplyError(frontend, cmd.Id(), e) + return + } + + // Parse hash160s out of addresses. + pkHashMap := make(map[string]struct{}) + for _, addr := range cmd.Addresses { + pkHash, net, err := btcutil.DecodeAddress(addr) + if err != nil || net != cfg.Net() { + e := &btcjson.Error{ + Code: btcjson.ErrInvalidParams.Code, + Message: "invalid address", + } + ReplyError(frontend, cmd.Id(), e) + return + } + pkHashMap[string(pkHash)] = struct{}{} + } + + txList, err := a.ListAddressTransactions(pkHashMap) + if err != nil { + e := &btcjson.Error{ + Code: btcjson.ErrWallet.Code, + Message: err.Error(), + } + ReplyError(frontend, cmd.Id(), e) + return + } + ReplySuccess(frontend, cmd.Id(), txList) +} + +// ListAllTransactions replies to a listalltransactions request by returning // an array of JSON objects with details of sent and recevied wallet // transactions. This is similar to ListTransactions, except it takes // only a single optional argument for the account name and replies with