Move last seen block to RPC client structure.

Pass the RPC client to the notification handlers.  Update the last
seen block for blockconnected notifications in the client structure
directly, protecting access with a mutex.
This commit is contained in:
Josh Rickmar 2014-07-07 16:57:00 -05:00
parent 770384be12
commit 061a220354
6 changed files with 267 additions and 183 deletions

View file

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

View file

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

59
cmd.go
View file

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

View file

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

View file

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

View file

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