mirror of
https://github.com/LBRYFoundation/lbcwallet.git
synced 2025-08-23 17:47:29 +00:00
Refill keypool if empty and wallet is unlocked.
This commit is contained in:
parent
74d7178aa8
commit
38ed238a7f
4 changed files with 113 additions and 42 deletions
2
cmd.go
2
cmd.go
|
@ -334,7 +334,7 @@ func main() {
|
||||||
accounts.Unlock()
|
accounts.Unlock()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Errorf("cannot open wallet: %v", err)
|
log.Warnf("cannot open wallet: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read CA file to verify a btcd TLS connection.
|
// Read CA file to verify a btcd TLS connection.
|
||||||
|
|
29
cmdmgr.go
29
cmdmgr.go
|
@ -388,8 +388,34 @@ func GetNewAddress(frontend chan []byte, icmd btcjson.Cmd) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get current block's height and hash.
|
||||||
|
bs, err := GetCurBlock()
|
||||||
|
if err != nil {
|
||||||
|
e := &btcjson.Error{
|
||||||
|
Code: btcjson.ErrInternal.Code,
|
||||||
|
Message: "btcd disconnected",
|
||||||
|
}
|
||||||
|
ReplyError(frontend, cmd.Id(), e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Get next address from wallet.
|
// Get next address from wallet.
|
||||||
addr, err := a.NextUnusedAddress()
|
addr, err := a.NextChainedAddress(&bs)
|
||||||
|
if err == wallet.ErrWalletLocked {
|
||||||
|
// The wallet is locked error may be sent if the keypool needs
|
||||||
|
// to be refilled, but the wallet is currently in a locked
|
||||||
|
// state. Notify the frontend that an unlock is needed to
|
||||||
|
// refill the keypool.
|
||||||
|
ReplyError(frontend, cmd.Id(), &btcjson.ErrWalletKeypoolRanOut)
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
e := &btcjson.Error{
|
||||||
|
Code: btcjson.ErrWallet.Code,
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
|
ReplyError(frontend, cmd.Id(), e)
|
||||||
|
return
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(jrick): generate new addresses if the address pool is
|
// TODO(jrick): generate new addresses if the address pool is
|
||||||
// empty.
|
// empty.
|
||||||
|
@ -893,6 +919,7 @@ func WalletPassphrase(frontend chan []byte, icmd btcjson.Cmd) {
|
||||||
&btcjson.ErrWalletPassphraseIncorrect)
|
&btcjson.ErrWalletPassphraseIncorrect)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// XXX
|
||||||
ReplySuccess(frontend, cmd.Id(), nil)
|
ReplySuccess(frontend, cmd.Id(), nil)
|
||||||
NotifyWalletLockStateChange("", false)
|
NotifyWalletLockStateChange("", false)
|
||||||
go func() {
|
go func() {
|
||||||
|
|
10
createtx.go
10
createtx.go
|
@ -203,9 +203,15 @@ func (w *Account) txToPairs(pairs map[string]int64, fee int64, minconf int) (*Cr
|
||||||
// Create a new address to spend leftover outputs to.
|
// Create a new address to spend leftover outputs to.
|
||||||
// TODO(jrick): use the next chained address, not the next unused.
|
// TODO(jrick): use the next chained address, not the next unused.
|
||||||
var err error
|
var err error
|
||||||
changeAddr, err = w.NextUnusedAddress()
|
// Get current block's height and hash.
|
||||||
|
bs, err := GetCurBlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get next unused address: %s", err)
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
changeAddr, err = w.NextChainedAddress(&bs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get next address: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spend change
|
// Spend change
|
||||||
|
|
112
wallet/wallet.go
112
wallet/wallet.go
|
@ -46,6 +46,9 @@ const (
|
||||||
// Maximum length in bytes of a comment that can have a size represented
|
// Maximum length in bytes of a comment that can have a size represented
|
||||||
// as a uint16.
|
// as a uint16.
|
||||||
maxCommentLen = (1 << 16) - 1
|
maxCommentLen = (1 << 16) - 1
|
||||||
|
|
||||||
|
// Number of addresses to extend keypool by.
|
||||||
|
nKeypoolIncrement = 100
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -409,9 +412,6 @@ func NewWallet(name, desc string, passphrase []byte, net btcwire.BitcoinNet,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define number of addresses to pre-generate for keypool.
|
|
||||||
const nPregenerated = 100
|
|
||||||
|
|
||||||
// Create and fill wallet.
|
// Create and fill wallet.
|
||||||
w := &Wallet{
|
w := &Wallet{
|
||||||
version: 0, // TODO(jrick): implement versioning
|
version: 0, // TODO(jrick): implement versioning
|
||||||
|
@ -432,7 +432,7 @@ func NewWallet(name, desc string, passphrase []byte, net btcwire.BitcoinNet,
|
||||||
addrCommentMap: make(map[addressHashKey]comment),
|
addrCommentMap: make(map[addressHashKey]comment),
|
||||||
txCommentMap: make(map[transactionHashKey]comment),
|
txCommentMap: make(map[transactionHashKey]comment),
|
||||||
chainIdxMap: make(map[int64]addressHashKey),
|
chainIdxMap: make(map[int64]addressHashKey),
|
||||||
lastChainIdx: nPregenerated - 1,
|
lastChainIdx: rootKeyChainIdx,
|
||||||
}
|
}
|
||||||
copy(w.name[:], []byte(name))
|
copy(w.name[:], []byte(name))
|
||||||
copy(w.desc[:], []byte(desc))
|
copy(w.desc[:], []byte(desc))
|
||||||
|
@ -441,31 +441,10 @@ func NewWallet(name, desc string, passphrase []byte, net btcwire.BitcoinNet,
|
||||||
w.addrMap[addressHashKey(w.keyGenerator.pubKeyHash[:])] = &w.keyGenerator
|
w.addrMap[addressHashKey(w.keyGenerator.pubKeyHash[:])] = &w.keyGenerator
|
||||||
w.chainIdxMap[rootKeyChainIdx] = addressHashKey(w.keyGenerator.pubKeyHash[:])
|
w.chainIdxMap[rootKeyChainIdx] = addressHashKey(w.keyGenerator.pubKeyHash[:])
|
||||||
|
|
||||||
// Pre-generate encrypted addresses and add to maps.
|
// Fill keypool.
|
||||||
addr := &w.keyGenerator
|
if err := w.extendKeypool(nKeypoolIncrement, aeskey, createdAt); err != nil {
|
||||||
cc := addr.chaincode[:]
|
|
||||||
for i := 0; i < nPregenerated; i++ {
|
|
||||||
// Wallet has not been returned to the caller yet, so need to
|
|
||||||
// lock and unlock the previous address's key's clear text
|
|
||||||
// private key mutex.
|
|
||||||
privkey, err := ChainedPrivKey(addr.privKeyCT.key, addr.pubKey, cc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
newaddr, err := newBtcAddress(privkey, nil, createdAt, true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err = newaddr.encrypt(aeskey); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
addrKey := addressHashKey(newaddr.pubKeyHash[:])
|
|
||||||
w.addrMap[addrKey] = newaddr
|
|
||||||
newaddr.chainIndex = addr.chainIndex + 1
|
|
||||||
w.chainIdxMap[newaddr.chainIndex] = addrKey
|
|
||||||
copy(newaddr.chaincode[:], cc) // armory does this.. but why?
|
|
||||||
addr = newaddr
|
|
||||||
}
|
|
||||||
|
|
||||||
return w, nil
|
return w, nil
|
||||||
}
|
}
|
||||||
|
@ -703,29 +682,88 @@ func (w *Wallet) Version() (string, int) {
|
||||||
return "", 0
|
return "", 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextUnusedAddress attempts to get the next chained address.
|
// NextChainedAddress attempts to get the next chained address,
|
||||||
//
|
// refilling the keypool if necessary.
|
||||||
// TODO(jrick): this currently relies on pre-generated addresses
|
func (w *Wallet) NextChainedAddress(bs *BlockStamp) (string, error) {
|
||||||
// and will return an empty string if the address pool has run out.
|
|
||||||
func (w *Wallet) NextUnusedAddress() (string, error) {
|
|
||||||
// Attempt to get address hash of next chained address.
|
// Attempt to get address hash of next chained address.
|
||||||
next160, ok := w.chainIdxMap[w.highestUsed+1]
|
next160, ok := w.chainIdxMap[w.highestUsed+1]
|
||||||
if !ok {
|
if !ok {
|
||||||
// TODO(jrick): Re-fill key pool.
|
// Extending the keypool requires an unlocked wallet.
|
||||||
return "", errors.New("cannot find generated address")
|
aeskey := make([]byte, 32)
|
||||||
|
w.secret.Lock()
|
||||||
|
if len(w.secret.key) != 32 {
|
||||||
|
w.secret.Unlock()
|
||||||
|
return "", ErrWalletLocked
|
||||||
|
}
|
||||||
|
copy(aeskey, w.secret.key)
|
||||||
|
w.secret.Unlock()
|
||||||
|
|
||||||
|
err := w.extendKeypool(nKeypoolIncrement, aeskey, bs)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
next160, ok = w.chainIdxMap[w.highestUsed+1]
|
||||||
|
if !ok {
|
||||||
|
return "", errors.New("chain index map inproperly updated")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
w.highestUsed++
|
|
||||||
|
|
||||||
// Look up address.
|
// Look up address.
|
||||||
addr := w.addrMap[next160]
|
addr, ok := w.addrMap[next160]
|
||||||
if addr == nil {
|
if !ok {
|
||||||
return "", errors.New("cannot find generated address")
|
return "", errors.New("cannot find generated address")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.highestUsed++
|
||||||
|
|
||||||
// Create and return payment address for address hash.
|
// Create and return payment address for address hash.
|
||||||
return addr.paymentAddress(w.net)
|
return addr.paymentAddress(w.net)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extendKeypool grows the keypool by n addresses.
|
||||||
|
func (w *Wallet) extendKeypool(n uint, aeskey []byte, bs *BlockStamp) error {
|
||||||
|
// Get last chained address. New chained addresses will be
|
||||||
|
// chained off of this address's chaincode and private key.
|
||||||
|
addrKey := w.chainIdxMap[w.lastChainIdx]
|
||||||
|
addr, ok := w.addrMap[addrKey]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("expected last chained address not found")
|
||||||
|
}
|
||||||
|
privkey, err := addr.unlock(aeskey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cc := addr.chaincode[:]
|
||||||
|
|
||||||
|
// Create n encrypted addresses and add each to the wallet's
|
||||||
|
// bookkeeping maps.
|
||||||
|
for i := uint(0); i < n; i++ {
|
||||||
|
privkey, err = ChainedPrivKey(privkey, addr.pubKey, cc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newaddr, err := newBtcAddress(privkey, nil, bs, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = newaddr.encrypt(aeskey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
addrKey := addressHashKey(newaddr.pubKeyHash[:])
|
||||||
|
w.addrMap[addrKey] = newaddr
|
||||||
|
newaddr.chainIndex = addr.chainIndex + 1
|
||||||
|
w.chainIdxMap[newaddr.chainIndex] = addrKey
|
||||||
|
w.lastChainIdx++
|
||||||
|
// armory does this.. but all the chaincodes are equal so why
|
||||||
|
// not use the root's?
|
||||||
|
copy(newaddr.chaincode[:], cc)
|
||||||
|
addr = newaddr
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// addrHashForAddress decodes and returns the address hash for a
|
// addrHashForAddress decodes and returns the address hash for a
|
||||||
// payment address string, performing some basic sanity checking that it
|
// payment address string, performing some basic sanity checking that it
|
||||||
// matches the Bitcoin network used by the wallet.
|
// matches the Bitcoin network used by the wallet.
|
||||||
|
|
Loading…
Add table
Reference in a new issue