From 34b683b4aa733edf6649006d2cf7d8bbde011904 Mon Sep 17 00:00:00 2001 From: "Owain G. Ainsworth" Date: Mon, 27 Jan 2014 17:53:32 +0000 Subject: [PATCH] Implement listsinceblock command Closes #52 --- account.go | 24 +++++++++++++++++++ accountstore.go | 19 +++++++++++++++ btcdrpc.go | 2 +- cmdmgr.go | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ tx/tx.go | 13 ++++++++++ 5 files changed, 121 insertions(+), 1 deletion(-) diff --git a/account.go b/account.go index 83f7d52..4ae33cd 100644 --- a/account.go +++ b/account.go @@ -271,6 +271,30 @@ func (a *Account) CurrentAddress() (btcutil.Address, error) { return addr, nil } +// ListSinceBlock returns a slice of maps with details about transactions since +// the given block. If the block is -1 then all transactions are included. +// transaction. This is intended to be used for listsinceblock RPC +// replies. +func (a *Account) ListSinceBlock(since, curBlockHeight int32, minconf int) ([]map[string]interface{}, error) { + var txInfoList []map[string]interface{} + a.mtx.RLock() + defer a.mtx.RUnlock() + a.TxStore.RLock() + defer a.TxStore.RUnlock() + + for _, tx := range a.TxStore.s { + // check block number. + if since != -1 && tx.Height() <= since { + continue + } + + txInfoList = append(txInfoList, + tx.TxInfo(a.name, curBlockHeight, a.Net())...) + } + + return txInfoList, nil +} + // ListTransactions returns a slice of maps with details about a recorded // transaction. This is intended to be used for listtransactions RPC // replies. diff --git a/accountstore.go b/accountstore.go index f620023..413efeb 100644 --- a/accountstore.go +++ b/accountstore.go @@ -405,6 +405,25 @@ func (store *AccountStore) ListAccounts(minconf int) map[string]float64 { return pairs } +// ListSinceBlock returns a slice of maps of strings to interface containing +// structures defining all transactions in the wallets since the given block. +// To be used for the listsinceblock command. +func (store *AccountStore) ListSinceBlock(since, curBlockHeight int32, minconf int) ([]map[string]interface{}, error) { + store.RLock() + defer store.RUnlock() + + // Create and fill a map of account names and their balances. + txInfoList := []map[string]interface{}{} + for _, a := range store.accounts { + txTmp, err := a.ListSinceBlock(since, curBlockHeight, minconf) + if err != nil { + return nil, err + } + txInfoList = append(txInfoList, txTmp...) + } + return txInfoList, nil +} + // RescanActiveAddresses begins a rescan for all active addresses for // each account. // diff --git a/btcdrpc.go b/btcdrpc.go index 563ce24..ff1df00 100644 --- a/btcdrpc.go +++ b/btcdrpc.go @@ -83,7 +83,7 @@ func (btcd *BtcdRPCConn) SendRequest(request *RPCRequest) chan *RPCResponse { default: addRequest := &AddRPCRequest{ Request: request, - ResponseChan: make(chan chan *RPCResponse), + ResponseChan: make(chan chan *RPCResponse, 1), } btcd.addRequest <- addRequest return <-addRequest.ResponseChan diff --git a/cmdmgr.go b/cmdmgr.go index ce30380..9492019 100644 --- a/cmdmgr.go +++ b/cmdmgr.go @@ -41,6 +41,7 @@ var rpcHandlers = map[string]cmdHandler{ "importprivkey": ImportPrivKey, "keypoolrefill": KeypoolRefill, "listaccounts": ListAccounts, + "listsinceblock": ListSinceBlock, "listtransactions": ListTransactions, "sendfrom": SendFrom, "sendmany": SendMany, @@ -629,6 +630,69 @@ func ListAccounts(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { return accountstore.ListAccounts(cmd.MinConf), nil } +// ListSinceBlock handles a listsinceblock request by returning an array of maps +// with details of sent and received wallet transactions since the given block. +func ListSinceBlock(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { + cmd, ok := icmd.(*btcjson.ListSinceBlockCmd) + if !ok { + return nil, &btcjson.ErrInternal + } + + height := int32(-1) + if cmd.BlockHash != "" { + br, err := GetBlock(CurrentRPCConn(), cmd.BlockHash) + if err != nil { + return nil, err + } + height = int32(br.Height) + } + + bs, err := GetCurBlock() + if err != nil { + return nil, &btcjson.Error{ + Code: btcjson.ErrWallet.Code, + Message: err.Error(), + } + } + + // For the result we need the block hash for the last block counted + // in the blockchain due to confirmations. We send this off now so that + // it can arrive asynchronously while we figure out the rest. + gbh, err := btcjson.NewGetBlockHashCmd(<-NewJSONID, + int64(bs.Height)+1-int64(cmd.TargetConfirmations)) + if err != nil { + return nil, &btcjson.Error{ + Code: btcjson.ErrWallet.Code, + Message: err.Error(), + } + } + + bhChan := CurrentRPCConn().SendRequest(NewRPCRequest(gbh, new(string))) + + txInfoList, err := accountstore.ListSinceBlock(height, bs.Height, + cmd.TargetConfirmations) + if err != nil { + return nil, &btcjson.Error{ + Code: btcjson.ErrWallet.Code, + Message: err.Error(), + } + } + + // Done with work, get the response. + response := <-bhChan + if response.Err != nil { + return nil, response.Err + } + + hash := response.Result.(*string) + + res := make(map[string]interface{}) + res["transactions"] = txInfoList + res["lastblock"] = *hash + + return res, nil +} + // ListTransactions handles a listtransactions request by returning an // array of maps with details of sent and recevied wallet transactions. func ListTransactions(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { diff --git a/tx/tx.go b/tx/tx.go index acab561..9aa073f 100644 --- a/tx/tx.go +++ b/tx/tx.go @@ -108,6 +108,7 @@ type Tx interface { io.WriterTo ReadFromVersion(uint32, io.Reader) (int64, error) TxInfo(string, int32, btcwire.BitcoinNet) []map[string]interface{} + Height() int32 } // TxStore is a slice holding RecvTx and SendTx pointers. @@ -1060,6 +1061,12 @@ func (tx *RecvTx) TxInfo(account string, curheight int32, return []map[string]interface{}{txInfo} } +// Height returns the current blockheight of the transaction, implementing the +// Tx interface. +func (tx *RecvTx) Height() int32 { + return tx.BlockHeight +} + func (tx *SendTx) ReadFromVersion(vers uint32, r io.Reader) (n int64, err error) { var read int64 @@ -1197,3 +1204,9 @@ func (tx *SendTx) TxInfo(account string, curheight int32, return reply } + +// Height returns the current blockheight of the transaction, implementing the +// Tx interface. +func (tx *SendTx) Height() int32 { + return tx.BlockHeight +}