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:
parent
770384be12
commit
061a220354
6 changed files with 267 additions and 183 deletions
120
account.go
120
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
|
||||
}
|
||||
|
|
65
acctmgr.go
65
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
|
||||
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
|
||||
}
|
||||
return pairs
|
||||
pairs[a.name] = bal
|
||||
}
|
||||
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 {
|
||||
|
|
53
cmd.go
53
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),
|
||||
},
|
||||
}
|
||||
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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
64
rpcclient.go
64
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
|
||||
|
||||
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)
|
||||
|
|
124
rpcserver.go
124
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.
|
||||
|
|
Loading…
Reference in a new issue