diff --git a/wallet/import.go b/wallet/import.go index 9f37e93d90..3bbcf97dcb 100644 --- a/wallet/import.go +++ b/wallet/import.go @@ -305,6 +305,15 @@ func (w *Wallet) ImportAccountDryRun(name string, *waddrmgr.AccountProperties, []waddrmgr.ManagedAddress, []waddrmgr.ManagedAddress, error) { + // The address manager uses OnCommit on the walletdb tx to update the + // in-memory state of the account state. But because the commit happens + // _after_ the account manager internal lock has been released, there + // is a chance for the address index to be accessed concurrently, even + // though the closure in OnCommit re-acquires the lock. To avoid this + // issue, we surround the whole address creation process with a lock. + w.newAddrMtx.Lock() + defer w.newAddrMtx.Unlock() + var ( accountProps *waddrmgr.AccountProperties externalAddrs []waddrmgr.ManagedAddress diff --git a/wallet/wallet.go b/wallet/wallet.go index fb7f10419b..2f36f918bb 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -133,6 +133,8 @@ type Wallet struct { chainClientSynced bool chainClientSyncMtx sync.Mutex + newAddrMtx sync.Mutex + lockedOutpoints map[wire.OutPoint]struct{} lockedOutpointsMtx sync.Mutex @@ -3153,6 +3155,15 @@ func (w *Wallet) NewAddress(account uint32, return nil, err } + // The address manager uses OnCommit on the walletdb tx to update the + // in-memory state of the account state. But because the commit happens + // _after_ the account manager internal lock has been released, there + // is a chance for the address index to be accessed concurrently, even + // though the closure in OnCommit re-acquires the lock. To avoid this + // issue, we surround the whole address creation process with a lock. + w.newAddrMtx.Lock() + defer w.newAddrMtx.Unlock() + var ( addr btcutil.Address props *waddrmgr.AccountProperties @@ -3211,6 +3222,15 @@ func (w *Wallet) NewChangeAddress(account uint32, return nil, err } + // The address manager uses OnCommit on the walletdb tx to update the + // in-memory state of the account state. But because the commit happens + // _after_ the account manager internal lock has been released, there + // is a chance for the address index to be accessed concurrently, even + // though the closure in OnCommit re-acquires the lock. To avoid this + // issue, we surround the whole address creation process with a lock. + w.newAddrMtx.Lock() + defer w.newAddrMtx.Unlock() + var addr btcutil.Address err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error { addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)