mirror of
https://github.com/LBRYFoundation/lbcwallet.git
synced 2025-08-23 17:47:29 +00:00
Merge pull request #535 from cfromknecht/notify-addrs-after-db-commit
wallet/wallet: notify addrs+props after db commit
This commit is contained in:
commit
d14d889e88
4 changed files with 139 additions and 75 deletions
|
@ -21,20 +21,6 @@ import (
|
||||||
// for change when the tests are run.
|
// for change when the tests are run.
|
||||||
var TstLatestMgrVersion = &latestMgrVersion
|
var TstLatestMgrVersion = &latestMgrVersion
|
||||||
|
|
||||||
// Replace the Manager.newSecretKey function with the given one and calls
|
|
||||||
// the callback function. Afterwards the original newSecretKey
|
|
||||||
// function will be restored.
|
|
||||||
func TstRunWithReplacedNewSecretKey(callback func()) {
|
|
||||||
orig := newSecretKey
|
|
||||||
defer func() {
|
|
||||||
newSecretKey = orig
|
|
||||||
}()
|
|
||||||
newSecretKey = func(passphrase *[]byte, config *ScryptOptions) (*snacl.SecretKey, error) {
|
|
||||||
return nil, snacl.ErrDecryptFailed
|
|
||||||
}
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TstCheckPublicPassphrase returns true if the provided public passphrase is
|
// TstCheckPublicPassphrase returns true if the provided public passphrase is
|
||||||
// correct for the manager.
|
// correct for the manager.
|
||||||
func (m *Manager) TstCheckPublicPassphrase(pubPassphrase []byte) bool {
|
func (m *Manager) TstCheckPublicPassphrase(pubPassphrase []byte) bool {
|
||||||
|
|
|
@ -174,15 +174,46 @@ type unlockDeriveInfo struct {
|
||||||
index uint32
|
index uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SecretKeyGenerator is the function signature of a method that can generate
|
||||||
|
// secret keys for the address manager.
|
||||||
|
type SecretKeyGenerator func(
|
||||||
|
passphrase *[]byte, config *ScryptOptions) (*snacl.SecretKey, error)
|
||||||
|
|
||||||
// defaultNewSecretKey returns a new secret key. See newSecretKey.
|
// defaultNewSecretKey returns a new secret key. See newSecretKey.
|
||||||
func defaultNewSecretKey(passphrase *[]byte, config *ScryptOptions) (*snacl.SecretKey, error) {
|
func defaultNewSecretKey(passphrase *[]byte,
|
||||||
|
config *ScryptOptions) (*snacl.SecretKey, error) {
|
||||||
return snacl.NewSecretKey(passphrase, config.N, config.R, config.P)
|
return snacl.NewSecretKey(passphrase, config.N, config.R, config.P)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newSecretKey is used as a way to replace the new secret key generation
|
var (
|
||||||
// function used so tests can provide a version that fails for testing error
|
// secretKeyGen is the inner method that is executed when calling
|
||||||
// paths.
|
// newSecretKey.
|
||||||
var newSecretKey = defaultNewSecretKey
|
secretKeyGen = defaultNewSecretKey
|
||||||
|
|
||||||
|
// secretKeyGenMtx protects access to secretKeyGen, so that it can be
|
||||||
|
// replaced in testing.
|
||||||
|
secretKeyGenMtx sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetSecretKeyGen replaces the existing secret key generator, and returns the
|
||||||
|
// previous generator.
|
||||||
|
func SetSecretKeyGen(keyGen SecretKeyGenerator) SecretKeyGenerator {
|
||||||
|
secretKeyGenMtx.Lock()
|
||||||
|
oldKeyGen := secretKeyGen
|
||||||
|
secretKeyGen = keyGen
|
||||||
|
secretKeyGenMtx.Unlock()
|
||||||
|
|
||||||
|
return oldKeyGen
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSecretKey generates a new secret key using the active secretKeyGen.
|
||||||
|
func newSecretKey(passphrase *[]byte,
|
||||||
|
config *ScryptOptions) (*snacl.SecretKey, error) {
|
||||||
|
|
||||||
|
secretKeyGenMtx.RLock()
|
||||||
|
defer secretKeyGenMtx.RUnlock()
|
||||||
|
return secretKeyGen(passphrase, config)
|
||||||
|
}
|
||||||
|
|
||||||
// EncryptorDecryptor provides an abstraction on top of snacl.CryptoKey so that
|
// EncryptorDecryptor provides an abstraction on top of snacl.CryptoKey so that
|
||||||
// our tests can use dependency injection to force the behaviour they need.
|
// our tests can use dependency injection to force the behaviour they need.
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
|
"github.com/btcsuite/btcwallet/snacl"
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
"github.com/btcsuite/btcwallet/walletdb"
|
"github.com/btcsuite/btcwallet/walletdb"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
@ -33,6 +34,13 @@ func newHash(hexStr string) *chainhash.Hash {
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// failingSecretKeyGen is a waddrmgr.SecretKeyGenerator that always returns
|
||||||
|
// snacl.ErrDecryptFailed.
|
||||||
|
func failingSecretKeyGen(passphrase *[]byte,
|
||||||
|
config *waddrmgr.ScryptOptions) (*snacl.SecretKey, error) {
|
||||||
|
return nil, snacl.ErrDecryptFailed
|
||||||
|
}
|
||||||
|
|
||||||
// testContext is used to store context information about a running test which
|
// testContext is used to store context information about a running test which
|
||||||
// is passed into helper functions. The useSpends field indicates whether or
|
// is passed into helper functions. The useSpends field indicates whether or
|
||||||
// not the spend data should be empty or figure it out based on the specific
|
// not the spend data should be empty or figure it out based on the specific
|
||||||
|
@ -1099,14 +1107,12 @@ func testChangePassphrase(tc *testContext) bool {
|
||||||
// that intentionally errors.
|
// that intentionally errors.
|
||||||
testName := "ChangePassphrase (public) with invalid new secret key"
|
testName := "ChangePassphrase (public) with invalid new secret key"
|
||||||
|
|
||||||
var err error
|
oldKeyGen := waddrmgr.SetSecretKeyGen(failingSecretKeyGen)
|
||||||
waddrmgr.TstRunWithReplacedNewSecretKey(func() {
|
err := walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
||||||
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||||
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
return tc.rootManager.ChangePassphrase(
|
||||||
return tc.rootManager.ChangePassphrase(
|
ns, pubPassphrase, pubPassphrase2, false, fastScrypt,
|
||||||
ns, pubPassphrase, pubPassphrase2, false, fastScrypt,
|
)
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
if !checkManagerError(tc.t, testName, err, waddrmgr.ErrCrypto) {
|
if !checkManagerError(tc.t, testName, err, waddrmgr.ErrCrypto) {
|
||||||
return false
|
return false
|
||||||
|
@ -1114,6 +1120,7 @@ func testChangePassphrase(tc *testContext) bool {
|
||||||
|
|
||||||
// Attempt to change public passphrase with invalid old passphrase.
|
// Attempt to change public passphrase with invalid old passphrase.
|
||||||
testName = "ChangePassphrase (public) with invalid old passphrase"
|
testName = "ChangePassphrase (public) with invalid old passphrase"
|
||||||
|
waddrmgr.SetSecretKeyGen(oldKeyGen)
|
||||||
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
||||||
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||||
return tc.rootManager.ChangePassphrase(
|
return tc.rootManager.ChangePassphrase(
|
||||||
|
|
136
wallet/wallet.go
136
wallet/wallet.go
|
@ -1434,33 +1434,62 @@ func (w *Wallet) CalculateAccountBalances(account uint32, confirms int32) (Balan
|
||||||
// been used (there is at least one transaction spending to it in the
|
// been used (there is at least one transaction spending to it in the
|
||||||
// blockchain or btcd mempool), the next chained address is returned.
|
// blockchain or btcd mempool), the next chained address is returned.
|
||||||
func (w *Wallet) CurrentAddress(account uint32, scope waddrmgr.KeyScope) (btcutil.Address, error) {
|
func (w *Wallet) CurrentAddress(account uint32, scope waddrmgr.KeyScope) (btcutil.Address, error) {
|
||||||
|
chainClient, err := w.requireChainClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
manager, err := w.Manager.FetchScopedKeyManager(scope)
|
manager, err := w.Manager.FetchScopedKeyManager(scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var addr btcutil.Address
|
var (
|
||||||
|
addr btcutil.Address
|
||||||
|
props *waddrmgr.AccountProperties
|
||||||
|
)
|
||||||
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
||||||
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||||
maddr, err := manager.LastExternalAddress(addrmgrNs, account)
|
maddr, err := manager.LastExternalAddress(addrmgrNs, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If no address exists yet, create the first external address
|
// If no address exists yet, create the first external
|
||||||
|
// address.
|
||||||
if waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) {
|
if waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) {
|
||||||
addr, err = w.newAddress(addrmgrNs, account, scope)
|
addr, props, err = w.newAddress(
|
||||||
|
addrmgrNs, account, scope,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return err
|
return 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 maddr.Used(addrmgrNs) {
|
if maddr.Used(addrmgrNs) {
|
||||||
addr, err = w.newAddress(addrmgrNs, account, scope)
|
addr, props, err = w.newAddress(
|
||||||
|
addrmgrNs, account, scope,
|
||||||
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
addr = maddr.Address()
|
addr = maddr.Address()
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return addr, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the props have been initially, then we had to create a new address
|
||||||
|
// to satisfy the query. Notify the rpc server about the new address.
|
||||||
|
if props != nil {
|
||||||
|
err = chainClient.NotifyReceived([]btcutil.Address{addr})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.NtfnServer.notifyAccountProperties(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PubKeyForAddress looks up the associated public key for a P2PKH address.
|
// PubKeyForAddress looks up the associated public key for a P2PKH address.
|
||||||
|
@ -2763,69 +2792,94 @@ func (w *Wallet) SortedActivePaymentAddresses() ([]string, error) {
|
||||||
|
|
||||||
// NewAddress returns the next external chained address for a wallet.
|
// NewAddress returns the next external chained address for a wallet.
|
||||||
func (w *Wallet) NewAddress(account uint32,
|
func (w *Wallet) NewAddress(account uint32,
|
||||||
scope waddrmgr.KeyScope) (addr btcutil.Address, err error) {
|
scope waddrmgr.KeyScope) (btcutil.Address, error) {
|
||||||
|
|
||||||
|
chainClient, err := w.requireChainClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
addr btcutil.Address
|
||||||
|
props *waddrmgr.AccountProperties
|
||||||
|
)
|
||||||
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
||||||
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||||
var err error
|
var err error
|
||||||
addr, err = w.newAddress(addrmgrNs, account, scope)
|
addr, props, err = w.newAddress(addrmgrNs, account, scope)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
return
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify the rpc server about the newly created address.
|
||||||
|
err = chainClient.NotifyReceived([]btcutil.Address{addr})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.NtfnServer.notifyAccountProperties(props)
|
||||||
|
|
||||||
|
return addr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wallet) newAddress(addrmgrNs walletdb.ReadWriteBucket, account uint32,
|
func (w *Wallet) newAddress(addrmgrNs walletdb.ReadWriteBucket, account uint32,
|
||||||
scope waddrmgr.KeyScope) (btcutil.Address, error) {
|
scope waddrmgr.KeyScope) (btcutil.Address, *waddrmgr.AccountProperties, error) {
|
||||||
|
|
||||||
manager, err := w.Manager.FetchScopedKeyManager(scope)
|
manager, err := w.Manager.FetchScopedKeyManager(scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get next address from wallet.
|
// Get next address from wallet.
|
||||||
addrs, err := manager.NextExternalAddresses(addrmgrNs, account, 1)
|
addrs, err := manager.NextExternalAddresses(addrmgrNs, account, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
|
||||||
|
|
||||||
// Request updates from btcd for new transactions sent to this address.
|
|
||||||
utilAddrs := make([]btcutil.Address, len(addrs))
|
|
||||||
for i, addr := range addrs {
|
|
||||||
utilAddrs[i] = addr.Address()
|
|
||||||
}
|
|
||||||
w.chainClientLock.Lock()
|
|
||||||
chainClient := w.chainClient
|
|
||||||
w.chainClientLock.Unlock()
|
|
||||||
if chainClient != nil {
|
|
||||||
err := chainClient.NotifyReceived(utilAddrs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
props, err := manager.AccountProperties(addrmgrNs, account)
|
props, err := manager.AccountProperties(addrmgrNs, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Cannot fetch account properties for notification "+
|
log.Errorf("Cannot fetch account properties for notification "+
|
||||||
"after deriving next external address: %v", err)
|
"after deriving next external address: %v", err)
|
||||||
} else {
|
return nil, nil, err
|
||||||
w.NtfnServer.notifyAccountProperties(props)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return utilAddrs[0], nil
|
return addrs[0].Address(), props, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewChangeAddress returns a new change address for a wallet.
|
// NewChangeAddress returns a new change address for a wallet.
|
||||||
func (w *Wallet) NewChangeAddress(account uint32, scope waddrmgr.KeyScope) (addr btcutil.Address, err error) {
|
func (w *Wallet) NewChangeAddress(account uint32,
|
||||||
|
scope waddrmgr.KeyScope) (btcutil.Address, error) {
|
||||||
|
|
||||||
|
chainClient, err := w.requireChainClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var addr btcutil.Address
|
||||||
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
||||||
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||||
var err error
|
var err error
|
||||||
addr, err = w.newChangeAddress(addrmgrNs, account)
|
addr, err = w.newChangeAddress(addrmgrNs, account)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
return
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify the rpc server about the newly created address.
|
||||||
|
err = chainClient.NotifyReceived([]btcutil.Address{addr})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wallet) newChangeAddress(addrmgrNs walletdb.ReadWriteBucket, account uint32) (btcutil.Address, error) {
|
func (w *Wallet) newChangeAddress(addrmgrNs walletdb.ReadWriteBucket,
|
||||||
|
account uint32) (btcutil.Address, error) {
|
||||||
|
|
||||||
// As we're making a change address, we'll fetch the type of manager
|
// As we're making a change address, we'll fetch the type of manager
|
||||||
// that is able to make p2wkh output as they're the most efficient.
|
// that is able to make p2wkh output as they're the most efficient.
|
||||||
scopes := w.Manager.ScopesForExternalAddrType(
|
scopes := w.Manager.ScopesForExternalAddrType(
|
||||||
|
@ -2842,21 +2896,7 @@ func (w *Wallet) newChangeAddress(addrmgrNs walletdb.ReadWriteBucket, account ui
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request updates from btcd for new transactions sent to this address.
|
return addrs[0].Address(), nil
|
||||||
utilAddrs := make([]btcutil.Address, len(addrs))
|
|
||||||
for i, addr := range addrs {
|
|
||||||
utilAddrs[i] = addr.Address()
|
|
||||||
}
|
|
||||||
|
|
||||||
chainClient, err := w.requireChainClient()
|
|
||||||
if err == nil {
|
|
||||||
err = chainClient.NotifyReceived(utilAddrs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilAddrs[0], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// confirmed checks whether a transaction at height txHeight has met minconf
|
// confirmed checks whether a transaction at height txHeight has met minconf
|
||||||
|
|
Loading…
Add table
Reference in a new issue