Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion rpc/rpcserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,10 @@ func (s *walletServer) Accounts(ctx context.Context, req *pb.AccountsRequest) (
func (s *walletServer) RenameAccount(ctx context.Context, req *pb.RenameAccountRequest) (
*pb.RenameAccountResponse, error) {

err := s.wallet.RenameAccountDeprecated(waddrmgr.KeyScopeBIP0044, req.GetAccountNumber(), req.GetNewName())
err := s.wallet.RenameAccountDeprecated(
waddrmgr.KeyScopeBIP0044, req.GetAccountNumber(),
req.GetNewName(),
)
if err != nil {
return nil, translateError(err)
}
Expand Down
4 changes: 3 additions & 1 deletion waddrmgr/scoped_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -1566,7 +1566,9 @@ func (s *ScopedKeyManager) CanAddAccount() error {
// differs from the NewAccount method in that this method takes the account
// number *directly*, rather than taking a string name for the account, then
// mapping that to the next highest account number.
func (s *ScopedKeyManager) NewRawAccount(ns walletdb.ReadWriteBucket, number uint32) error {
func (s *ScopedKeyManager) NewRawAccount(
ns walletdb.ReadWriteBucket, number uint32) error {

s.mtx.Lock()
defer s.mtx.Unlock()

Expand Down
88 changes: 63 additions & 25 deletions wallet/account_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

// Package wallet implements the account management for the wallet.
//
// TODO(yy): bring wrapcheck back when implementing the `Store` interface.
//
//nolint:wrapcheck
package wallet

import (
Expand All @@ -14,6 +19,7 @@ import (
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/btcsuite/btcwallet/wtxmgr"
)

// AccountManager provides a high-level interface for managing wallet
Expand Down Expand Up @@ -118,6 +124,7 @@ func (w *Wallet) NewAccount(_ context.Context, scope waddrmgr.KeyScope,
}

var props *waddrmgr.AccountProperties

err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)

Expand All @@ -129,6 +136,7 @@ func (w *Wallet) NewAccount(_ context.Context, scope waddrmgr.KeyScope,

// Get the account's properties.
props, err = manager.AccountProperties(addrmgrNs, accNum)

return err
})

Expand Down Expand Up @@ -176,6 +184,7 @@ func (w *Wallet) ListAccounts(_ context.Context) (*AccountsResult, error) {
scopes := w.addrStore.ActiveScopedKeyManagers()

var accounts []AccountResult

err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)

Expand All @@ -187,8 +196,8 @@ func (w *Wallet) ListAccounts(_ context.Context) (*AccountsResult, error) {
return err
}

// Now, iterate through each key scope to assemble the final list
// of accounts with their properties and balances.
// Now, iterate through all key scopes to assemble the final
// list of accounts with their properties and balances.
for _, scopeMgr := range scopes {
scope := scopeMgr.Scope()
accountBalances := scopedBalances[scope]
Expand Down Expand Up @@ -217,6 +226,7 @@ func (w *Wallet) ListAccounts(_ context.Context) (*AccountsResult, error) {
// Include the wallet's current sync state in the result to provide a
// point-in-time reference for the balances.
syncBlock := w.addrStore.SyncedTo()

return &AccountsResult{
Accounts: accounts,
CurrentBlockHash: syncBlock.Hash,
Expand Down Expand Up @@ -246,6 +256,7 @@ func (w *Wallet) ListAccountsByScope(_ context.Context,
}

var accounts []AccountResult

err = walletdb.View(w.db, func(tx walletdb.ReadTx) error {
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)

Expand All @@ -263,6 +274,7 @@ func (w *Wallet) ListAccountsByScope(_ context.Context,
accounts, err = listAccountsWithBalances(
manager, addrmgrNs, scopedBalances[scope],
)

return err
})
if err != nil {
Expand All @@ -271,6 +283,7 @@ func (w *Wallet) ListAccountsByScope(_ context.Context,

// Include the wallet's current sync state in the result.
syncBlock := w.addrStore.SyncedTo()

return &AccountsResult{
Accounts: accounts,
CurrentBlockHash: syncBlock.Hash,
Expand All @@ -294,6 +307,7 @@ func (w *Wallet) ListAccountsByName(_ context.Context,
scopes := w.addrStore.ActiveScopedKeyManagers()

var accounts []AccountResult

err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
// First, calculate the balances for any accounts that match the
// given name. This is efficient as it iterates over the UTXO
Expand Down Expand Up @@ -333,6 +347,7 @@ func (w *Wallet) ListAccountsByName(_ context.Context,
// Get the pre-calculated balance for this account. If
// the account has no balance, it will be zero.
var balance btcutil.Amount

balances, ok := scopedBalances[scopeMgr.Scope()]
if ok {
balance = balances[accNum]
Expand All @@ -351,6 +366,7 @@ func (w *Wallet) ListAccountsByName(_ context.Context,
}

syncBlock := w.addrStore.SyncedTo()

return &AccountsResult{
Accounts: accounts,
CurrentBlockHash: syncBlock.Hash,
Expand All @@ -374,6 +390,7 @@ func (w *Wallet) GetAccount(_ context.Context, scope waddrmgr.KeyScope,
}

var account *AccountResult

err = walletdb.View(w.db, func(tx walletdb.ReadTx) error {
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)

Expand Down Expand Up @@ -435,7 +452,8 @@ func (w *Wallet) RenameAccount(_ context.Context, scope waddrmgr.KeyScope,

// Validate the new account name to ensure it meets the required
// criteria.
if err := waddrmgr.ValidateAccountName(newName); err != nil {
err = waddrmgr.ValidateAccountName(newName)
if err != nil {
return err
}

Expand Down Expand Up @@ -467,6 +485,7 @@ func (w *Wallet) Balance(_ context.Context, conf int32,
scope waddrmgr.KeyScope, name string) (btcutil.Amount, error) {

var balance btcutil.Amount

err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
Expand All @@ -476,6 +495,7 @@ func (w *Wallet) Balance(_ context.Context, conf int32,
if err != nil {
return err
}

accNum, err := manager.LookupAccount(addrmgrNs, name)
if err != nil {
return err
Expand All @@ -484,40 +504,22 @@ func (w *Wallet) Balance(_ context.Context, conf int32,
// Iterate through all unspent outputs and sum the balances for
// the addresses that belong to the target account.
syncBlock := w.addrStore.SyncedTo()

utxos, err := w.txStore.UnspentOutputs(txmgrNs)
if err != nil {
return err
}

for _, utxo := range utxos {
// Skip any UTXOs that have not yet reached the required
// number of confirmations.
if !confirmed(conf, utxo.Height, syncBlock.Height) {
continue
}

// Extract the address from the UTXO's public key script.
addr := extractAddrFromPKScript(
utxo.PkScript, w.chainParams,
)
if addr == nil {
continue
}

// Look up the account that owns the address.
addrScope, addrAcc, err := w.addrStore.AddrAccount(
addrmgrNs, addr,
balance += w.balanceForUTXO(
addrmgrNs, scope, accNum, utxo,
)
if err != nil {
// Ignore addresses that are not found in the
// wallet.
continue
}

// If the address belongs to the target account, add the
// UTXO's value to the total balance.
if addrScope.Scope() == scope && addrAcc == accNum {
balance += utxo.Amount
}
}

return nil
Expand All @@ -529,6 +531,34 @@ func (w *Wallet) Balance(_ context.Context, conf int32,
return balance, nil
}

// balanceForUTXO is a helper function for Balance that calculates the balance
// of a single UTXO if it belongs to the target account.
func (w *Wallet) balanceForUTXO(addrmgrNs walletdb.ReadBucket,
scope waddrmgr.KeyScope, accNum uint32,
utxo wtxmgr.Credit) btcutil.Amount {

// Extract the address from the UTXO's public key script.
addr := extractAddrFromPKScript(utxo.PkScript, w.chainParams)
if addr == nil {
return 0
}

// Look up the account that owns the address.
addrScope, addrAcc, err := w.addrStore.AddrAccount(addrmgrNs, addr)
if err != nil {
// Ignore addresses that are not found in the wallet.
return 0
}

// If the address belongs to the target account, add the UTXO's value
// to the total balance.
if addrScope.Scope() == scope && addrAcc == accNum {
return utxo.Amount
}

return 0
}

// ImportAccount imports an account from an extended public or private key.
//
// The time complexity of this method is dominated by the database lookup
Expand Down Expand Up @@ -558,6 +588,11 @@ func (w *Wallet) ImportAccount(_ context.Context,

// extractAddrFromPKScript extracts an address from a public key script. If the
// script cannot be parsed or does not contain any addresses, it returns nil.
//
// The btcutil.Address is an interface that abstracts over different address
// types. Returning the interface is idiomatic in this context.
//
//nolint:ireturn
func extractAddrFromPKScript(pkScript []byte,
chainParams *chaincfg.Params) btcutil.Address {

Expand Down Expand Up @@ -690,6 +725,7 @@ func (w *Wallet) fetchAccountBalances(tx walletdb.ReadTx,
if err != nil {
log.Errorf("Unable to query account using address %v: "+
"%v", addr, err)

continue
}

Expand Down Expand Up @@ -744,13 +780,15 @@ func listAccountsWithBalances(scopeMgr *waddrmgr.ScopedKeyManager,
accountBalances map[uint32]btcutil.Amount) ([]AccountResult, error) {

var accounts []AccountResult

lastAccount, err := scopeMgr.LastAccount(addrmgrNs)
if err != nil {
// If the scope has no accounts, we can just return an empty
// slice. This is a normal condition and not an error.
if waddrmgr.IsError(err, waddrmgr.ErrAccountNotFound) {
return nil, nil
}

return nil, err
}

Expand Down
Loading
Loading