diff --git a/account.go b/account.go index bac01ed..90cb1e2 100644 --- a/account.go +++ b/account.go @@ -104,18 +104,17 @@ func (a *Account) AddressUsed(addr btcutil.Address) bool { // a UTXO must be in a block. If confirmations is 1 or greater, // the balance will be calculated based on how many how many blocks // include a UTXO. -func (a *Account) CalculateBalance(confirms int) float64 { - bs, err := GetCurBlock() - if bs.Height == int32(btcutil.BlockHeightUnknown) || err != nil { - return 0. +func (a *Account) CalculateBalance(confirms int) (btcutil.Amount, error) { + rpcc, err := accessClient() + if err != nil { + return 0, err + } + bs, err := rpcc.BlockStamp() + if err != nil { + return 0, err } - bal, err := a.TxStore.Balance(confirms, bs.Height) - if err != nil { - log.Errorf("Cannot calculate balance: %v", err) - return 0 - } - return bal.ToUnit(btcutil.AmountBTC) + return a.TxStore.Balance(confirms, bs.Height) } // CalculateAddressBalance sums the amounts of all unspent transaction @@ -127,16 +126,20 @@ func (a *Account) CalculateBalance(confirms int) float64 { // a UTXO must be in a block. If confirmations is 1 or greater, // the balance will be calculated based on how many how many blocks // include a UTXO. -func (a *Account) CalculateAddressBalance(addr btcutil.Address, confirms int) float64 { - bs, err := GetCurBlock() - if bs.Height == int32(btcutil.BlockHeightUnknown) || err != nil { - return 0. +func (a *Account) CalculateAddressBalance(addr btcutil.Address, confirms int) (btcutil.Amount, error) { + rpcc, err := accessClient() + if err != nil { + return 0, err + } + bs, err := rpcc.BlockStamp() + if err != nil { + return 0, err } var bal btcutil.Amount unspent, err := a.TxStore.UnspentOutputs() if err != nil { - return 0. + return 0, err } for _, credit := range unspent { if credit.Confirmed(confirms, bs.Height) { @@ -151,7 +154,7 @@ func (a *Account) CalculateAddressBalance(addr btcutil.Address, confirms int) fl } } } - return bal.ToUnit(btcutil.AmountBTC) + return bal, nil } // CurrentAddress gets the most recently requested Bitcoin payment address @@ -203,14 +206,18 @@ func (a *Account) ListSinceBlock(since, curBlockHeight int32, // transaction. This is intended to be used for listtransactions RPC // replies. func (a *Account) ListTransactions(from, count int) ([]btcjson.ListTransactionsResult, error) { + txList := []btcjson.ListTransactionsResult{} + // Get current block. The block height used for calculating // the number of tx confirmations. - bs, err := GetCurBlock() + rpcc, err := accessClient() if err != nil { - return nil, err + return txList, err + } + bs, err := rpcc.BlockStamp() + if err != nil { + return txList, err } - - txList := []btcjson.ListTransactionsResult{} records := a.TxStore.Records() lastLookupIdx := len(records) - count @@ -232,14 +239,19 @@ func (a *Account) ListTransactions(from, count int) ([]btcjson.ListTransactionsR func (a *Account) ListAddressTransactions(pkHashes map[string]struct{}) ( []btcjson.ListTransactionsResult, error) { + txList := []btcjson.ListTransactionsResult{} + // Get current block. The block height used for calculating // the number of tx confirmations. - bs, err := GetCurBlock() + rpcc, err := accessClient() if err != nil { - return nil, err + return txList, err + } + bs, err := rpcc.BlockStamp() + if err != nil { + return txList, err } - txList := []btcjson.ListTransactionsResult{} for _, r := range a.TxStore.Records() { for _, c := range r.Credits() { // We only care about the case where len(addrs) == 1, @@ -271,16 +283,21 @@ func (a *Account) ListAddressTransactions(pkHashes map[string]struct{}) ( // transaction. This is intended to be used for listalltransactions RPC // replies. func (a *Account) ListAllTransactions() ([]btcjson.ListTransactionsResult, error) { + txList := []btcjson.ListTransactionsResult{} + // Get current block. The block height used for calculating // the number of tx confirmations. - bs, err := GetCurBlock() + rpcc, err := accessClient() if err != nil { - return nil, err + return txList, err + } + bs, err := rpcc.BlockStamp() + if err != nil { + return txList, err } // Search in reverse order: lookup most recently-added first. records := a.TxStore.Records() - txList := []btcjson.ListTransactionsResult{} for i := len(records) - 1; i >= 0; i-- { jsonResults, err := records[i].ToJSON(a.name, bs.Height, a.Net()) if err != nil { @@ -478,7 +495,7 @@ func (a *Account) LockedOutpoints() []btcjson.TransactionInput { // Track requests btcd to send notifications of new transactions for // each address stored in a wallet. func (a *Account) Track() { - client, err := accessClient() + rpcc, err := accessClient() if err != nil { log.Errorf("No chain server client to track addresses.") return @@ -495,7 +512,7 @@ func (a *Account) Track() { addrs = append(addrs, addr) } - if err := client.NotifyReceived(addrs); err != nil { + if err := rpcc.NotifyReceived(addrs); err != nil { log.Error("Unable to request transaction updates for address.") } @@ -543,7 +560,7 @@ func (a *Account) RescanActiveJob() (*RescanJob, error) { // credits that are not known to have been mined into a block, and attempts // to send each to the chain server for relay. func (a *Account) ResendUnminedTxs() { - client, err := accessClient() + rpcc, err := accessClient() if err != nil { log.Errorf("No chain server client to resend txs.") return @@ -551,7 +568,7 @@ func (a *Account) ResendUnminedTxs() { txs := a.TxStore.UnminedDebitTxs() for _, tx := range txs { - _, err := client.SendRawTransaction(tx.MsgTx(), false) + _, err := rpcc.SendRawTransaction(tx.MsgTx(), false) if err != nil { // TODO(jrick): Check error for if this tx is a double spend, // remove it if so. @@ -592,7 +609,11 @@ func (a *Account) ActivePaymentAddresses() map[string]struct{} { // NewAddress returns a new payment address for an account. func (a *Account) NewAddress() (btcutil.Address, error) { // Get current block's height and hash. - bs, err := GetCurBlock() + rpcc, err := accessClient() + if err != nil { + return nil, err + } + bs, err := rpcc.BlockStamp() if err != nil { return nil, err } @@ -613,11 +634,7 @@ func (a *Account) NewAddress() (btcutil.Address, error) { AcctMgr.MarkAddressForAccount(addr, a) // Request updates from btcd for new transactions sent to this address. - client, err := accessClient() - if err != nil { - return nil, err - } - if err := client.NotifyReceived([]btcutil.Address{addr}); err != nil { + if err := rpcc.NotifyReceived([]btcutil.Address{addr}); err != nil { return nil, err } @@ -627,7 +644,11 @@ func (a *Account) NewAddress() (btcutil.Address, error) { // NewChangeAddress returns a new change address for an account. func (a *Account) NewChangeAddress() (btcutil.Address, error) { // Get current block's height and hash. - bs, err := GetCurBlock() + rpcc, err := accessClient() + if err != nil { + return nil, err + } + bs, err := rpcc.BlockStamp() if err != nil { return nil, err } @@ -648,11 +669,7 @@ func (a *Account) NewChangeAddress() (btcutil.Address, error) { AcctMgr.MarkAddressForAccount(addr, a) // Request updates from btcd for new transactions sent to this address. - client, err := accessClient() - if err != nil { - return nil, err - } - if err := client.NotifyReceived([]btcutil.Address{addr}); err != nil { + if err := rpcc.NotifyReceived([]btcutil.Address{addr}); err != nil { return nil, err } @@ -676,13 +693,13 @@ func (a *Account) RecoverAddresses(n int) error { // Run a goroutine to rescan blockchain for recovered addresses. go func() { - client, err := accessClient() + rpcc, err := accessClient() if err != nil { log.Errorf("Cannot access chain server client to " + "rescan recovered addresses.") return } - err = client.Rescan(lastInfo.FirstBlock(), addrs, nil) + err = rpcc.Rescan(lastInfo.FirstBlock(), addrs, nil) if err != nil { log.Errorf("Rescanning for recovered addresses "+ "failed: %v", err) @@ -703,13 +720,13 @@ func ReqSpentUtxoNtfns(credits []txstore.Credit) { ops = append(ops, op) } - client, err := accessClient() + rpcc, err := accessClient() if err != nil { log.Errorf("Cannot access chain server client to " + "request spent output notifications.") return } - if err := client.NotifySpent(ops); err != nil { + if err := rpcc.NotifySpent(ops); err != nil { log.Errorf("Cannot request notifications for spent outputs: %v", err) } @@ -718,8 +735,12 @@ func ReqSpentUtxoNtfns(credits []txstore.Credit) { // 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() +func (a *Account) TotalReceived(confirms int) (btcutil.Amount, error) { + rpcc, err := accessClient() + if err != nil { + return 0, err + } + bs, err := rpcc.BlockStamp() if err != nil { return 0, err } @@ -738,6 +759,5 @@ func (a *Account) TotalReceived(confirms int) (float64, error) { } } } - - return amount.ToUnit(btcutil.AmountBTC), nil + return amount, nil } diff --git a/acctmgr.go b/acctmgr.go index 10e13b2..853a42b 100644 --- a/acctmgr.go +++ b/acctmgr.go @@ -758,10 +758,16 @@ func (am *AccountManager) BlockNotify(bs *wallet.BlockStamp) { // TODO: need a flag or check that the utxo store was actually // modified, or this will notify even if there are no balance // changes, or sending these notifications as the utxos are added. - confirmed := a.CalculateBalance(1) - unconfirmed := a.CalculateBalance(0) - confirmed - server.NotifyWalletBalance(a.name, confirmed) - server.NotifyWalletBalanceUnconfirmed(a.name, unconfirmed) + confirmed, err := a.CalculateBalance(1) + var unconfirmed btcutil.Amount + if err == nil { + unconfirmed, err = a.CalculateBalance(0) + } + if err == nil { + unconfirmed -= confirmed + server.NotifyWalletBalance(a.name, confirmed) + server.NotifyWalletBalanceUnconfirmed(a.name, unconfirmed) + } // If this is the default account, update the block all accounts // are synced with, and schedule a wallet write. @@ -797,13 +803,13 @@ func (am *AccountManager) RecordSpendingTx(tx *btcutil.Tx, block *txstore.Block) // CalculateBalance returns the balance, calculated using minconf block // confirmations, of an account. -func (am *AccountManager) CalculateBalance(account string, minconf int) (float64, error) { +func (am *AccountManager) CalculateBalance(account string, minconf int) (btcutil.Amount, error) { a, err := am.Account(account) if err != nil { return 0, err } - return a.CalculateBalance(minconf), nil + return a.CalculateBalance(minconf) } // CreateEncryptedWallet creates a new default account with a wallet file @@ -814,7 +820,11 @@ func (am *AccountManager) CreateEncryptedWallet(passphrase []byte) error { } // Get current block's height and hash. - bs, err := GetCurBlock() + rpcc, err := accessClient() + if err != nil { + return err + } + bs, err := rpcc.BlockStamp() if err != nil { return err } @@ -918,13 +928,37 @@ func (am *AccountManager) DumpWIFPrivateKey(addr btcutil.Address) (string, error // ListAccounts returns a map of account names to their current account // balances. The balances are calculated using minconf confirmations. -func (am *AccountManager) ListAccounts(minconf int) map[string]float64 { +func (am *AccountManager) ListAccounts(minconf int) (map[string]btcutil.Amount, error) { // Create and fill a map of account names and their balances. - pairs := make(map[string]float64) - for _, a := range am.AllAccounts() { - pairs[a.name] = a.CalculateBalance(minconf) + accts := am.AllAccounts() + pairs := make(map[string]btcutil.Amount, len(accts)) + for _, a := range accts { + bal, err := a.CalculateBalance(minconf) + if err != nil { + return nil, err + } + pairs[a.name] = bal } - return pairs + return pairs, nil +} + +// ListAccountsF64 returns a map of account names to their current account +// balances. The balances are calculated using minconf confirmations. +// +// The amounts are converted to float64 so this result may be marshaled +// as a JSON object for the listaccounts RPC. +func (am *AccountManager) ListAccountsF64(minconf int) (map[string]float64, error) { + // Create and fill a map of account names and their balances. + accts := am.AllAccounts() + pairs := make(map[string]float64, len(accts)) + for _, a := range accts { + bal, err := a.CalculateBalance(minconf) + if err != nil { + return nil, err + } + pairs[a.name] = bal.ToUnit(btcutil.AmountBTC) + } + return pairs, nil } // ListSinceBlock returns a slice of objects representing all transactions in @@ -983,14 +1017,19 @@ func (am *AccountManager) GetTransaction(txSha *btcwire.ShaHash) []accountTx { func (am *AccountManager) ListUnspent(minconf, maxconf int, addresses map[string]bool) ([]*btcjson.ListUnspentResult, error) { - bs, err := GetCurBlock() + results := []*btcjson.ListUnspentResult{} + + rpcc, err := accessClient() if err != nil { - return nil, err + return results, err + } + bs, err := rpcc.BlockStamp() + if err != nil { + return results, err } filter := len(addresses) != 0 - results := []*btcjson.ListUnspentResult{} for _, a := range am.AllAccounts() { unspent, err := a.TxStore.SortedUnspentOutputs() if err != nil { diff --git a/cmd.go b/cmd.go index 29d804a..a0e5bd9 100644 --- a/cmd.go +++ b/cmd.go @@ -23,67 +23,16 @@ import ( "net/http" _ "net/http/pprof" "os" - "sync" "time" - - "github.com/conformal/btcutil" - "github.com/conformal/btcwallet/wallet" - "github.com/conformal/btcwire" ) var ( - cfg *config - server *rpcServer - shutdownChan = make(chan struct{}) - - curBlock = struct { - sync.RWMutex - wallet.BlockStamp - }{ - BlockStamp: wallet.BlockStamp{ - Height: int32(btcutil.BlockHeightUnknown), - }, - } + cfg *config + server *rpcServer + shutdownChan = make(chan struct{}) + clientAccessChan = make(chan *rpcClient) ) -// GetCurBlock returns the blockchain height and SHA hash of the most -// recently seen block. If no blocks have been seen since btcd has -// connected, btcd is queried for the current block height and hash. -func GetCurBlock() (wallet.BlockStamp, error) { - curBlock.RLock() - bs := curBlock.BlockStamp - curBlock.RUnlock() - if bs.Height != int32(btcutil.BlockHeightUnknown) { - return bs, nil - } - - var bbHash *btcwire.ShaHash - var bbHeight int32 - client, err := accessClient() - if err == nil { - bbHash, bbHeight, err = client.GetBestBlock() - } - if err != nil { - unknown := wallet.BlockStamp{ - Height: int32(btcutil.BlockHeightUnknown), - } - return unknown, err - } - - curBlock.Lock() - if bbHeight > curBlock.BlockStamp.Height { - bs = wallet.BlockStamp{ - Height: bbHeight, - Hash: *bbHash, - } - curBlock.BlockStamp = bs - } - curBlock.Unlock() - return bs, nil -} - -var clientAccessChan = make(chan *rpcClient) - func clientAccess(newClient <-chan *rpcClient) { var client *rpcClient for { diff --git a/createtx.go b/createtx.go index e72f5bc..0013f6f 100644 --- a/createtx.go +++ b/createtx.go @@ -163,7 +163,11 @@ func (a *Account) txToPairs(pairs map[string]btcutil.Amount, } // Get current block's height and hash. - bs, err := GetCurBlock() + rpcc, err := accessClient() + if err != nil { + return nil, err + } + bs, err := rpcc.BlockStamp() if err != nil { return nil, err } diff --git a/rpcclient.go b/rpcclient.go index d331796..fcecd64 100644 --- a/rpcclient.go +++ b/rpcclient.go @@ -72,7 +72,7 @@ type acceptedTx struct { type ( // Container type for any notification. notification interface { - handleNotification() error + handleNotification(*rpcClient) error } blockConnected blockSummary @@ -85,18 +85,18 @@ type ( rescanProgress int32 ) -func (n blockConnected) handleNotification() error { +func (n blockConnected) handleNotification(c *rpcClient) error { // Update the blockstamp for the newly-connected block. - bs := &wallet.BlockStamp{ + bs := wallet.BlockStamp{ Height: n.height, Hash: *n.hash, } - curBlock.Lock() - curBlock.BlockStamp = *bs - curBlock.Unlock() + c.mtx.Lock() + c.blockStamp = bs + c.mtx.Unlock() AcctMgr.Grab() - AcctMgr.BlockNotify(bs) + AcctMgr.BlockNotify(&bs) AcctMgr.Release() // Pass notification to wallet clients too. @@ -121,7 +121,7 @@ func (n blockConnected) MarshalJSON() ([]byte, error) { return nn.MarshalJSON() } -func (n blockDisconnected) handleNotification() error { +func (n blockDisconnected) handleNotification(c *rpcClient) error { AcctMgr.Grab() defer AcctMgr.Release() @@ -170,14 +170,14 @@ func parseBlock(block *btcws.BlockDetails) (*txstore.Block, int, error) { return b, block.Index, nil } -func (n recvTx) handleNotification() error { +func (n recvTx) handleNotification(c *rpcClient) error { block, txIdx, err := parseBlock(n.block) if err != nil { return InvalidNotificationError{err} } n.tx.SetIndex(txIdx) - bs, err := GetCurBlock() + bs, err := c.BlockStamp() if err != nil { return fmt.Errorf("cannot get current block: %v", err) } @@ -233,7 +233,7 @@ func (n recvTx) handleNotification() error { return nil } -func (n redeemingTx) handleNotification() error { +func (n redeemingTx) handleNotification(c *rpcClient) error { block, txIdx, err := parseBlock(n.block) if err != nil { return InvalidNotificationError{err} @@ -246,26 +246,34 @@ func (n redeemingTx) handleNotification() error { return err } -func (n rescanFinished) handleNotification() error { +func (n rescanFinished) handleNotification(c *rpcClient) error { AcctMgr.rm.MarkFinished(n) return nil } -func (n rescanProgress) handleNotification() error { +func (n rescanProgress) handleNotification(c *rpcClient) error { AcctMgr.rm.MarkProgress(n) return nil } type rpcClient struct { *btcrpcclient.Client // client to btcd - enqueueNotification chan notification - dequeueNotification chan notification - quit chan struct{} - wg sync.WaitGroup + + mtx sync.Mutex + blockStamp wallet.BlockStamp + + enqueueNotification chan notification + dequeueNotification chan notification + + quit chan struct{} + wg sync.WaitGroup } func newRPCClient(certs []byte) (*rpcClient, error) { client := rpcClient{ + blockStamp: wallet.BlockStamp{ + Height: int32(btcutil.BlockHeightUnknown), + }, enqueueNotification: make(chan notification), dequeueNotification: make(chan notification), quit: make(chan struct{}), @@ -408,7 +416,7 @@ out: func (c *rpcClient) handleNotifications() { for n := range c.dequeueNotification { - err := n.handleNotification() + err := n.handleNotification(c) if err != nil { switch e := err.(type) { case InvalidNotificationError: @@ -421,6 +429,30 @@ func (c *rpcClient) handleNotifications() { c.wg.Done() } +// BlockStamp returns (as a blockstamp) the height and hash of the last seen +// block from the RPC client. If no blocks have been seen (the height is -1), +// the chain server is queried for the block and the result is saved for future +// calls, or an error is returned if the RPC is unsuccessful. +func (c *rpcClient) BlockStamp() (wallet.BlockStamp, error) { + c.mtx.Lock() + defer c.mtx.Unlock() + + if c.blockStamp.Height != int32(btcutil.BlockHeightUnknown) { + return c.blockStamp, nil + } + + hash, height, err := c.GetBestBlock() + if err != nil { + return wallet.BlockStamp{}, err + } + bs := wallet.BlockStamp{ + Hash: *hash, + Height: height, + } + c.blockStamp = bs + return bs, nil +} + // Handshake first checks that the websocket connection between btcwallet and // btcd is valid, that is, that there are no mismatching settings between // the two processes (such as running on different Bitcoin networks). If the @@ -448,9 +480,9 @@ func (c *rpcClient) Handshake() error { // saved block hash, assume that this btcd instance is not yet // synced up to a previous btcd that was last used with this // wallet. - bs, err := GetCurBlock() + bs, err := c.BlockStamp() if err != nil { - return fmt.Errorf("cannot get best block: %v", err) + return err } if server != nil { server.NotifyNewBlockChainHeight(&bs) diff --git a/rpcserver.go b/rpcserver.go index 891dd65..8e788ac 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -398,9 +398,9 @@ func (s *rpcServer) Stop() { } // Disconnect the connected chain server, if any. - client, err := accessClient() + rpcc, err := accessClient() if err == nil { - client.Stop() + rpcc.Stop() } // Stop the account manager and finish all pending account file writes. @@ -535,9 +535,9 @@ func (s *rpcServer) postPassthrough(w http.ResponseWriter, request rawRequest) { // request's ID. func passthrough(request rawRequest) []byte { var res json.RawMessage - client, err := accessClient() + rpcc, err := accessClient() if err == nil { - res, err = client.RawRequest(request.Method, request.Params) + res, err = rpcc.RawRequest(request.Method, request.Params) } var jsonErr *btcjson.Error if err != nil { @@ -878,9 +878,9 @@ func (s *rpcServer) PostClientRPC(w http.ResponseWriter, r *http.Request) { // current connection status of btcwallet to btcd. func (s *rpcServer) NotifyConnectionStatus(wsc *websocketClient) { connected := false - client, err := accessClient() + rpcc, err := accessClient() if err == nil { - connected = !client.Disconnected() + connected = !rpcc.Disconnected() } ntfn := btcws.NewBtcdConnectedNtfn(connected) mntfn, err := ntfn.MarshalJSON() @@ -1287,7 +1287,7 @@ func GetBalance(icmd btcjson.Cmd) (interface{}, error) { if err == ErrNotFound { return nil, btcjson.ErrWalletInvalidAccountName } - return balance, err + return balance.ToUnit(btcutil.AmountBTC), err } // GetInfo handles a getinfo request by returning the a structure containing @@ -1296,22 +1296,25 @@ func GetBalance(icmd btcjson.Cmd) (interface{}, error) { func GetInfo(icmd btcjson.Cmd) (interface{}, error) { // Call down to btcd for all of the information in this command known // by them. - client, err := accessClient() + rpcc, err := accessClient() if err != nil { return nil, err } - info, err := client.GetInfo() + info, err := rpcc.GetInfo() if err != nil { return nil, err } - balance := float64(0.0) - accounts := AcctMgr.ListAccounts(1) + var balance btcutil.Amount + accounts, err := AcctMgr.ListAccounts(1) + if err != nil { + return nil, err + } for _, v := range accounts { balance += v } info.WalletVersion = int32(wallet.VersCurrent.Uint32()) - info.Balance = balance + info.Balance = balance.ToUnit(btcutil.AmountBTC) // Keypool times are not tracked. set to current time. info.KeypoolOldest = time.Now().Unix() info.KeypoolSize = int32(cfg.KeypoolSize) @@ -1406,7 +1409,8 @@ func GetAddressBalance(icmd btcjson.Cmd) (interface{}, error) { return nil, ErrAddressNotInWallet } - return a.CalculateAddressBalance(addr, int(cmd.Minconf)), nil + bal, err := a.CalculateAddressBalance(addr, int(cmd.Minconf)) + return bal.ToUnit(btcutil.AmountBTC), err } // GetUnconfirmedBalance handles a getunconfirmedbalance extension request @@ -1427,7 +1431,16 @@ func GetUnconfirmedBalance(icmd btcjson.Cmd) (interface{}, error) { return nil, err } - return a.CalculateBalance(0) - a.CalculateBalance(1), nil + unconfirmed, err := a.CalculateBalance(0) + if err != nil { + return nil, err + } + confirmed, err := a.CalculateBalance(1) + if err != nil { + return nil, err + } + + return (unconfirmed - confirmed).ToUnit(btcutil.AmountBTC), nil } // ImportPrivKey handles an importprivkey request by parsing @@ -1502,8 +1515,15 @@ func (s *rpcServer) NotifyNewBlockChainHeight(bs *wallet.BlockStamp) { // separate notifications for each account. func (s *rpcServer) NotifyBalances() { for _, a := range AcctMgr.AllAccounts() { - balance := a.CalculateBalance(1) - unconfirmed := a.CalculateBalance(0) - balance + balance, err := a.CalculateBalance(1) + var unconfirmed btcutil.Amount + if err == nil { + unconfirmed, err = a.CalculateBalance(0) + } + if err != nil { + break + } + unconfirmed -= balance s.NotifyWalletBalance(a.name, balance) s.NotifyWalletBalanceUnconfirmed(a.name, unconfirmed) } @@ -1580,7 +1600,8 @@ func GetReceivedByAccount(icmd btcjson.Cmd) (interface{}, error) { return nil, err } - return a.TotalReceived(cmd.MinConf) + bal, err := a.TotalReceived(cmd.MinConf) + return bal.ToUnit(btcutil.AmountBTC), err } // GetTransaction handles a gettransaction request by returning details about @@ -1602,7 +1623,11 @@ func GetTransaction(icmd btcjson.Cmd) (interface{}, error) { return nil, btcjson.ErrNoTxInfo } - bs, err := GetCurBlock() + rpcc, err := accessClient() + if err != nil { + return nil, err + } + bs, err := rpcc.BlockStamp() if err != nil { return nil, err } @@ -1719,7 +1744,7 @@ func ListAccounts(icmd btcjson.Cmd) (interface{}, error) { } // Return the map. This will be marshaled into a JSON object. - return AcctMgr.ListAccounts(cmd.MinConf), nil + return AcctMgr.ListAccounts(cmd.MinConf) } // ListLockUnspent handles a listlockunspent request by returning an slice of @@ -1763,14 +1788,17 @@ func ListReceivedByAddress(icmd btcjson.Cmd) (interface{}, error) { confirmations int32 } - // Intermediate data for all addresses. - allAddrData := make(map[string]AddrData) - - bs, err := GetCurBlock() + rpcc, err := accessClient() + if err != nil { + return nil, err + } + bs, err := rpcc.BlockStamp() if err != nil { return nil, err } + // Intermediate data for all addresses. + allAddrData := make(map[string]AddrData) for _, account := range AcctMgr.AllAccounts() { if cmd.IncludeEmpty { // Create an AddrData entry for each active address in the account. @@ -1842,7 +1870,7 @@ func ListSinceBlock(icmd btcjson.Cmd) (interface{}, error) { return nil, btcjson.ErrInternal } - client, err := accessClient() + rpcc, err := accessClient() if err != nil { return nil, err } @@ -1853,14 +1881,14 @@ func ListSinceBlock(icmd btcjson.Cmd) (interface{}, error) { if err != nil { return nil, DeserializationError{err} } - block, err := client.GetBlock(hash) + block, err := rpcc.GetBlock(hash) if err != nil { return nil, err } height = int32(block.Height()) } - bs, err := GetCurBlock() + bs, err := rpcc.BlockStamp() if err != nil { return nil, err } @@ -1868,7 +1896,7 @@ func ListSinceBlock(icmd btcjson.Cmd) (interface{}, 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 := client.GetBlockHashAsync(int64(bs.Height) + 1 - int64(cmd.TargetConfirmations)) + gbh := rpcc.GetBlockHashAsync(int64(bs.Height) + 1 - int64(cmd.TargetConfirmations)) if err != nil { return nil, err } @@ -2038,7 +2066,7 @@ func LockUnspent(icmd btcjson.Cmd) (interface{}, error) { func sendPairs(icmd btcjson.Cmd, account string, amounts map[string]btcutil.Amount, minconf int) (interface{}, error) { - client, err := accessClient() + rpcc, err := accessClient() if err != nil { return nil, err } @@ -2070,13 +2098,13 @@ func sendPairs(icmd btcjson.Cmd, account string, amounts map[string]btcutil.Amou if err := AcctMgr.ds.FlushAccount(a); err != nil { return nil, fmt.Errorf("Cannot write account: %v", err) } - err := client.NotifyReceived([]btcutil.Address{createdTx.changeAddr}) + err := rpcc.NotifyReceived([]btcutil.Address{createdTx.changeAddr}) if err != nil { return nil, err } } - txSha, err := client.SendRawTransaction(createdTx.tx.MsgTx(), false) + txSha, err := rpcc.SendRawTransaction(createdTx.tx.MsgTx(), false) if err != nil { return nil, err } @@ -2179,8 +2207,11 @@ func handleSendRawTxReply(icmd btcjson.Cmd, txSha *btcwire.ShaHash, a *Account, AcctMgr.ds.ScheduleTxStoreWrite(a) // Notify websocket clients of the transaction. - bs, err := GetCurBlock() - if err == nil { + rpcc, err := accessClient() + if err != nil { + return err + } + if bs, err := rpcc.BlockStamp(); err == nil { ltr, err := debits.ToJSON(a.Name(), bs.Height, a.Net()) if err != nil { log.Errorf("Error adding sent tx history: %v", err) @@ -2199,8 +2230,15 @@ func handleSendRawTxReply(icmd btcjson.Cmd, txSha *btcwire.ShaHash, a *Account, // Notify websocket clients of account's new unconfirmed and // confirmed balance. - confirmed := a.CalculateBalance(1) - unconfirmed := a.CalculateBalance(0) - confirmed + confirmed, err := a.CalculateBalance(1) + var unconfirmed btcutil.Amount + if err == nil { + unconfirmed, err = a.CalculateBalance(0) + } + if err != nil { + return err + } + unconfirmed -= confirmed server.NotifyWalletBalance(a.name, confirmed) server.NotifyWalletBalanceUnconfirmed(a.name, unconfirmed) @@ -2394,7 +2432,7 @@ func SignRawTransaction(icmd btcjson.Cmd) (interface{}, error) { }] = script } - var client *rpcClient + var rpcc *rpcClient // Now we go and look for any inputs that we were not provided by // querying btcd with getrawtransaction. We queue up a bunch of async @@ -2419,15 +2457,15 @@ func SignRawTransaction(icmd btcjson.Cmd) (interface{}, error) { } // Never heard of this one before, request it. - if client == nil { - client, err = accessClient() + if rpcc == nil { + rpcc, err = accessClient() if err != nil { return nil, err } } prevHash := &txIn.PreviousOutpoint.Hash requested[txIn.PreviousOutpoint.Hash] = &pendingTx{ - resp: client.GetRawTransactionAsync(prevHash), + resp: rpcc.GetRawTransactionAsync(prevHash), inputs: []uint32{txIn.PreviousOutpoint.Index}, } } @@ -2814,8 +2852,9 @@ func (s *rpcServer) NotifyWalletLockStateChange(account string, locked bool) { // NotifyWalletBalance sends a confirmed account balance notification // to all websocket clients. -func (s *rpcServer) NotifyWalletBalance(account string, balance float64) { - ntfn := btcws.NewAccountBalanceNtfn(account, balance, true) +func (s *rpcServer) NotifyWalletBalance(account string, balance btcutil.Amount) { + fbal := balance.ToUnit(btcutil.AmountBTC) + ntfn := btcws.NewAccountBalanceNtfn(account, fbal, true) mntfn, err := ntfn.MarshalJSON() // If the marshal failed, it indicates that the btcws notification // struct contains a field with a type that is not marshalable. @@ -2830,8 +2869,9 @@ func (s *rpcServer) NotifyWalletBalance(account string, balance float64) { // NotifyWalletBalanceUnconfirmed sends a confirmed account balance // notification to all websocket clients. -func (s *rpcServer) NotifyWalletBalanceUnconfirmed(account string, balance float64) { - ntfn := btcws.NewAccountBalanceNtfn(account, balance, false) +func (s *rpcServer) NotifyWalletBalanceUnconfirmed(account string, balance btcutil.Amount) { + fbal := balance.ToUnit(btcutil.AmountBTC) + ntfn := btcws.NewAccountBalanceNtfn(account, fbal, false) mntfn, err := ntfn.MarshalJSON() // If the marshal failed, it indicates that the btcws notification // struct contains a field with a type that is not marshalable.