From 99e4728eb77f4e1d7ded53561d9326d0b02cf5f5 Mon Sep 17 00:00:00 2001 From: priyanshiiit Date: Sat, 23 Jul 2022 11:05:53 +0530 Subject: [PATCH] lnwallet: adds list addresses method --- lntest/mock/walletcontroller.go | 7 +++ lnwallet/btcwallet/btcwallet.go | 106 ++++++++++++++++++++++++++++++++ lnwallet/interface.go | 34 ++++++++++ 3 files changed, 147 insertions(+) diff --git a/lntest/mock/walletcontroller.go b/lntest/mock/walletcontroller.go index f47864912..c935e7662 100644 --- a/lntest/mock/walletcontroller.go +++ b/lntest/mock/walletcontroller.go @@ -110,6 +110,13 @@ func (w *WalletController) RequiredReserve(uint32) btcutil.Amount { return 0 } +// ListAddresses currently returns a dummy value. +func (w *WalletController) ListAddresses(string, + bool) (lnwallet.AccountAddressMap, error) { + + return nil, nil +} + // ImportAccount currently returns a dummy value. func (w *WalletController) ImportAccount(string, *hdkeychain.ExtendedKey, uint32, *waddrmgr.AddressType, bool) (*waddrmgr.AccountProperties, diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index 5fd6614a7..eca08eb37 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -706,6 +706,112 @@ func (b *BtcWallet) RequiredReserve( return reserved } +// ListAddresses retrieves all the addresses along with their balance. An +// account name filter can be provided to filter through all of the +// wallet accounts and return the addresses of only those matching. +// +// This is a part of the WalletController interface. +func (b *BtcWallet) ListAddresses(name string, + showCustomAccounts bool) (lnwallet.AccountAddressMap, error) { + + accounts, err := b.ListAccounts(name, nil) + if err != nil { + return nil, err + } + + addresses := make(lnwallet.AccountAddressMap) + addressBalance := make(map[string]btcutil.Amount) + + // Retrieve all the unspent ouputs. + outputs, err := b.wallet.ListUnspent(0, math.MaxInt32, "") + if err != nil { + return nil, err + } + + // Calculate the total balance of each address. + for _, output := range outputs { + amount, err := btcutil.NewAmount(output.Amount) + if err != nil { + return nil, err + } + + addressBalance[output.Address] += amount + } + + for _, accntDetails := range accounts { + accntScope := accntDetails.KeyScope + scopedMgr, err := b.wallet.Manager.FetchScopedKeyManager( + accntScope, + ) + if err != nil { + return nil, err + } + + var managedAddrs []waddrmgr.ManagedAddress + err = walletdb.View( + b.wallet.Database(), func(tx walletdb.ReadTx) error { + managedAddrs = nil + addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey) + return scopedMgr.ForEachAccountAddress( + addrmgrNs, accntDetails.AccountNumber, + func(a waddrmgr.ManagedAddress) error { + managedAddrs = append( + managedAddrs, a, + ) + + return nil + }, + ) + }, + ) + if err != nil { + return nil, err + } + + // Only consider those accounts which have addresses. + if len(managedAddrs) == 0 { + continue + } + + // All the lnd internal/custom keys for channels and other + // functionality are derived from the same scope. Since they + // aren't really used as addresses and will never have an + // on-chain balance, we'll want to show the public key instead. + isLndCustom := accntScope.Purpose == keychain.BIP0043Purpose + addressProperties := make( + []lnwallet.AddressProperty, len(managedAddrs), + ) + + for idx, managedAddr := range managedAddrs { + addr := managedAddr.Address() + addressString := addr.String() + + // Hex-encode the compressed public key for custom lnd + // keys, addresses don't make a lot of sense. + pubKey, ok := managedAddr.(waddrmgr.ManagedPubKeyAddress) + if ok && isLndCustom { + addressString = hex.EncodeToString( + pubKey.PubKey().SerializeCompressed(), + ) + } + + addressProperties[idx] = lnwallet.AddressProperty{ + Address: addressString, + Internal: managedAddr.Internal(), + Balance: addressBalance[addressString], + } + } + + if accntScope.Purpose != keychain.BIP0043Purpose || + showCustomAccounts { + + addresses[accntDetails] = addressProperties + } + } + + return addresses, nil +} + // ImportAccount imports an account backed by an account extended public key. // The master key fingerprint denotes the fingerprint of the root key // corresponding to the account public key (also known as the key with diff --git a/lnwallet/interface.go b/lnwallet/interface.go index 8837f3788..bbaf5c430 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -32,6 +32,10 @@ const ( // WalletController supports. type AddressType uint8 +// AccountAddressMap maps the account properties to an array of +// address properties. +type AccountAddressMap map[*waddrmgr.AccountProperties][]AddressProperty + const ( // UnknownAddressType represents an output with an unknown or non-standard // script. @@ -76,6 +80,31 @@ var ErrNoOutputs = errors.New("no outputs") var ErrInvalidMinconf = errors.New("minimum number of confirmations must " + "be a non-negative number") +// AddressProperty contains wallet related information of an address. +type AddressProperty struct { + // Address is the address of an account. + Address string + + // Internal denotes if the address is a change address. + Internal bool + + // Balance returns the total balance of an address. + Balance btcutil.Amount +} + +// AccountIdentifier contains information to uniquely identify an account. +type AccountIdentifier struct { + // Name is the name of the account. + Name string + + // AddressType is the type of addresses supported by the account. + AddressType AddressType + + // DerivationPath is the derivation path corresponding to the account + // public key. + DerivationPath string +} + // Utxo is an unspent output denoted by its outpoint, and output value of the // original output. type Utxo struct { @@ -250,6 +279,11 @@ type WalletController interface { // capped at a maximum. RequiredReserve(uint32) btcutil.Amount + // ListAddresses retrieves all the addresses along with their balance. An + // account name filter can be provided to filter through all of the + // wallet accounts and return the addresses of only those matching. + ListAddresses(string, bool) (AccountAddressMap, error) + // ImportAccount imports an account backed by an account extended public // key. The master key fingerprint denotes the fingerprint of the root // key corresponding to the account public key (also known as the key