Prepare for adding other types of wallet addresses other than pkhash
Add a walletAddress interface to handle the differences betweent he different types. Stop using btcutil.AddressPubKeyHash everywhere and just use the standard address.
This commit is contained in:
parent
df31e30839
commit
59fb904dc7
2 changed files with 201 additions and 125 deletions
224
wallet/wallet.go
224
wallet/wallet.go
|
@ -473,9 +473,14 @@ func (v *varEntries) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type addressKey string
|
||||||
type transactionHashKey string
|
type transactionHashKey string
|
||||||
type comment []byte
|
type comment []byte
|
||||||
|
|
||||||
|
func getAddressKey(addr btcutil.Address) addressKey {
|
||||||
|
return addressKey(addr.ScriptAddress())
|
||||||
|
}
|
||||||
|
|
||||||
// Wallet represents an btcwallet wallet in memory. It implements
|
// Wallet represents an btcwallet wallet in memory. It implements
|
||||||
// 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.
|
||||||
|
@ -494,15 +499,15 @@ type Wallet struct {
|
||||||
// root address and the appended entries.
|
// root address and the appended entries.
|
||||||
recent recentBlocks
|
recent recentBlocks
|
||||||
|
|
||||||
addrMap map[btcutil.AddressPubKeyHash]*btcAddress
|
addrMap map[addressKey]walletAddress
|
||||||
addrCommentMap map[btcutil.AddressPubKeyHash]comment
|
addrCommentMap map[addressKey]comment
|
||||||
txCommentMap map[transactionHashKey]comment
|
txCommentMap map[transactionHashKey]comment
|
||||||
|
|
||||||
// The rest of the fields in this struct are not serialized.
|
// The rest of the fields in this struct are not serialized.
|
||||||
passphrase []byte
|
passphrase []byte
|
||||||
secret []byte
|
secret []byte
|
||||||
chainIdxMap map[int64]*btcutil.AddressPubKeyHash
|
chainIdxMap map[int64]btcutil.Address
|
||||||
importedAddrs []*btcAddress
|
importedAddrs []walletAddress
|
||||||
lastChainIdx int64
|
lastChainIdx int64
|
||||||
missingKeysStart int64
|
missingKeysStart int64
|
||||||
}
|
}
|
||||||
|
@ -575,10 +580,10 @@ func NewWallet(name, desc string, passphrase []byte, net btcwire.BitcoinNet,
|
||||||
&createdAt.Hash,
|
&createdAt.Hash,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
addrMap: make(map[btcutil.AddressPubKeyHash]*btcAddress),
|
addrMap: make(map[addressKey]walletAddress),
|
||||||
addrCommentMap: make(map[btcutil.AddressPubKeyHash]comment),
|
addrCommentMap: make(map[addressKey]comment),
|
||||||
txCommentMap: make(map[transactionHashKey]comment),
|
txCommentMap: make(map[transactionHashKey]comment),
|
||||||
chainIdxMap: make(map[int64]*btcutil.AddressPubKeyHash),
|
chainIdxMap: make(map[int64]btcutil.Address),
|
||||||
lastChainIdx: rootKeyChainIdx,
|
lastChainIdx: rootKeyChainIdx,
|
||||||
secret: aeskey,
|
secret: aeskey,
|
||||||
}
|
}
|
||||||
|
@ -586,8 +591,9 @@ func NewWallet(name, desc string, passphrase []byte, net btcwire.BitcoinNet,
|
||||||
copy(w.desc[:], []byte(desc))
|
copy(w.desc[:], []byte(desc))
|
||||||
|
|
||||||
// Add root address to maps.
|
// Add root address to maps.
|
||||||
w.addrMap[*w.keyGenerator.address(net)] = &w.keyGenerator
|
rootAddr := w.keyGenerator.address(w.net)
|
||||||
w.chainIdxMap[rootKeyChainIdx] = w.keyGenerator.address(net)
|
w.addrMap[getAddressKey(rootAddr)] = &w.keyGenerator
|
||||||
|
w.chainIdxMap[rootKeyChainIdx] = rootAddr
|
||||||
|
|
||||||
// Fill keypool.
|
// Fill keypool.
|
||||||
if err := w.extendKeypool(keypoolSize, createdAt); err != nil {
|
if err := w.extendKeypool(keypoolSize, createdAt); err != nil {
|
||||||
|
@ -620,9 +626,9 @@ func (w *Wallet) Name() string {
|
||||||
func (w *Wallet) ReadFrom(r io.Reader) (n int64, err error) {
|
func (w *Wallet) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
var read int64
|
var read int64
|
||||||
|
|
||||||
w.addrMap = make(map[btcutil.AddressPubKeyHash]*btcAddress)
|
w.addrMap = make(map[addressKey]walletAddress)
|
||||||
w.addrCommentMap = make(map[btcutil.AddressPubKeyHash]comment)
|
w.addrCommentMap = make(map[addressKey]comment)
|
||||||
w.chainIdxMap = make(map[int64]*btcutil.AddressPubKeyHash)
|
w.chainIdxMap = make(map[int64]btcutil.Address)
|
||||||
w.txCommentMap = make(map[transactionHashKey]comment)
|
w.txCommentMap = make(map[transactionHashKey]comment)
|
||||||
|
|
||||||
var id [8]byte
|
var id [8]byte
|
||||||
|
@ -671,7 +677,7 @@ func (w *Wallet) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
|
|
||||||
// Add root address to address map.
|
// Add root address to address map.
|
||||||
rootAddr := w.keyGenerator.address(w.net)
|
rootAddr := w.keyGenerator.address(w.net)
|
||||||
w.addrMap[*rootAddr] = &w.keyGenerator
|
w.addrMap[getAddressKey(rootAddr)] = &w.keyGenerator
|
||||||
w.chainIdxMap[rootKeyChainIdx] = rootAddr
|
w.chainIdxMap[rootKeyChainIdx] = rootAddr
|
||||||
|
|
||||||
// Fill unserializied fields.
|
// Fill unserializied fields.
|
||||||
|
@ -680,8 +686,8 @@ func (w *Wallet) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
switch e := wt.(type) {
|
switch e := wt.(type) {
|
||||||
case *addrEntry:
|
case *addrEntry:
|
||||||
addr := e.addr.address(w.net)
|
addr := e.addr.address(w.net)
|
||||||
w.addrMap[*addr] = &e.addr
|
w.addrMap[getAddressKey(addr)] = &e.addr
|
||||||
if e.addr.chainIndex == importedKeyChainIdx {
|
if e.addr.imported() {
|
||||||
w.importedAddrs = append(w.importedAddrs, &e.addr)
|
w.importedAddrs = append(w.importedAddrs, &e.addr)
|
||||||
} else {
|
} else {
|
||||||
w.chainIdxMap[e.addr.chainIndex] = addr
|
w.chainIdxMap[e.addr.chainIndex] = addr
|
||||||
|
@ -703,7 +709,8 @@ func (w *Wallet) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
|
|
||||||
case *addrCommentEntry:
|
case *addrCommentEntry:
|
||||||
addr := e.address(w.net)
|
addr := e.address(w.net)
|
||||||
w.addrCommentMap[*addr] = comment(e.comment)
|
w.addrCommentMap[getAddressKey(addr)] =
|
||||||
|
comment(e.comment)
|
||||||
|
|
||||||
case *txCommentEntry:
|
case *txCommentEntry:
|
||||||
txKey := transactionHashKey(e.txHash[:])
|
txKey := transactionHashKey(e.txHash[:])
|
||||||
|
@ -723,18 +730,21 @@ func (w *Wallet) WriteTo(wtr io.Writer) (n int64, err error) {
|
||||||
var wts []io.WriterTo
|
var wts []io.WriterTo
|
||||||
var chainedAddrs = make([]io.WriterTo, len(w.chainIdxMap)-1)
|
var chainedAddrs = make([]io.WriterTo, len(w.chainIdxMap)-1)
|
||||||
var importedAddrs []io.WriterTo
|
var importedAddrs []io.WriterTo
|
||||||
for addr, btcAddr := range w.addrMap {
|
for _, wAddr := range w.addrMap {
|
||||||
|
switch btcAddr := wAddr.(type) {
|
||||||
|
case *btcAddress:
|
||||||
e := &addrEntry{
|
e := &addrEntry{
|
||||||
addr: *btcAddr,
|
addr: *btcAddr,
|
||||||
}
|
}
|
||||||
copy(e.pubKeyHash160[:], addr.ScriptAddress())
|
copy(e.pubKeyHash160[:], btcAddr.pubKeyHash[:])
|
||||||
if btcAddr.chainIndex >= 0 {
|
if btcAddr.imported() {
|
||||||
|
// No order for imported addresses.
|
||||||
|
importedAddrs = append(importedAddrs, e)
|
||||||
|
} else if btcAddr.chainIndex >= 0 {
|
||||||
// Chained addresses are sorted. This is
|
// Chained addresses are sorted. This is
|
||||||
// kind of nice but probably isn't necessary.
|
// kind of nice but probably isn't necessary.
|
||||||
chainedAddrs[btcAddr.chainIndex] = e
|
chainedAddrs[btcAddr.chainIndex] = e
|
||||||
} else if btcAddr.chainIndex == importedKeyChainIdx {
|
}
|
||||||
// No order for imported addresses.
|
|
||||||
importedAddrs = append(importedAddrs, e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wts = append(chainedAddrs, importedAddrs...)
|
wts = append(chainedAddrs, importedAddrs...)
|
||||||
|
@ -742,7 +752,9 @@ func (w *Wallet) WriteTo(wtr io.Writer) (n int64, err error) {
|
||||||
e := &addrCommentEntry{
|
e := &addrCommentEntry{
|
||||||
comment: []byte(comment),
|
comment: []byte(comment),
|
||||||
}
|
}
|
||||||
copy(e.pubKeyHash160[:], addr.ScriptAddress())
|
// addresskey is the address hash as a string, we can cast it
|
||||||
|
// safely (though a little distasteful).
|
||||||
|
copy(e.pubKeyHash160[:], []byte(addr))
|
||||||
wts = append(wts, e)
|
wts = append(wts, e)
|
||||||
}
|
}
|
||||||
for hash, comment := range w.txCommentMap {
|
for hash, comment := range w.txCommentMap {
|
||||||
|
@ -868,7 +880,13 @@ func (w *Wallet) ChangePassphrase(new []byte) error {
|
||||||
oldkey := w.secret
|
oldkey := w.secret
|
||||||
newkey := Key(new, &w.kdfParams)
|
newkey := Key(new, &w.kdfParams)
|
||||||
|
|
||||||
for _, a := range w.addrMap {
|
for _, wa := range w.addrMap {
|
||||||
|
// Only btcAddresses curently have private keys.
|
||||||
|
a, ok := wa.(*btcAddress)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if err := a.changeEncryptionKey(oldkey, newkey); err != nil {
|
if err := a.changeEncryptionKey(oldkey, newkey); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -902,7 +920,7 @@ func (w *Wallet) IsLocked() bool {
|
||||||
// is used. If not and the wallet is unlocked, the keypool is extended.
|
// is used. If not and the wallet is unlocked, the keypool is extended.
|
||||||
// If locked, a new address's pubkey is chained off the last pubkey
|
// If locked, a new address's pubkey is chained off the last pubkey
|
||||||
// and added to the wallet.
|
// and added to the wallet.
|
||||||
func (w *Wallet) NextChainedAddress(bs *BlockStamp, keypoolSize uint) (*btcutil.AddressPubKeyHash, error) {
|
func (w *Wallet) NextChainedAddress(bs *BlockStamp, keypoolSize uint) (btcutil.Address, error) {
|
||||||
addr, err := w.nextChainedAddress(bs, keypoolSize)
|
addr, err := w.nextChainedAddress(bs, keypoolSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -912,7 +930,7 @@ func (w *Wallet) NextChainedAddress(bs *BlockStamp, keypoolSize uint) (*btcutil.
|
||||||
return addr.address(w.net), nil
|
return addr.address(w.net), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wallet) ChangeAddress(bs *BlockStamp, keypoolSize uint) (*btcutil.AddressPubKeyHash, error) {
|
func (w *Wallet) ChangeAddress(bs *BlockStamp, keypoolSize uint) (btcutil.Address, error) {
|
||||||
addr, err := w.nextChainedAddress(bs, keypoolSize)
|
addr, err := w.nextChainedAddress(bs, keypoolSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -948,20 +966,25 @@ func (w *Wallet) nextChainedAddress(bs *BlockStamp, keypoolSize uint) (*btcAddre
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look up address.
|
// Look up address.
|
||||||
addr, ok := w.addrMap[*nextAPKH]
|
addr, ok := w.addrMap[getAddressKey(nextAPKH)]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("cannot find generated address")
|
return nil, errors.New("cannot find generated address")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
btcAddr, ok := addr.(*btcAddress)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("found non-pubkey chained address")
|
||||||
|
}
|
||||||
|
|
||||||
w.highestUsed++
|
w.highestUsed++
|
||||||
|
|
||||||
return addr, nil
|
return btcAddr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastChainedAddress returns the most recently requested chained
|
// LastChainedAddress returns the most recently requested chained
|
||||||
// address from calling NextChainedAddress, or the root address if
|
// address from calling NextChainedAddress, or the root address if
|
||||||
// no chained addresses have been requested.
|
// no chained addresses have been requested.
|
||||||
func (w *Wallet) LastChainedAddress() *btcutil.AddressPubKeyHash {
|
func (w *Wallet) LastChainedAddress() btcutil.Address {
|
||||||
return w.chainIdxMap[w.highestUsed]
|
return w.chainIdxMap[w.highestUsed]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -970,17 +993,22 @@ func (w *Wallet) extendKeypool(n uint, bs *BlockStamp) error {
|
||||||
// Get last chained address. New chained addresses will be
|
// Get last chained address. New chained addresses will be
|
||||||
// chained off of this address's chaincode and private key.
|
// chained off of this address's chaincode and private key.
|
||||||
a := w.chainIdxMap[w.lastChainIdx]
|
a := w.chainIdxMap[w.lastChainIdx]
|
||||||
addr, ok := w.addrMap[*a]
|
waddr, ok := w.addrMap[getAddressKey(a)]
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("expected last chained address not found")
|
return errors.New("expected last chained address not found")
|
||||||
}
|
}
|
||||||
if len(w.secret) != 32 {
|
if len(w.secret) != 32 {
|
||||||
return ErrWalletLocked
|
return ErrWalletLocked
|
||||||
}
|
}
|
||||||
privkey, err := addr.unlock(w.secret)
|
privkey, err := waddr.unlock(w.secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addr, ok := waddr.(*btcAddress)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("found non-pubkey chained address")
|
||||||
|
}
|
||||||
cc := addr.chaincode[:]
|
cc := addr.chaincode[:]
|
||||||
|
|
||||||
// Create n encrypted addresses and add each to the wallet's
|
// Create n encrypted addresses and add each to the wallet's
|
||||||
|
@ -1001,10 +1029,11 @@ func (w *Wallet) extendKeypool(n uint, bs *BlockStamp) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a := newaddr.address(w.net)
|
a := newaddr.address(w.net)
|
||||||
w.addrMap[*a] = newaddr
|
w.addrMap[getAddressKey(a)] = newaddr
|
||||||
newaddr.chainIndex = addr.chainIndex + 1
|
newaddr.chainIndex = addr.chainIndex + 1
|
||||||
w.chainIdxMap[newaddr.chainIndex] = a
|
w.chainIdxMap[newaddr.chainIndex] = a
|
||||||
w.lastChainIdx++
|
w.lastChainIdx++
|
||||||
|
|
||||||
// armory does this.. but all the chaincodes are equal so why
|
// armory does this.. but all the chaincodes are equal so why
|
||||||
// not use the root's?
|
// not use the root's?
|
||||||
copy(newaddr.chaincode[:], cc)
|
copy(newaddr.chaincode[:], cc)
|
||||||
|
@ -1021,11 +1050,16 @@ func (w *Wallet) extendKeypool(n uint, bs *BlockStamp) error {
|
||||||
// not be called unless the keypool has been depleted.
|
// not be called unless the keypool has been depleted.
|
||||||
func (w *Wallet) extendLockedWallet(bs *BlockStamp) error {
|
func (w *Wallet) extendLockedWallet(bs *BlockStamp) error {
|
||||||
a := w.chainIdxMap[w.lastChainIdx]
|
a := w.chainIdxMap[w.lastChainIdx]
|
||||||
addr, ok := w.addrMap[*a]
|
waddr, ok := w.addrMap[getAddressKey(a)]
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("expected last chained address not found")
|
return errors.New("expected last chained address not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addr, ok := waddr.(*btcAddress)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("found non-pubkey chained address")
|
||||||
|
}
|
||||||
|
|
||||||
cc := addr.chaincode[:]
|
cc := addr.chaincode[:]
|
||||||
prevPubkey := addr.pubKey
|
prevPubkey := addr.pubKey
|
||||||
|
|
||||||
|
@ -1038,7 +1072,7 @@ func (w *Wallet) extendLockedWallet(bs *BlockStamp) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a = newaddr.address(w.net)
|
a = newaddr.address(w.net)
|
||||||
w.addrMap[*a] = newaddr
|
w.addrMap[getAddressKey(a)] = newaddr
|
||||||
newaddr.chainIndex = addr.chainIndex + 1
|
newaddr.chainIndex = addr.chainIndex + 1
|
||||||
w.chainIdxMap[newaddr.chainIndex] = a
|
w.chainIdxMap[newaddr.chainIndex] = a
|
||||||
w.lastChainIdx++
|
w.lastChainIdx++
|
||||||
|
@ -1062,10 +1096,16 @@ func (w *Wallet) createMissingPrivateKeys() error {
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("missing previous chained address")
|
return errors.New("missing previous chained address")
|
||||||
}
|
}
|
||||||
prevAddr := w.addrMap[*apkh]
|
prevWAddr := w.addrMap[getAddressKey(apkh)]
|
||||||
if len(w.secret) != 32 {
|
if len(w.secret) != 32 {
|
||||||
return ErrWalletLocked
|
return ErrWalletLocked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prevAddr, ok := prevWAddr.(*btcAddress)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("found non-pubkey chained address")
|
||||||
|
}
|
||||||
|
|
||||||
prevPrivKey, err := prevAddr.unlock(w.secret)
|
prevPrivKey, err := prevAddr.unlock(w.secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1086,7 +1126,11 @@ func (w *Wallet) createMissingPrivateKeys() error {
|
||||||
// Finished.
|
// Finished.
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
addr := w.addrMap[*apkh]
|
waddr := w.addrMap[getAddressKey(apkh)]
|
||||||
|
addr, ok := waddr.(*btcAddress)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("found non-pubkey chained address")
|
||||||
|
}
|
||||||
addr.privKeyCT = ithPrivKey
|
addr.privKeyCT = ithPrivKey
|
||||||
if err := addr.encrypt(w.secret); err != nil {
|
if err := addr.encrypt(w.secret); err != nil {
|
||||||
// Avoid bug: see comment for VersUnsetNeedsPrivkeyFlag.
|
// Avoid bug: see comment for VersUnsetNeedsPrivkeyFlag.
|
||||||
|
@ -1116,22 +1160,17 @@ func (w *Wallet) AddressKey(a btcutil.Address) (key *ecdsa.PrivateKey, err error
|
||||||
return nil, ErrWalletIsWatchingOnly
|
return nil, ErrWalletIsWatchingOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently, only P2PKH addresses are supported. This should
|
|
||||||
// be extended to a switch-case statement when support for other
|
|
||||||
// addresses are added.
|
|
||||||
addr, ok := a.(*btcutil.AddressPubKeyHash)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("unsupported address")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup address from map.
|
// Lookup address from map.
|
||||||
btcaddr, ok := w.addrMap[*addr]
|
waddr, ok := w.addrMap[getAddressKey(a)]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, ErrAddressNotFound
|
return nil, ErrAddressNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// Both the pubkey and encrypted privkey must be recorded to return
|
switch btcaddr := waddr.(type) {
|
||||||
// the private key. Error if neither are saved.
|
case *btcAddress:
|
||||||
|
|
||||||
|
// Both the pubkey and encrypted privkey must be recorded to
|
||||||
|
// return the private key. Error if neither are saved.
|
||||||
if !btcaddr.flags.hasPubKey {
|
if !btcaddr.flags.hasPubKey {
|
||||||
return nil, errors.New("no public key for address")
|
return nil, errors.New("no public key for address")
|
||||||
}
|
}
|
||||||
|
@ -1150,9 +1189,9 @@ func (w *Wallet) AddressKey(a btcutil.Address) (key *ecdsa.PrivateKey, err error
|
||||||
return nil, ErrWalletLocked
|
return nil, ErrWalletLocked
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock address with wallet secret. unlock returns a copy of the
|
// Unlock address with wallet secret. unlock returns a copy of
|
||||||
// clear text private key, and may be used safely even during an address
|
// the clear text private key, and may be used safely even
|
||||||
// lock.
|
// during an address lock.
|
||||||
privKeyCT, err := btcaddr.unlock(w.secret)
|
privKeyCT, err := btcaddr.unlock(w.secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1162,6 +1201,9 @@ func (w *Wallet) AddressKey(a btcutil.Address) (key *ecdsa.PrivateKey, err error
|
||||||
PublicKey: *pubkey,
|
PublicKey: *pubkey,
|
||||||
D: new(big.Int).SetBytes(privKeyCT),
|
D: new(big.Int).SetBytes(privKeyCT),
|
||||||
}, nil
|
}, nil
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unsupported address type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddressInfo returns an AddressInfo structure for an address in a wallet.
|
// AddressInfo returns an AddressInfo structure for an address in a wallet.
|
||||||
|
@ -1175,7 +1217,7 @@ func (w *Wallet) AddressInfo(a btcutil.Address) (AddressInfo, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look up address by address hash.
|
// Look up address by address hash.
|
||||||
btcaddr, ok := w.addrMap[*addr]
|
btcaddr, ok := w.addrMap[getAddressKey(addr)]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, ErrAddressNotFound
|
return nil, ErrAddressNotFound
|
||||||
}
|
}
|
||||||
|
@ -1261,8 +1303,9 @@ func (w *Wallet) EarliestBlockHeight() int32 {
|
||||||
// Imported keys will be the only ones that may have an earlier
|
// Imported keys will be the only ones that may have an earlier
|
||||||
// blockchain height. Check each and set the returned height
|
// blockchain height. Check each and set the returned height
|
||||||
for _, addr := range w.importedAddrs {
|
for _, addr := range w.importedAddrs {
|
||||||
if addr.firstBlock < height {
|
aheight := addr.FirstBlockHeight()
|
||||||
height = addr.firstBlock
|
if aheight < height {
|
||||||
|
height = aheight
|
||||||
|
|
||||||
// Can't go any lower than 0.
|
// Can't go any lower than 0.
|
||||||
if height == 0 {
|
if height == 0 {
|
||||||
|
@ -1286,7 +1329,7 @@ func (w *Wallet) SetBetterEarliestBlockHeight(height int32) {
|
||||||
|
|
||||||
// ImportPrivateKey creates a new encrypted btcAddress with a
|
// ImportPrivateKey creates a new encrypted btcAddress with a
|
||||||
// user-provided private key and adds it to the wallet.
|
// user-provided private key and adds it to the wallet.
|
||||||
func (w *Wallet) ImportPrivateKey(privkey []byte, compressed bool, bs *BlockStamp) (*btcutil.AddressPubKeyHash, error) {
|
func (w *Wallet) ImportPrivateKey(privkey []byte, compressed bool, bs *BlockStamp) (btcutil.Address, error) {
|
||||||
if w.flags.watchingOnly {
|
if w.flags.watchingOnly {
|
||||||
return nil, ErrWalletIsWatchingOnly
|
return nil, ErrWalletIsWatchingOnly
|
||||||
}
|
}
|
||||||
|
@ -1294,12 +1337,7 @@ func (w *Wallet) ImportPrivateKey(privkey []byte, compressed bool, bs *BlockStam
|
||||||
// First, must check that the key being imported will not result
|
// First, must check that the key being imported will not result
|
||||||
// in a duplicate address.
|
// in a duplicate address.
|
||||||
pkh := btcutil.Hash160(pubkeyFromPrivkey(privkey, compressed))
|
pkh := btcutil.Hash160(pubkeyFromPrivkey(privkey, compressed))
|
||||||
// Will always be valid inputs so omit error check.
|
if _, ok := w.addrMap[addressKey(pkh)]; ok {
|
||||||
apkh, err := btcutil.NewAddressPubKeyHash(pkh, w.Net())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, ok := w.addrMap[*apkh]; ok {
|
|
||||||
return nil, ErrDuplicate
|
return nil, ErrDuplicate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1320,14 +1358,15 @@ func (w *Wallet) ImportPrivateKey(privkey []byte, compressed bool, bs *BlockStam
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addr := btcaddr.address(w.Net())
|
||||||
// Add address to wallet's bookkeeping structures. Adding to
|
// Add address to wallet's bookkeeping structures. Adding to
|
||||||
// the map will result in the imported address being serialized
|
// the map will result in the imported address being serialized
|
||||||
// on the next WriteTo call.
|
// on the next WriteTo call.
|
||||||
w.addrMap[*btcaddr.address(w.net)] = btcaddr
|
w.addrMap[getAddressKey(addr)] = btcaddr
|
||||||
w.importedAddrs = append(w.importedAddrs, btcaddr)
|
w.importedAddrs = append(w.importedAddrs, btcaddr)
|
||||||
|
|
||||||
// Create and return address.
|
// Create and return address.
|
||||||
return btcutil.NewAddressPubKeyHash(btcaddr.pubKeyHash[:], w.Net())
|
return addr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateDate returns the Unix time of the wallet creation time. This
|
// CreateDate returns the Unix time of the wallet creation time. This
|
||||||
|
@ -1348,6 +1387,9 @@ func (w *Wallet) ExportWatchingWallet() (*Wallet, error) {
|
||||||
return nil, ErrWalletIsWatchingOnly
|
return nil, ErrWalletIsWatchingOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kgwc := w.keyGenerator.watchingCopy()
|
||||||
|
kgwcba := kgwc.(*btcAddress)
|
||||||
|
|
||||||
// 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{
|
||||||
|
@ -1361,16 +1403,16 @@ func (w *Wallet) ExportWatchingWallet() (*Wallet, error) {
|
||||||
desc: w.desc,
|
desc: w.desc,
|
||||||
createDate: w.createDate,
|
createDate: w.createDate,
|
||||||
highestUsed: w.highestUsed,
|
highestUsed: w.highestUsed,
|
||||||
keyGenerator: *w.keyGenerator.watchingCopy(),
|
keyGenerator: *kgwcba,
|
||||||
recent: recentBlocks{
|
recent: recentBlocks{
|
||||||
lastHeight: w.recent.lastHeight,
|
lastHeight: w.recent.lastHeight,
|
||||||
},
|
},
|
||||||
|
|
||||||
addrMap: make(map[btcutil.AddressPubKeyHash]*btcAddress),
|
addrMap: make(map[addressKey]walletAddress),
|
||||||
addrCommentMap: make(map[btcutil.AddressPubKeyHash]comment),
|
addrCommentMap: make(map[addressKey]comment),
|
||||||
txCommentMap: make(map[transactionHashKey]comment),
|
txCommentMap: make(map[transactionHashKey]comment),
|
||||||
|
|
||||||
chainIdxMap: make(map[int64]*btcutil.AddressPubKeyHash),
|
chainIdxMap: make(map[int64]btcutil.Address),
|
||||||
lastChainIdx: w.lastChainIdx,
|
lastChainIdx: w.lastChainIdx,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1383,10 +1425,14 @@ func (w *Wallet) ExportWatchingWallet() (*Wallet, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for apkh, addr := range w.addrMap {
|
for apkh, addr := range w.addrMap {
|
||||||
apkhCopy := apkh
|
if !addr.imported() {
|
||||||
if addr.chainIndex != importedKeyChainIdx {
|
// Must be a btcAddress if !imported.
|
||||||
ww.chainIdxMap[addr.chainIndex] = &apkhCopy
|
btcAddr := addr.(*btcAddress)
|
||||||
|
|
||||||
|
ww.chainIdxMap[btcAddr.chainIndex] =
|
||||||
|
addr.address(w.net)
|
||||||
}
|
}
|
||||||
|
apkhCopy := apkh
|
||||||
ww.addrMap[apkhCopy] = addr.watchingCopy()
|
ww.addrMap[apkhCopy] = addr.watchingCopy()
|
||||||
}
|
}
|
||||||
for apkh, cmt := range w.addrCommentMap {
|
for apkh, cmt := range w.addrCommentMap {
|
||||||
|
@ -1395,7 +1441,8 @@ func (w *Wallet) ExportWatchingWallet() (*Wallet, error) {
|
||||||
ww.addrCommentMap[apkh] = cmtCopy
|
ww.addrCommentMap[apkh] = cmtCopy
|
||||||
}
|
}
|
||||||
if len(w.importedAddrs) != 0 {
|
if len(w.importedAddrs) != 0 {
|
||||||
ww.importedAddrs = make([]*btcAddress, 0, len(w.importedAddrs))
|
ww.importedAddrs = make([]walletAddress, 0,
|
||||||
|
len(w.importedAddrs))
|
||||||
for _, addr := range w.importedAddrs {
|
for _, addr := range w.importedAddrs {
|
||||||
ww.importedAddrs = append(ww.importedAddrs, addr.watchingCopy())
|
ww.importedAddrs = append(ww.importedAddrs, addr.watchingCopy())
|
||||||
}
|
}
|
||||||
|
@ -1478,7 +1525,7 @@ func (w *Wallet) SortedActiveAddresses() []AddressInfo {
|
||||||
w.highestUsed+int64(len(w.importedAddrs))+1)
|
w.highestUsed+int64(len(w.importedAddrs))+1)
|
||||||
for i := int64(rootKeyChainIdx); i <= w.highestUsed; i++ {
|
for i := int64(rootKeyChainIdx); i <= w.highestUsed; i++ {
|
||||||
a := w.chainIdxMap[i]
|
a := w.chainIdxMap[i]
|
||||||
info, err := w.addrMap[*a].info(w.Net())
|
info, err := w.addrMap[getAddressKey(a)].info(w.Net())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
addrs = append(addrs, info)
|
addrs = append(addrs, info)
|
||||||
}
|
}
|
||||||
|
@ -1499,7 +1546,7 @@ func (w *Wallet) ActiveAddresses() map[btcutil.Address]AddressInfo {
|
||||||
addrs := make(map[btcutil.Address]AddressInfo)
|
addrs := make(map[btcutil.Address]AddressInfo)
|
||||||
for i := int64(rootKeyChainIdx); i <= w.highestUsed; i++ {
|
for i := int64(rootKeyChainIdx); i <= w.highestUsed; i++ {
|
||||||
a := w.chainIdxMap[i]
|
a := w.chainIdxMap[i]
|
||||||
info, err := w.addrMap[*a].info(w.Net())
|
info, err := w.addrMap[getAddressKey(a)].info(w.Net())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
addrs[info.Address()] = info
|
addrs[info.Address()] = info
|
||||||
}
|
}
|
||||||
|
@ -1526,8 +1573,8 @@ func (w *Wallet) ExtendActiveAddresses(n int, keypoolSize uint) ([]btcutil.Addre
|
||||||
return nil, errors.New("n is not positive")
|
return nil, errors.New("n is not positive")
|
||||||
}
|
}
|
||||||
|
|
||||||
last := w.addrMap[*w.chainIdxMap[w.highestUsed]]
|
last := w.addrMap[getAddressKey(w.chainIdxMap[w.highestUsed])]
|
||||||
bs := &BlockStamp{Height: last.firstBlock}
|
bs := &BlockStamp{Height: last.FirstBlockHeight()}
|
||||||
|
|
||||||
addrs := make([]btcutil.Address, 0, n)
|
addrs := make([]btcutil.Address, 0, n)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
|
@ -1892,6 +1939,21 @@ func (u *unusedSpace) WriteTo(w io.Writer) (int64, error) {
|
||||||
return written + int64(n), err
|
return written + int64(n), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type walletAddress interface {
|
||||||
|
io.ReaderFrom
|
||||||
|
io.WriterTo
|
||||||
|
encrypt(key []byte) error
|
||||||
|
lock() error
|
||||||
|
unlock(key []byte) (privKeyCT []byte, err error)
|
||||||
|
changeEncryptionKey(oldkey, newkey []byte) error
|
||||||
|
verifyKeypairs() error
|
||||||
|
address(net btcwire.BitcoinNet) btcutil.Address
|
||||||
|
info(net btcwire.BitcoinNet) (AddressInfo, error)
|
||||||
|
watchingCopy() walletAddress
|
||||||
|
FirstBlockHeight() int32
|
||||||
|
imported() bool
|
||||||
|
}
|
||||||
|
|
||||||
type btcAddress struct {
|
type btcAddress struct {
|
||||||
pubKeyHash [ripemd160.Size]byte
|
pubKeyHash [ripemd160.Size]byte
|
||||||
flags addrFlags
|
flags addrFlags
|
||||||
|
@ -2317,7 +2379,7 @@ func (a *btcAddress) changeEncryptionKey(oldkey, newkey []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// address returns a btcutil.AddressPubKeyHash for a btcAddress.
|
// address returns a btcutil.AddressPubKeyHash for a btcAddress.
|
||||||
func (a *btcAddress) address(net btcwire.BitcoinNet) *btcutil.AddressPubKeyHash {
|
func (a *btcAddress) address(net btcwire.BitcoinNet) btcutil.Address {
|
||||||
// error is not returned because the hash will always be 20
|
// error is not returned because the hash will always be 20
|
||||||
// bytes, and net is assumed to be valid.
|
// bytes, and net is assumed to be valid.
|
||||||
addr, _ := btcutil.NewAddressPubKeyHash(a.pubKeyHash[:], net)
|
addr, _ := btcutil.NewAddressPubKeyHash(a.pubKeyHash[:], net)
|
||||||
|
@ -2334,7 +2396,7 @@ func (a *btcAddress) info(net btcwire.BitcoinNet) (AddressInfo, error) {
|
||||||
addrHash: string(a.pubKeyHash[:]),
|
addrHash: string(a.pubKeyHash[:]),
|
||||||
compressed: a.flags.compressed,
|
compressed: a.flags.compressed,
|
||||||
firstBlock: a.firstBlock,
|
firstBlock: a.firstBlock,
|
||||||
imported: a.chainIndex == importedKeyChainIdx,
|
imported: a.imported(),
|
||||||
Pubkey: hex.EncodeToString(a.pubKey),
|
Pubkey: hex.EncodeToString(a.pubKey),
|
||||||
change: a.flags.change,
|
change: a.flags.change,
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -2343,7 +2405,7 @@ func (a *btcAddress) info(net btcwire.BitcoinNet) (AddressInfo, error) {
|
||||||
// watchingCopy creates a copy of an address without a private key.
|
// watchingCopy creates a copy of an address without a private key.
|
||||||
// This is used to fill a watching a wallet with addresses from a
|
// This is used to fill a watching a wallet with addresses from a
|
||||||
// normal wallet.
|
// normal wallet.
|
||||||
func (a *btcAddress) watchingCopy() *btcAddress {
|
func (a *btcAddress) watchingCopy() walletAddress {
|
||||||
return &btcAddress{
|
return &btcAddress{
|
||||||
pubKeyHash: a.pubKeyHash,
|
pubKeyHash: a.pubKeyHash,
|
||||||
flags: addrFlags{
|
flags: addrFlags{
|
||||||
|
@ -2365,6 +2427,14 @@ func (a *btcAddress) watchingCopy() *btcAddress {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *btcAddress) FirstBlockHeight() int32 {
|
||||||
|
return a.firstBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *btcAddress) imported() bool {
|
||||||
|
return a.chainIndex == importedKeyChainIdx
|
||||||
|
}
|
||||||
|
|
||||||
func walletHash(b []byte) uint32 {
|
func walletHash(b []byte) uint32 {
|
||||||
sum := btcwire.DoubleSha256(b)
|
sum := btcwire.DoubleSha256(b)
|
||||||
return binary.LittleEndian.Uint32(sum)
|
return binary.LittleEndian.Uint32(sum)
|
||||||
|
|
|
@ -489,10 +489,10 @@ func TestWatchingWalletExport(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maintain a set of the active addresses in the wallet.
|
// Maintain a set of the active addresses in the wallet.
|
||||||
activeAddrs := make(map[btcutil.AddressPubKeyHash]struct{})
|
activeAddrs := make(map[addressKey]struct{})
|
||||||
|
|
||||||
// Add root address.
|
// Add root address.
|
||||||
activeAddrs[*w.LastChainedAddress()] = struct{}{}
|
activeAddrs[getAddressKey(w.LastChainedAddress())] = struct{}{}
|
||||||
|
|
||||||
// Get as many new active addresses as necessary to deplete the keypool.
|
// Get as many new active addresses as necessary to deplete the keypool.
|
||||||
// This is done as we will want to test that new addresses created by
|
// This is done as we will want to test that new addresses created by
|
||||||
|
@ -504,7 +504,7 @@ func TestWatchingWalletExport(t *testing.T) {
|
||||||
t.Errorf("unable to get next address: %v", err)
|
t.Errorf("unable to get next address: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
activeAddrs[*apkh] = struct{}{}
|
activeAddrs[getAddressKey(apkh)] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create watching wallet from w.
|
// Create watching wallet from w.
|
||||||
|
@ -541,23 +541,29 @@ func TestWatchingWalletExport(t *testing.T) {
|
||||||
t.Errorf("Watching root address marked as needing a private key to be generated later.")
|
t.Errorf("Watching root address marked as needing a private key to be generated later.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for apkh, addr := range ww.addrMap {
|
for apkh, waddr := range ww.addrMap {
|
||||||
|
switch addr := waddr.(type) {
|
||||||
|
case *btcAddress:
|
||||||
if addr.flags.encrypted {
|
if addr.flags.encrypted {
|
||||||
t.Errorf("Chained address should not be encrypted (nothing to encrypt)")
|
t.Errorf("Chained address should not be encrypted (nothing to encrypt)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ww.keyGenerator.flags.hasPrivKey {
|
if addr.flags.hasPrivKey {
|
||||||
t.Errorf("Chained address marked as having a private key.")
|
t.Errorf("Chained address marked as having a private key.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !ww.keyGenerator.flags.hasPubKey {
|
if !addr.flags.hasPubKey {
|
||||||
t.Errorf("Chained address marked as missing a public key.")
|
t.Errorf("Chained address marked as missing a public key.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ww.keyGenerator.flags.createPrivKeyNextUnlock {
|
if addr.flags.createPrivKeyNextUnlock {
|
||||||
t.Errorf("Chained address marked as needing a private key to be generated later.")
|
t.Errorf("Chained address marked as needing a private key to be generated later.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
t.Errorf("Chained address unknown type!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if _, ok := activeAddrs[apkh]; !ok {
|
if _, ok := activeAddrs[apkh]; !ok {
|
||||||
t.Errorf("Address from watching wallet not found in original wallet.")
|
t.Errorf("Address from watching wallet not found in original wallet.")
|
||||||
|
@ -587,7 +593,7 @@ func TestWatchingWalletExport(t *testing.T) {
|
||||||
t.Errorf("Cannot get next chained address for watching wallet: %v", err)
|
t.Errorf("Cannot get next chained address for watching wallet: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if addr.String() != waddr.String() {
|
if addr.EncodeAddress() != waddr.EncodeAddress() {
|
||||||
t.Errorf("Next addresses for each wallet do not match eachother.")
|
t.Errorf("Next addresses for each wallet do not match eachother.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue