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
|
||||
|
||||
// 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
|
||||
|
@ -191,8 +191,8 @@ func (a *managedAddress) Compressed() bool {
|
|||
// Used returns true if the address has been used in a transaction.
|
||||
//
|
||||
// This is part of the ManagedAddress interface implementation.
|
||||
func (a *managedAddress) Used() bool {
|
||||
return a.used
|
||||
func (a *managedAddress) Used() (bool, error) {
|
||||
return a.manager.fetchUsed(a.AddrHash())
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// This is part of the ManagedAddress interface implementation.
|
||||
func (a *scriptAddress) Used() bool {
|
||||
return a.used
|
||||
func (a *scriptAddress) Used() (bool, error) {
|
||||
return a.manager.fetchUsed(a.AddrHash())
|
||||
}
|
||||
|
||||
// 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.
|
||||
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,
|
||||
m.chainParams)
|
||||
if err != nil {
|
||||
|
@ -496,6 +496,5 @@ func newScriptAddress(m *Manager, account uint32, scriptHash, scriptEncrypted []
|
|||
account: account,
|
||||
address: address,
|
||||
scriptEncrypted: scriptEncrypted,
|
||||
used: used,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -113,7 +113,6 @@ type dbAddressRow struct {
|
|||
account uint32
|
||||
addTime uint64
|
||||
syncStatus syncStatus
|
||||
used bool
|
||||
rawData []byte // Varies based on address type field.
|
||||
}
|
||||
|
||||
|
@ -987,17 +986,6 @@ func serializeScriptAddress(encryptedHash, encryptedScript []byte) []byte {
|
|||
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
|
||||
// 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
|
||||
|
@ -1016,7 +1004,6 @@ func fetchAddressByHash(tx walletdb.Tx, addrHash []byte) (interface{}, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
row.used = fetchAddressUsed(tx, addrHash[:])
|
||||
|
||||
switch row.addrType {
|
||||
case adtChain:
|
||||
|
@ -1031,6 +1018,14 @@ func fetchAddressByHash(tx walletdb.Tx, addrHash []byte) (interface{}, error) {
|
|||
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.
|
||||
func markAddressUsed(tx walletdb.Tx, addressID []byte) error {
|
||||
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.
|
||||
//
|
||||
// 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
|
||||
// depending on whether the passed key is private. Also, zero the
|
||||
// key after creating the managed address from it.
|
||||
|
@ -397,7 +397,6 @@ func (m *Manager) keyToManaged(derivedKey *hdkeychain.ExtendedKey, account, bran
|
|||
if branch == internalBranch {
|
||||
ma.internal = true
|
||||
}
|
||||
ma.used = used
|
||||
|
||||
return ma, nil
|
||||
}
|
||||
|
@ -512,7 +511,7 @@ func (m *Manager) loadAccountInfo(account uint32) (*accountInfo, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lastExtAddr, err := m.keyToManaged(lastExtKey, account, branch, index, false)
|
||||
lastExtAddr, err := m.keyToManaged(lastExtKey, account, branch, index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -527,7 +526,7 @@ func (m *Manager) loadAccountInfo(account uint32) (*accountInfo, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lastIntAddr, err := m.keyToManaged(lastIntKey, account, branch, index, false)
|
||||
lastIntAddr, err := m.keyToManaged(lastIntKey, account, branch, index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -563,7 +562,7 @@ func (m *Manager) chainAddressRowToManaged(row *dbChainAddressRow) (ManagedAddre
|
|||
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
|
||||
|
@ -590,7 +589,6 @@ func (m *Manager) importedAddressRowToManaged(row *dbImportedAddressRow) (Manage
|
|||
}
|
||||
ma.privKeyEncrypted = row.encryptedPrivKey
|
||||
ma.imported = true
|
||||
ma.used = row.used
|
||||
|
||||
return ma, nil
|
||||
}
|
||||
|
@ -605,7 +603,7 @@ func (m *Manager) scriptAddressRowToManaged(row *dbScriptAddressRow) (ManagedAdd
|
|||
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
|
||||
|
@ -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
|
||||
// should not be cleared out from under the caller.
|
||||
scriptAddr, err := newScriptAddress(m, ImportedAddrAccount, scriptHash,
|
||||
encryptedScript, false)
|
||||
encryptedScript)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1360,15 +1358,26 @@ func (m *Manager) Unlock(passphrase []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// MarkUsed updates the used flag for the provided address id.
|
||||
func (m *Manager) MarkUsed(addressID []byte) error {
|
||||
// fetchUsed returns true if the provided address id was flagged used.
|
||||
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 {
|
||||
return markAddressUsed(tx, addressID)
|
||||
})
|
||||
if err != nil {
|
||||
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))
|
||||
return nil
|
||||
}
|
||||
|
@ -1580,7 +1589,11 @@ func (m *Manager) LastExternalAddress(account uint32) (ManagedAddress, error) {
|
|||
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
|
||||
|
@ -1608,7 +1621,11 @@ func (m *Manager) LastInternalAddress(account uint32) (ManagedAddress, error) {
|
|||
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.
|
||||
|
|
|
@ -1028,28 +1028,37 @@ func testMarkUsed(tc *testContext) bool {
|
|||
addrHash := expectedAddr1.addressHash
|
||||
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)
|
||||
if err != nil {
|
||||
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 "+
|
||||
"%v, want %v", prefix, maddr.Used(), expectedAddr1.used)
|
||||
"%v, want %v", prefix, used, expectedAddr1.used)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -262,8 +262,7 @@ func (w *Wallet) markAddrsUsed(t *txstore.TxRecord) error {
|
|||
// range below does nothing.
|
||||
_, addrs, _, _ := c.Addresses(w.chainParams)
|
||||
for _, addr := range addrs {
|
||||
addressID := addr.ScriptAddress()
|
||||
if err := w.Manager.MarkUsed(addressID); err != nil {
|
||||
if err := w.Manager.MarkUsed(addr); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("Marked address used %s", addr.EncodeAddress())
|
||||
|
@ -715,14 +714,6 @@ func (w *Wallet) diskWriter() {
|
|||
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
|
||||
// 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.
|
||||
|
@ -732,7 +723,11 @@ func (w *Wallet) AccountUsed(account uint32) (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
if w.AddressUsed(addr) {
|
||||
used, err := addr.Used()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if used {
|
||||
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) {
|
||||
addr, err := w.Manager.LastExternalAddress(account)
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue