Unmark addresses as requiring private keys next unlock.
The flag marking chained addresses as needing private keys be generated on the next wallet unlock was not being correctly unset after creating and encrypting the private key. After serializing/deserializing the wallet, on next unlock, recreating missing private keys would begin too early in the chain and fail due to trying to encrypt an already encrypted address. This change correctly unsets the flag and bumps the version so a special case can be created for ignoring duplicate encryption attempts when reading an old wallet file. Tests have also been added to the chained pubkey test to test for this error case.
This commit is contained in:
parent
8dac5080ac
commit
5edd01e8a5
2 changed files with 37 additions and 9 deletions
|
@ -54,6 +54,7 @@ const (
|
||||||
// Possible errors when dealing with wallets.
|
// Possible errors when dealing with wallets.
|
||||||
var (
|
var (
|
||||||
ErrAddressNotFound = errors.New("address not found")
|
ErrAddressNotFound = errors.New("address not found")
|
||||||
|
ErrAlreadyEncrypted = errors.New("private key is already encrypted")
|
||||||
ErrChecksumMismatch = errors.New("checksum mismatch")
|
ErrChecksumMismatch = errors.New("checksum mismatch")
|
||||||
ErrDuplicate = errors.New("duplicate key or address")
|
ErrDuplicate = errors.New("duplicate key or address")
|
||||||
ErrMalformedEntry = errors.New("malformed entry")
|
ErrMalformedEntry = errors.New("malformed entry")
|
||||||
|
@ -384,8 +385,18 @@ var (
|
||||||
// the 20 most recently seen block hashes.
|
// the 20 most recently seen block hashes.
|
||||||
Vers20LastBlocks = version{1, 36, 0, 0}
|
Vers20LastBlocks = version{1, 36, 0, 0}
|
||||||
|
|
||||||
|
// VersUnsetNeedsPrivkeyFlag is the bugfix version where the
|
||||||
|
// createPrivKeyNextUnlock address flag is correctly unset
|
||||||
|
// after creating and encrypting its private key after unlock.
|
||||||
|
// Otherwise, re-creating private keys will occur too early
|
||||||
|
// in the address chain and fail due to encrypting an already
|
||||||
|
// encrypted address. Wallet versions at or before this
|
||||||
|
// version include a special case to allow the duplicate
|
||||||
|
// encrypt.
|
||||||
|
VersUnsetNeedsPrivkeyFlag = version{1, 36, 1, 0}
|
||||||
|
|
||||||
// VersCurrent is the current wallet file version.
|
// VersCurrent is the current wallet file version.
|
||||||
VersCurrent = Vers20LastBlocks
|
VersCurrent = VersUnsetNeedsPrivkeyFlag
|
||||||
)
|
)
|
||||||
|
|
||||||
type varEntries []io.WriterTo
|
type varEntries []io.WriterTo
|
||||||
|
@ -469,6 +480,7 @@ type comment []byte
|
||||||
// the io.ReaderFrom and io.WriterTo interfaces to read from and
|
// the io.ReaderFrom and io.WriterTo interfaces to read from and
|
||||||
// write to any type of byte streams, including files.
|
// write to any type of byte streams, including files.
|
||||||
type Wallet struct {
|
type Wallet struct {
|
||||||
|
vers version
|
||||||
net btcwire.BitcoinNet
|
net btcwire.BitcoinNet
|
||||||
flags walletFlags
|
flags walletFlags
|
||||||
createDate int64
|
createDate int64
|
||||||
|
@ -547,7 +559,8 @@ func NewWallet(name, desc string, passphrase []byte, net btcwire.BitcoinNet,
|
||||||
|
|
||||||
// Create and fill wallet.
|
// Create and fill wallet.
|
||||||
w := &Wallet{
|
w := &Wallet{
|
||||||
net: net,
|
vers: VersCurrent,
|
||||||
|
net: net,
|
||||||
flags: walletFlags{
|
flags: walletFlags{
|
||||||
useEncryption: true,
|
useEncryption: true,
|
||||||
watchingOnly: false,
|
watchingOnly: false,
|
||||||
|
@ -613,7 +626,6 @@ func (w *Wallet) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
w.txCommentMap = make(map[transactionHashKey]comment)
|
w.txCommentMap = make(map[transactionHashKey]comment)
|
||||||
|
|
||||||
var id [8]byte
|
var id [8]byte
|
||||||
var vers version
|
|
||||||
var appendedEntries varEntries
|
var appendedEntries varEntries
|
||||||
|
|
||||||
// Iterate through each entry needing to be read. If data
|
// Iterate through each entry needing to be read. If data
|
||||||
|
@ -621,7 +633,7 @@ func (w *Wallet) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
// data is a pointer to a fixed sized value.
|
// data is a pointer to a fixed sized value.
|
||||||
datas := []interface{}{
|
datas := []interface{}{
|
||||||
&id,
|
&id,
|
||||||
&vers,
|
&w.vers,
|
||||||
&w.net,
|
&w.net,
|
||||||
&w.flags,
|
&w.flags,
|
||||||
make([]byte, 6), // Bytes for Armory unique ID
|
make([]byte, 6), // Bytes for Armory unique ID
|
||||||
|
@ -639,7 +651,7 @@ func (w *Wallet) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
var err error
|
var err error
|
||||||
switch d := data.(type) {
|
switch d := data.(type) {
|
||||||
case ReaderFromVersion:
|
case ReaderFromVersion:
|
||||||
read, err = d.ReadFromVersion(vers, r)
|
read, err = d.ReadFromVersion(w.vers, r)
|
||||||
|
|
||||||
case io.ReaderFrom:
|
case io.ReaderFrom:
|
||||||
read, err = d.ReadFrom(r)
|
read, err = d.ReadFrom(r)
|
||||||
|
@ -750,7 +762,7 @@ func (w *Wallet) WriteTo(wtr io.Writer) (n int64, err error) {
|
||||||
&VersCurrent,
|
&VersCurrent,
|
||||||
&w.net,
|
&w.net,
|
||||||
&w.flags,
|
&w.flags,
|
||||||
make([]byte, 6), // Bytes for Armory unique ID
|
make([]byte, 6), // Bytes for Armory unique ID
|
||||||
&w.createDate,
|
&w.createDate,
|
||||||
&w.name,
|
&w.name,
|
||||||
&w.desc,
|
&w.desc,
|
||||||
|
@ -1078,8 +1090,12 @@ func (w *Wallet) createMissingPrivateKeys() error {
|
||||||
addr := w.addrMap[*apkh]
|
addr := w.addrMap[*apkh]
|
||||||
addr.privKeyCT = ithPrivKey
|
addr.privKeyCT = ithPrivKey
|
||||||
if err := addr.encrypt(w.secret); err != nil {
|
if err := addr.encrypt(w.secret); err != nil {
|
||||||
return err
|
// Avoid bug: see comment for VersUnsetNeedsPrivkeyFlag.
|
||||||
|
if err != ErrAlreadyEncrypted || !w.vers.LT(VersUnsetNeedsPrivkeyFlag) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
addr.flags.createPrivKeyNextUnlock = false
|
||||||
|
|
||||||
// Set previous address and private key for next iteration.
|
// Set previous address and private key for next iteration.
|
||||||
prevAddr = addr
|
prevAddr = addr
|
||||||
|
@ -1336,7 +1352,8 @@ func (w *Wallet) ExportWatchingWallet() (*Wallet, error) {
|
||||||
// Copy members of w into a new wallet, but mark as watching-only and
|
// Copy members of w into a new wallet, but mark as watching-only and
|
||||||
// do not include any private keys.
|
// do not include any private keys.
|
||||||
ww := &Wallet{
|
ww := &Wallet{
|
||||||
net: w.net,
|
vers: w.vers,
|
||||||
|
net: w.net,
|
||||||
flags: walletFlags{
|
flags: walletFlags{
|
||||||
useEncryption: false,
|
useEncryption: false,
|
||||||
watchingOnly: true,
|
watchingOnly: true,
|
||||||
|
@ -2140,7 +2157,7 @@ func (a *btcAddress) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
// not 32 bytes. If successful, the encryption flag is set.
|
// not 32 bytes. If successful, the encryption flag is set.
|
||||||
func (a *btcAddress) encrypt(key []byte) error {
|
func (a *btcAddress) encrypt(key []byte) error {
|
||||||
if a.flags.encrypted {
|
if a.flags.encrypted {
|
||||||
return errors.New("address already encrypted")
|
return ErrAlreadyEncrypted
|
||||||
}
|
}
|
||||||
if len(a.privKeyCT) != 32 {
|
if len(a.privKeyCT) != 32 {
|
||||||
return errors.New("invalid clear text private key")
|
return errors.New("invalid clear text private key")
|
||||||
|
|
|
@ -462,6 +462,17 @@ func TestWalletPubkeyChaining(t *testing.T) {
|
||||||
t.Errorf("ECDSA verification failed; next address's keypair does not match.")
|
t.Errorf("ECDSA verification failed; next address's keypair does not match.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that the serialized wallet correctly unmarked the 'needs private
|
||||||
|
// keys later' flag.
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
w2.WriteTo(buf)
|
||||||
|
w2.ReadFrom(buf)
|
||||||
|
err = w2.Unlock([]byte("banana"))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unlock after serialize/deserialize failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWatchingWalletExport(t *testing.T) {
|
func TestWatchingWalletExport(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue