diff --git a/wallet/import.go b/wallet/import.go new file mode 100644 index 0000000..49442fd --- /dev/null +++ b/wallet/import.go @@ -0,0 +1,113 @@ +package wallet + +import ( + "fmt" + + "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwallet/waddrmgr" + "github.com/btcsuite/btcwallet/walletdb" +) + +// ImportPrivateKey imports a private key to the wallet and writes the new +// wallet to disk. +// +// NOTE: If a block stamp is not provided, then the wallet's birthday will be +// set to the genesis block of the corresponding chain. +func (w *Wallet) ImportPrivateKey(scope waddrmgr.KeyScope, wif *btcutil.WIF, + bs *waddrmgr.BlockStamp, rescan bool) (string, error) { + + manager, err := w.Manager.FetchScopedKeyManager(scope) + if err != nil { + return "", err + } + + // The starting block for the key is the genesis block unless otherwise + // specified. + if bs == nil { + bs = &waddrmgr.BlockStamp{ + Hash: *w.chainParams.GenesisHash, + Height: 0, + Timestamp: w.chainParams.GenesisBlock.Header.Timestamp, + } + } else if bs.Timestamp.IsZero() { + // Only update the new birthday time from default value if we + // actually have timestamp info in the header. + header, err := w.chainClient.GetBlockHeader(&bs.Hash) + if err == nil { + bs.Timestamp = header.Timestamp + } + } + + // Attempt to import private key into wallet. + var addr btcutil.Address + var props *waddrmgr.AccountProperties + err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error { + addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey) + maddr, err := manager.ImportPrivateKey(addrmgrNs, wif, bs) + if err != nil { + return err + } + addr = maddr.Address() + props, err = manager.AccountProperties( + addrmgrNs, waddrmgr.ImportedAddrAccount, + ) + if err != nil { + return err + } + + // We'll only update our birthday with the new one if it is + // before our current one. Otherwise, if we do, we can + // potentially miss detecting relevant chain events that + // occurred between them while rescanning. + birthdayBlock, _, err := w.Manager.BirthdayBlock(addrmgrNs) + if err != nil { + return err + } + if bs.Height >= birthdayBlock.Height { + return nil + } + + err = w.Manager.SetBirthday(addrmgrNs, bs.Timestamp) + if err != nil { + return err + } + + // To ensure this birthday block is correct, we'll mark it as + // unverified to prompt a sanity check at the next restart to + // ensure it is correct as it was provided by the caller. + return w.Manager.SetBirthdayBlock(addrmgrNs, *bs, false) + }) + if err != nil { + return "", err + } + + // Rescan blockchain for transactions with txout scripts paying to the + // imported address. + if rescan { + job := &RescanJob{ + Addrs: []btcutil.Address{addr}, + OutPoints: nil, + BlockStamp: *bs, + } + + // Submit rescan job and log when the import has completed. + // Do not block on finishing the rescan. The rescan success + // or failure is logged elsewhere, and the channel is not + // required to be read, so discard the return value. + _ = w.SubmitRescan(job) + } else { + err := w.chainClient.NotifyReceived([]btcutil.Address{addr}) + if err != nil { + return "", fmt.Errorf("Failed to subscribe for address ntfns for "+ + "address %s: %s", addr.EncodeAddress(), err) + } + } + + addrStr := addr.EncodeAddress() + log.Infof("Imported payment address %s", addrStr) + + w.NtfnServer.notifyAccountProperties(props) + + // Return the payment address string of the imported private key. + return addrStr, nil +} diff --git a/wallet/wallet.go b/wallet/wallet.go index 7291de5..3f6ed5d 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -2722,110 +2722,6 @@ func (w *Wallet) DumpWIFPrivateKey(addr btcutil.Address) (string, error) { return wif.String(), nil } -// ImportPrivateKey imports a private key to the wallet and writes the new -// wallet to disk. -// -// NOTE: If a block stamp is not provided, then the wallet's birthday will be -// set to the genesis block of the corresponding chain. -func (w *Wallet) ImportPrivateKey(scope waddrmgr.KeyScope, wif *btcutil.WIF, - bs *waddrmgr.BlockStamp, rescan bool) (string, error) { - - manager, err := w.Manager.FetchScopedKeyManager(scope) - if err != nil { - return "", err - } - - // The starting block for the key is the genesis block unless otherwise - // specified. - if bs == nil { - bs = &waddrmgr.BlockStamp{ - Hash: *w.chainParams.GenesisHash, - Height: 0, - Timestamp: w.chainParams.GenesisBlock.Header.Timestamp, - } - } else if bs.Timestamp.IsZero() { - // Only update the new birthday time from default value if we - // actually have timestamp info in the header. - header, err := w.chainClient.GetBlockHeader(&bs.Hash) - if err == nil { - bs.Timestamp = header.Timestamp - } - } - - // Attempt to import private key into wallet. - var addr btcutil.Address - var props *waddrmgr.AccountProperties - err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error { - addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey) - maddr, err := manager.ImportPrivateKey(addrmgrNs, wif, bs) - if err != nil { - return err - } - addr = maddr.Address() - props, err = manager.AccountProperties( - addrmgrNs, waddrmgr.ImportedAddrAccount, - ) - if err != nil { - return err - } - - // We'll only update our birthday with the new one if it is - // before our current one. Otherwise, if we do, we can - // potentially miss detecting relevant chain events that - // occurred between them while rescanning. - birthdayBlock, _, err := w.Manager.BirthdayBlock(addrmgrNs) - if err != nil { - return err - } - if bs.Height >= birthdayBlock.Height { - return nil - } - - err = w.Manager.SetBirthday(addrmgrNs, bs.Timestamp) - if err != nil { - return err - } - - // To ensure this birthday block is correct, we'll mark it as - // unverified to prompt a sanity check at the next restart to - // ensure it is correct as it was provided by the caller. - return w.Manager.SetBirthdayBlock(addrmgrNs, *bs, false) - }) - if err != nil { - return "", err - } - - // Rescan blockchain for transactions with txout scripts paying to the - // imported address. - if rescan { - job := &RescanJob{ - Addrs: []btcutil.Address{addr}, - OutPoints: nil, - BlockStamp: *bs, - } - - // Submit rescan job and log when the import has completed. - // Do not block on finishing the rescan. The rescan success - // or failure is logged elsewhere, and the channel is not - // required to be read, so discard the return value. - _ = w.SubmitRescan(job) - } else { - err := w.chainClient.NotifyReceived([]btcutil.Address{addr}) - if err != nil { - return "", fmt.Errorf("Failed to subscribe for address ntfns for "+ - "address %s: %s", addr.EncodeAddress(), err) - } - } - - addrStr := addr.EncodeAddress() - log.Infof("Imported payment address %s", addrStr) - - w.NtfnServer.notifyAccountProperties(props) - - // Return the payment address string of the imported private key. - return addrStr, nil -} - // LockedOutpoint returns whether an outpoint has been marked as locked and // should not be used as an input for created transactions. func (w *Wallet) LockedOutpoint(op wire.OutPoint) bool {