Implement listsinceblock command

Closes #52
This commit is contained in:
Owain G. Ainsworth 2014-01-27 17:53:32 +00:00
parent 845d54da55
commit 34b683b4aa
5 changed files with 121 additions and 1 deletions

View file

@ -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.

View file

@ -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.
//

View file

@ -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

View file

@ -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) {

View file

@ -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
}