Implement the getaccountaddress RPC command.

This commit is contained in:
Josh Rickmar 2013-12-31 13:11:47 -05:00
parent fa699ef4a5
commit ac79a59c90
3 changed files with 116 additions and 1 deletions

View file

@ -130,6 +130,31 @@ func (a *Account) Rollback(height int32, hash *btcwire.ShaHash) {
}
}
// AddressUsed returns whether there are any recorded transactions spending to
// a given address. Assumming correct TxStore usage, this will return true iff
// there are any transactions with outputs to this address in the blockchain or
// the btcd mempool.
func (a *Account) AddressUsed(pkHash []byte) bool {
// This can be optimized by recording this data as it is read when
// opening an account, and keeping it up to date each time a new
// received tx arrives.
a.TxStore.RLock()
defer a.TxStore.RUnlock()
for i := range a.TxStore.s {
rtx, ok := a.TxStore.s[i].(*tx.RecvTx)
if !ok {
continue
}
if bytes.Equal(rtx.ReceiverHash, pkHash) {
return true
}
}
return false
}
// CalculateBalance sums the amounts of all unspent transaction
// outputs to addresses of a wallet and returns the balance as a
// float64.
@ -190,6 +215,28 @@ func (a *Account) CalculateAddressBalance(pubkeyHash []byte, confirms int) float
return float64(bal) / float64(btcutil.SatoshiPerBitcoin)
}
// CurrentAddress gets the most recently requested Bitcoin payment address
// from an account. If the address has already been used (there is at least
// one transaction spending to it in the blockchain or btcd mempool), the next
// chained address is returned.
func (a *Account) CurrentAddress() (string, error) {
a.mtx.RLock()
addr, err := a.Wallet.LastChainedAddress()
a.mtx.RUnlock()
if err != nil {
return "", err
}
// Get next chained address if the last one has already been used.
pkHash, _, _ := btcutil.DecodeAddress(addr)
if a.AddressUsed(pkHash) {
addr, err = a.NewAddress()
}
return addr, err
}
// ListTransactions returns a slice of maps with details about a recorded
// transaction. This is intended to be used for listtransactions RPC
// replies.

View file

@ -44,6 +44,7 @@ var rpcHandlers = map[string]cmdHandler{
// Standard bitcoind methods (implemented)
"dumpprivkey": DumpPrivKey,
"getaccount": GetAccount,
"getaccountaddress": GetAccountAddress,
"getaddressesbyaccount": GetAddressesByAccount,
"getbalance": GetBalance,
"getnewaddress": GetNewAddress,
@ -61,7 +62,6 @@ var rpcHandlers = map[string]cmdHandler{
"backupwallet": Unimplemented,
"createmultisig": Unimplemented,
"dumpwallet": Unimplemented,
"getaccountaddress": Unimplemented,
"getrawchangeaddress": Unimplemented,
"getreceivedbyaccount": Unimplemented,
"getreceivedbyaddress": Unimplemented,
@ -369,6 +369,54 @@ func GetAccount(frontend chan []byte, icmd btcjson.Cmd) {
ReplySuccess(frontend, cmd.Id(), aname)
}
// GetAccountAddress replies to a getaccountaddress request with the most
// recently-created chained address that has not yet been used (does not yet
// appear in the blockchain, or any tx that has arrived in the btcd mempool).
// If the most recently-requested address has been used, a new address (the
// next chained address in the keypool) is used. This can fail if the keypool
// runs out (and will return btcjson.ErrWalletKeypoolRanOut if that happens).
func GetAccountAddress(frontend chan []byte, icmd btcjson.Cmd) {
// Type assert icmd to access parameters.
cmd, ok := icmd.(*btcjson.GetAccountAddressCmd)
if !ok {
ReplyError(frontend, icmd.Id(), &btcjson.ErrInternal)
return
}
// Lookup account for this request.
a, err := accountstore.Account(cmd.Account)
switch err {
case nil:
break
case ErrAcctNotExist:
ReplyError(frontend, cmd.Id(),
&btcjson.ErrWalletInvalidAccountName)
default: // all other non-nil errors
e := &btcjson.Error{
Code: btcjson.ErrWallet.Code,
Message: err.Error(),
}
ReplyError(frontend, cmd.Id(), e)
}
switch addr, err := a.CurrentAddress(); err {
case nil:
ReplySuccess(frontend, cmd.Id(), addr)
case wallet.ErrWalletLocked:
ReplyError(frontend, cmd.Id(), &btcjson.ErrWalletKeypoolRanOut)
default: // all other non-nil errors
e := &btcjson.Error{
Code: btcjson.ErrWallet.Code,
Message: err.Error(),
}
ReplyError(frontend, cmd.Id(), e)
}
}
// GetAddressBalance replies to a getaddressbalance extension request
// by replying with the current balance (sum of unspent transaction
// output amounts) for a single address.

View file

@ -851,6 +851,26 @@ func (w *Wallet) NextChainedAddress(bs *BlockStamp) (string, error) {
return addr.paymentAddress(w.net)
}
// LastChainedAddress returns the most recently requested chained
// address from calling NextChainedAddress, or the root address if
// no chained addresses have been requested.
func (w *Wallet) LastChainedAddress() (string, error) {
// Lookup pubkey hash for last used chained address.
pkHash, ok := w.chainIdxMap[w.highestUsed]
if !ok {
return "", errors.New("chain index references unknown address")
}
// Lookup address with this pubkey hash.
addr, ok := w.addrMap[pkHash]
if !ok {
return "", errors.New("cannot find address by pubkey hash")
}
// Create and return payment address from serialized pubkey.
return addr.paymentAddress(w.net)
}
// extendKeypool grows the keypool by n addresses.
func (w *Wallet) extendKeypool(n uint, aeskey []byte, bs *BlockStamp) error {
// Get last chained address. New chained addresses will be