From d7a4e5e816bb9b0debdd0a3030a796f0fa50e780 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Mon, 3 Feb 2014 13:00:28 -0500 Subject: [PATCH] Implement getreceivedbyaccount. Closes #42. --- account.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++-- rpcserver.go | 38 +++++++++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/account.go b/account.go index d110c3f..5609c25 100644 --- a/account.go +++ b/account.go @@ -140,7 +140,7 @@ func (a *Account) CalculateBalance(confirms int) float64 { for _, u := range a.UtxoStore { // Utxos not yet in blocks (height -1) should only be // added if confirmations is 0. - if confirms == 0 || (u.Height != -1 && int(bs.Height-u.Height+1) >= confirms) { + if confirmed(confirms, u.Height, bs.Height) { bal += u.Amt } } @@ -166,7 +166,7 @@ func (a *Account) CalculateAddressBalance(addr *btcutil.AddressPubKeyHash, confi for _, u := range a.UtxoStore { // Utxos not yet in blocks (height -1) should only be // added if confirmations is 0. - if confirms == 0 || (u.Height != -1 && int(bs.Height-u.Height+1) >= confirms) { + if confirmed(confirms, u.Height, bs.Height) { if bytes.Equal(addr.ScriptAddress(), u.AddrHash[:]) { bal += u.Amt } @@ -598,3 +598,53 @@ func ReqSpentUtxoNtfn(u *tx.Utxo) { NotifySpent(CurrentServerConn(), (*btcwire.OutPoint)(&u.Out)) } + +// TotalReceived iterates through an account's transaction history, returning the +// total amount of bitcoins received for any account address. Amounts received +// through multisig transactions are ignored. +func (a *Account) TotalReceived(confirms int) (float64, error) { + bs, err := GetCurBlock() + if err != nil { + return 0, err + } + + var totalSatoshis int64 + for _, e := range a.TxStore { + recvtx, ok := e.(*tx.RecvTx) + if !ok { + continue + } + + // Ignore change. + addr, err := btcutil.NewAddressPubKeyHash(recvtx.ReceiverHash, cfg.Net()) + if err != nil { + continue + } + info, err := a.Wallet.AddressInfo(addr) + if err != nil { + continue + } + if info.Change { + continue + } + + // Tally if the appropiate number of block confirmations have passed. + if confirmed(confirms, recvtx.Height(), bs.Height) { + totalSatoshis += recvtx.Amount + } + } + + return float64(totalSatoshis) / float64(btcutil.SatoshiPerBitcoin), nil +} + +// confirmed checks whether a transaction at height txHeight has met +// minconf confirmations for a blockchain at height curHeight. +func confirmed(minconf int, txHeight, curHeight int32) bool { + if minconf == 0 { + return true + } + if txHeight != -1 && int(curHeight-txHeight+1) >= minconf { + return true + } + return false +} diff --git a/rpcserver.go b/rpcserver.go index 7faa0bc..a205016 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -40,6 +40,7 @@ var rpcHandlers = map[string]cmdHandler{ "getinfo": GetInfo, "getnewaddress": GetNewAddress, "getrawchangeaddress": GetRawChangeAddress, + "getreceivedbyaccount": GetReceivedByAccount, "importprivkey": ImportPrivKey, "keypoolrefill": KeypoolRefill, "listaccounts": ListAccounts, @@ -58,7 +59,6 @@ var rpcHandlers = map[string]cmdHandler{ "createmultisig": Unimplemented, "dumpwallet": Unimplemented, "getblocktemplate": Unimplemented, - "getreceivedbyaccount": Unimplemented, "getreceivedbyaddress": Unimplemented, "gettransaction": Unimplemented, "gettxout": Unimplemented, @@ -745,6 +745,42 @@ func GetRawChangeAddress(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { return addr.EncodeAddress(), nil } +// GetReceivedByAccount handles a getreceivedbyaccount request by returning +// the total amount received by addresses of an account. +func GetReceivedByAccount(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { + cmd, ok := icmd.(*btcjson.GetReceivedByAccountCmd) + if !ok { + return nil, &btcjson.ErrInternal + } + + a, err := AcctMgr.Account(cmd.Account) + switch err { + case nil: + break + + case ErrNotFound: + return nil, &btcjson.ErrWalletInvalidAccountName + + default: // all other non-nil errors + e := btcjson.Error{ + Code: btcjson.ErrWallet.Code, + Message: err.Error(), + } + return nil, &e + } + + amt, err := a.TotalReceived(cmd.MinConf) + if err != nil { + e := btcjson.Error{ + Code: btcjson.ErrWallet.Code, + Message: err.Error(), + } + return nil, &e + } + + return amt, nil +} + // ListAccounts handles a listaccounts request by returning a map of account // names to their balances. func ListAccounts(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {