CurrentAddress: subsequently return new address

This commit is contained in:
Javed Khan 2015-04-14 19:47:02 +05:30
parent 948609f064
commit 74208f90c1
5 changed files with 86 additions and 62 deletions

View file

@ -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
}

View file

@ -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)

View file

@ -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.

View file

@ -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
}

View file

@ -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)
}