mirror of
https://github.com/LBRYFoundation/lbcwallet.git
synced 2025-08-31 01:11:31 +00:00
CurrentAddress: subsequently return new address
This commit is contained in:
parent
948609f064
commit
74208f90c1
5 changed files with 86 additions and 62 deletions
|
@ -53,7 +53,7 @@ type ManagedAddress interface {
|
||||||
Compressed() bool
|
Compressed() bool
|
||||||
|
|
||||||
// Used returns true if the backing address has been used in a transaction.
|
// Used returns true if the backing address has been used in a transaction.
|
||||||
Used() bool
|
Used() (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManagedPubKeyAddress extends ManagedAddress and additionally provides the
|
// ManagedPubKeyAddress extends ManagedAddress and additionally provides the
|
||||||
|
@ -191,8 +191,8 @@ func (a *managedAddress) Compressed() bool {
|
||||||
// Used returns true if the address has been used in a transaction.
|
// Used returns true if the address has been used in a transaction.
|
||||||
//
|
//
|
||||||
// This is part of the ManagedAddress interface implementation.
|
// This is part of the ManagedAddress interface implementation.
|
||||||
func (a *managedAddress) Used() bool {
|
func (a *managedAddress) Used() (bool, error) {
|
||||||
return a.used
|
return a.manager.fetchUsed(a.AddrHash())
|
||||||
}
|
}
|
||||||
|
|
||||||
// PubKey returns the public key associated with the address.
|
// PubKey returns the public key associated with the address.
|
||||||
|
@ -456,8 +456,8 @@ func (a *scriptAddress) Compressed() bool {
|
||||||
// Used returns true if the address has been used in a transaction.
|
// Used returns true if the address has been used in a transaction.
|
||||||
//
|
//
|
||||||
// This is part of the ManagedAddress interface implementation.
|
// This is part of the ManagedAddress interface implementation.
|
||||||
func (a *scriptAddress) Used() bool {
|
func (a *scriptAddress) Used() (bool, error) {
|
||||||
return a.used
|
return a.manager.fetchUsed(a.AddrHash())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Script returns the script associated with the address.
|
// Script returns the script associated with the address.
|
||||||
|
@ -484,7 +484,7 @@ func (a *scriptAddress) Script() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newScriptAddress initializes and returns a new pay-to-script-hash address.
|
// newScriptAddress initializes and returns a new pay-to-script-hash address.
|
||||||
func newScriptAddress(m *Manager, account uint32, scriptHash, scriptEncrypted []byte, used bool) (*scriptAddress, error) {
|
func newScriptAddress(m *Manager, account uint32, scriptHash, scriptEncrypted []byte) (*scriptAddress, error) {
|
||||||
address, err := btcutil.NewAddressScriptHashFromHash(scriptHash,
|
address, err := btcutil.NewAddressScriptHashFromHash(scriptHash,
|
||||||
m.chainParams)
|
m.chainParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -496,6 +496,5 @@ func newScriptAddress(m *Manager, account uint32, scriptHash, scriptEncrypted []
|
||||||
account: account,
|
account: account,
|
||||||
address: address,
|
address: address,
|
||||||
scriptEncrypted: scriptEncrypted,
|
scriptEncrypted: scriptEncrypted,
|
||||||
used: used,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,6 @@ type dbAddressRow struct {
|
||||||
account uint32
|
account uint32
|
||||||
addTime uint64
|
addTime uint64
|
||||||
syncStatus syncStatus
|
syncStatus syncStatus
|
||||||
used bool
|
|
||||||
rawData []byte // Varies based on address type field.
|
rawData []byte // Varies based on address type field.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -987,17 +986,6 @@ func serializeScriptAddress(encryptedHash, encryptedScript []byte) []byte {
|
||||||
return rawData
|
return rawData
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchAddressUsed returns true if the provided address hash was flagged as used.
|
|
||||||
func fetchAddressUsed(tx walletdb.Tx, addrHash []byte) bool {
|
|
||||||
bucket := tx.RootBucket().Bucket(usedAddrBucketName)
|
|
||||||
|
|
||||||
val := bucket.Get(addrHash[:])
|
|
||||||
if val != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetchAddressByHash loads address information for the provided address hash
|
// fetchAddressByHash loads address information for the provided address hash
|
||||||
// from the database. The returned value is one of the address rows for the
|
// from the database. The returned value is one of the address rows for the
|
||||||
// specific address type. The caller should use type assertions to ascertain
|
// specific address type. The caller should use type assertions to ascertain
|
||||||
|
@ -1016,7 +1004,6 @@ func fetchAddressByHash(tx walletdb.Tx, addrHash []byte) (interface{}, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
row.used = fetchAddressUsed(tx, addrHash[:])
|
|
||||||
|
|
||||||
switch row.addrType {
|
switch row.addrType {
|
||||||
case adtChain:
|
case adtChain:
|
||||||
|
@ -1031,6 +1018,14 @@ func fetchAddressByHash(tx walletdb.Tx, addrHash []byte) (interface{}, error) {
|
||||||
return nil, managerError(ErrDatabase, str, nil)
|
return nil, managerError(ErrDatabase, str, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fetchAddressUsed returns true if the provided address id was flagged as used.
|
||||||
|
func fetchAddressUsed(tx walletdb.Tx, addressID []byte) bool {
|
||||||
|
bucket := tx.RootBucket().Bucket(usedAddrBucketName)
|
||||||
|
|
||||||
|
addrHash := fastsha256.Sum256(addressID)
|
||||||
|
return bucket.Get(addrHash[:]) != nil
|
||||||
|
}
|
||||||
|
|
||||||
// markAddressUsed flags the provided address id as used in the database.
|
// markAddressUsed flags the provided address id as used in the database.
|
||||||
func markAddressUsed(tx walletdb.Tx, addressID []byte) error {
|
func markAddressUsed(tx walletdb.Tx, addressID []byte) error {
|
||||||
bucket := tx.RootBucket().Bucket(usedAddrBucketName)
|
bucket := tx.RootBucket().Bucket(usedAddrBucketName)
|
||||||
|
|
|
@ -374,7 +374,7 @@ func (m *Manager) Close() error {
|
||||||
// The passed derivedKey is zeroed after the new address is created.
|
// The passed derivedKey is zeroed after the new address is created.
|
||||||
//
|
//
|
||||||
// This function MUST be called with the manager lock held for writes.
|
// This function MUST be called with the manager lock held for writes.
|
||||||
func (m *Manager) keyToManaged(derivedKey *hdkeychain.ExtendedKey, account, branch, index uint32, used bool) (ManagedAddress, error) {
|
func (m *Manager) keyToManaged(derivedKey *hdkeychain.ExtendedKey, account, branch, index uint32) (ManagedAddress, error) {
|
||||||
// Create a new managed address based on the public or private key
|
// Create a new managed address based on the public or private key
|
||||||
// depending on whether the passed key is private. Also, zero the
|
// depending on whether the passed key is private. Also, zero the
|
||||||
// key after creating the managed address from it.
|
// key after creating the managed address from it.
|
||||||
|
@ -397,7 +397,6 @@ func (m *Manager) keyToManaged(derivedKey *hdkeychain.ExtendedKey, account, bran
|
||||||
if branch == internalBranch {
|
if branch == internalBranch {
|
||||||
ma.internal = true
|
ma.internal = true
|
||||||
}
|
}
|
||||||
ma.used = used
|
|
||||||
|
|
||||||
return ma, nil
|
return ma, nil
|
||||||
}
|
}
|
||||||
|
@ -512,7 +511,7 @@ func (m *Manager) loadAccountInfo(account uint32) (*accountInfo, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
lastExtAddr, err := m.keyToManaged(lastExtKey, account, branch, index, false)
|
lastExtAddr, err := m.keyToManaged(lastExtKey, account, branch, index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -527,7 +526,7 @@ func (m *Manager) loadAccountInfo(account uint32) (*accountInfo, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
lastIntAddr, err := m.keyToManaged(lastIntKey, account, branch, index, false)
|
lastIntAddr, err := m.keyToManaged(lastIntKey, account, branch, index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -563,7 +562,7 @@ func (m *Manager) chainAddressRowToManaged(row *dbChainAddressRow) (ManagedAddre
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.keyToManaged(addressKey, row.account, row.branch, row.index, row.used)
|
return m.keyToManaged(addressKey, row.account, row.branch, row.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
// importedAddressRowToManaged returns a new managed address based on imported
|
// importedAddressRowToManaged returns a new managed address based on imported
|
||||||
|
@ -590,7 +589,6 @@ func (m *Manager) importedAddressRowToManaged(row *dbImportedAddressRow) (Manage
|
||||||
}
|
}
|
||||||
ma.privKeyEncrypted = row.encryptedPrivKey
|
ma.privKeyEncrypted = row.encryptedPrivKey
|
||||||
ma.imported = true
|
ma.imported = true
|
||||||
ma.used = row.used
|
|
||||||
|
|
||||||
return ma, nil
|
return ma, nil
|
||||||
}
|
}
|
||||||
|
@ -605,7 +603,7 @@ func (m *Manager) scriptAddressRowToManaged(row *dbScriptAddressRow) (ManagedAdd
|
||||||
return nil, managerError(ErrCrypto, str, err)
|
return nil, managerError(ErrCrypto, str, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return newScriptAddress(m, row.account, scriptHash, row.encryptedScript, row.used)
|
return newScriptAddress(m, row.account, scriptHash, row.encryptedScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
// rowInterfaceToManaged returns a new managed address based on the given
|
// rowInterfaceToManaged returns a new managed address based on the given
|
||||||
|
@ -1173,7 +1171,7 @@ func (m *Manager) ImportScript(script []byte, bs *BlockStamp) (ManagedScriptAddr
|
||||||
// since it will be cleared on lock and the script the caller passed
|
// since it will be cleared on lock and the script the caller passed
|
||||||
// should not be cleared out from under the caller.
|
// should not be cleared out from under the caller.
|
||||||
scriptAddr, err := newScriptAddress(m, ImportedAddrAccount, scriptHash,
|
scriptAddr, err := newScriptAddress(m, ImportedAddrAccount, scriptHash,
|
||||||
encryptedScript, false)
|
encryptedScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1360,15 +1358,26 @@ func (m *Manager) Unlock(passphrase []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarkUsed updates the used flag for the provided address id.
|
// fetchUsed returns true if the provided address id was flagged used.
|
||||||
func (m *Manager) MarkUsed(addressID []byte) error {
|
func (m *Manager) fetchUsed(addressID []byte) (bool, error) {
|
||||||
|
var used bool
|
||||||
|
err := m.namespace.View(func(tx walletdb.Tx) error {
|
||||||
|
used = fetchAddressUsed(tx, addressID)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return used, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkUsed updates the used flag for the provided address.
|
||||||
|
func (m *Manager) MarkUsed(address btcutil.Address) error {
|
||||||
|
addressID := address.ScriptAddress()
|
||||||
err := m.namespace.Update(func(tx walletdb.Tx) error {
|
err := m.namespace.Update(func(tx walletdb.Tx) error {
|
||||||
return markAddressUsed(tx, addressID)
|
return markAddressUsed(tx, addressID)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return maybeConvertDbError(err)
|
return maybeConvertDbError(err)
|
||||||
}
|
}
|
||||||
// 'used' flag has become stale so remove key from cache
|
// Clear caches which might have stale entries for used addresses
|
||||||
delete(m.addrs, addrKey(addressID))
|
delete(m.addrs, addrKey(addressID))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1580,7 +1589,11 @@ func (m *Manager) LastExternalAddress(account uint32) (ManagedAddress, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return acctInfo.lastExternalAddr, nil
|
if acctInfo.nextExternalIndex > 0 {
|
||||||
|
return acctInfo.lastExternalAddr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, managerError(ErrAddressNotFound, "no previous external address", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastInternalAddress returns the most recently requested chained internal
|
// LastInternalAddress returns the most recently requested chained internal
|
||||||
|
@ -1608,7 +1621,11 @@ func (m *Manager) LastInternalAddress(account uint32) (ManagedAddress, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return acctInfo.lastInternalAddr, nil
|
if acctInfo.nextInternalIndex > 0 {
|
||||||
|
return acctInfo.lastInternalAddr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, managerError(ErrAddressNotFound, "no previous internal address", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateAccountName validates the given account name and returns an error, if any.
|
// ValidateAccountName validates the given account name and returns an error, if any.
|
||||||
|
|
|
@ -1028,28 +1028,37 @@ func testMarkUsed(tc *testContext) bool {
|
||||||
addrHash := expectedAddr1.addressHash
|
addrHash := expectedAddr1.addressHash
|
||||||
addr, err := btcutil.NewAddressPubKeyHash(addrHash, chainParams)
|
addr, err := btcutil.NewAddressPubKeyHash(addrHash, chainParams)
|
||||||
|
|
||||||
if tc.create {
|
|
||||||
// Test that initially the address is not flagged as used
|
|
||||||
maddr, err := tc.manager.Address(addr)
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Errorf("%s: unexpected error: %v", prefix, err)
|
|
||||||
}
|
|
||||||
if maddr.Used() != false {
|
|
||||||
tc.t.Errorf("%v: unexpected used flag -- got "+
|
|
||||||
"%v, want %v", prefix, maddr.Used(), expectedAddr1.used)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = tc.manager.MarkUsed(addrHash)
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Errorf("%s: unexpected error: %v", prefix, err)
|
|
||||||
}
|
|
||||||
maddr, err := tc.manager.Address(addr)
|
maddr, err := tc.manager.Address(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.t.Errorf("%s: unexpected error: %v", prefix, err)
|
tc.t.Errorf("%s: unexpected error: %v", prefix, err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
if maddr.Used() != expectedAddr1.used {
|
if tc.create {
|
||||||
|
// Test that initially the address is not flagged as used
|
||||||
|
used, err := maddr.Used()
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Errorf("%s: unexpected error: %v", prefix, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if used != false {
|
||||||
|
tc.t.Errorf("%v: unexpected used flag -- got "+
|
||||||
|
"%v, want %v", prefix, used, expectedAddr1.used)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = tc.manager.MarkUsed(addr)
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Errorf("%s: unexpected error: %v", prefix, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
used, err := maddr.Used()
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Errorf("%s: unexpected error: %v", prefix, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if used != expectedAddr1.used {
|
||||||
tc.t.Errorf("%v: unexpected used flag -- got "+
|
tc.t.Errorf("%v: unexpected used flag -- got "+
|
||||||
"%v, want %v", prefix, maddr.Used(), expectedAddr1.used)
|
"%v, want %v", prefix, used, expectedAddr1.used)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -262,8 +262,7 @@ func (w *Wallet) markAddrsUsed(t *txstore.TxRecord) error {
|
||||||
// range below does nothing.
|
// range below does nothing.
|
||||||
_, addrs, _, _ := c.Addresses(w.chainParams)
|
_, addrs, _, _ := c.Addresses(w.chainParams)
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
addressID := addr.ScriptAddress()
|
if err := w.Manager.MarkUsed(addr); err != nil {
|
||||||
if err := w.Manager.MarkUsed(addressID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Infof("Marked address used %s", addr.EncodeAddress())
|
log.Infof("Marked address used %s", addr.EncodeAddress())
|
||||||
|
@ -715,14 +714,6 @@ func (w *Wallet) diskWriter() {
|
||||||
w.wg.Done()
|
w.wg.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (w *Wallet) AddressUsed(addr waddrmgr.ManagedAddress) bool {
|
|
||||||
return addr.Used()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AccountUsed returns whether there are any recorded transactions spending to
|
// AccountUsed returns whether there are any recorded transactions spending to
|
||||||
// a given account. It returns true if atleast one address in the account was
|
// a given account. It returns true if atleast one address in the account was
|
||||||
// used and false if no address in the account was used.
|
// used and false if no address in the account was used.
|
||||||
|
@ -732,7 +723,11 @@ func (w *Wallet) AccountUsed(account uint32) (bool, error) {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
if w.AddressUsed(addr) {
|
used, err := addr.Used()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if used {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -791,11 +786,20 @@ func (w *Wallet) CalculateAccountBalance(account uint32, confirms int) (btcutil.
|
||||||
func (w *Wallet) CurrentAddress(account uint32) (btcutil.Address, error) {
|
func (w *Wallet) CurrentAddress(account uint32) (btcutil.Address, error) {
|
||||||
addr, err := w.Manager.LastExternalAddress(account)
|
addr, err := w.Manager.LastExternalAddress(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// If no address exists yet, create the first external address
|
||||||
|
merr, ok := err.(waddrmgr.ManagerError)
|
||||||
|
if ok && merr.ErrorCode == waddrmgr.ErrAddressNotFound {
|
||||||
|
return w.NewAddress(account)
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get next chained address if the last one has already been used.
|
// Get next chained address if the last one has already been used.
|
||||||
if w.AddressUsed(addr) {
|
used, err := addr.Used()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if used {
|
||||||
return w.NewAddress(account)
|
return w.NewAddress(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue