Skip to content

Commit

Permalink
CurrentAddress: subsequently return new address
Browse files Browse the repository at this point in the history
  • Loading branch information
tuxcanfly committed Apr 16, 2015
1 parent 948609f commit 74208f9
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 52 deletions.
13 changes: 6 additions & 7 deletions waddrmgr/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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 {
Expand All @@ -496,6 +496,5 @@ func newScriptAddress(m *Manager, account uint32, scriptHash, scriptEncrypted []
account: account,
address: address,
scriptEncrypted: scriptEncrypted,
used: used,
}, nil
}
21 changes: 8 additions & 13 deletions waddrmgr/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ type dbAddressRow struct {
account uint32
addTime uint64
syncStatus syncStatus
used bool
rawData []byte // Varies based on address type field.
}

Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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)
Expand Down
43 changes: 30 additions & 13 deletions waddrmgr/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
}
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
23 changes: 16 additions & 7 deletions waddrmgr/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1028,28 +1028,37 @@ func testMarkUsed(tc *testContext) bool {
addrHash := expectedAddr1.addressHash
addr, err := btcutil.NewAddressPubKeyHash(addrHash, chainParams)

maddr, err := tc.manager.Address(addr)
if err != nil {
tc.t.Errorf("%s: unexpected error: %v", prefix, err)
return false
}
if tc.create {
// Test that initially the address is not flagged as used
maddr, err := tc.manager.Address(addr)
used, err := maddr.Used()
if err != nil {
tc.t.Errorf("%s: unexpected error: %v", prefix, err)
return false
}
if maddr.Used() != false {
if used != false {
tc.t.Errorf("%v: unexpected used flag -- got "+
"%v, want %v", prefix, maddr.Used(), expectedAddr1.used)
"%v, want %v", prefix, used, expectedAddr1.used)
return false
}
}
err = tc.manager.MarkUsed(addrHash)
err = tc.manager.MarkUsed(addr)
if err != nil {
tc.t.Errorf("%s: unexpected error: %v", prefix, err)
return false
}
maddr, err := tc.manager.Address(addr)
used, err := maddr.Used()
if err != nil {
tc.t.Errorf("%s: unexpected error: %v", prefix, err)
return false
}
if maddr.Used() != expectedAddr1.used {
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
}
Expand Down
28 changes: 16 additions & 12 deletions wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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.
Expand All @@ -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
}
}
Expand Down Expand Up @@ -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)
}

Expand Down

0 comments on commit 74208f9

Please sign in to comment.