Switch to new btcutil Address encoding/decoding API.

This commit is contained in:
Josh Rickmar 2014-01-06 12:24:29 -05:00
parent ac79a59c90
commit e8265eca41
8 changed files with 223 additions and 254 deletions

View file

@ -134,7 +134,7 @@ func (a *Account) Rollback(height int32, hash *btcwire.ShaHash) {
// a given address. Assumming correct TxStore usage, this will return true iff // a given address. Assumming correct TxStore usage, this will return true iff
// there are any transactions with outputs to this address in the blockchain or // there are any transactions with outputs to this address in the blockchain or
// the btcd mempool. // the btcd mempool.
func (a *Account) AddressUsed(pkHash []byte) bool { func (a *Account) AddressUsed(addr btcutil.Address) bool {
// This can be optimized by recording this data as it is read when // This can be optimized by recording this data as it is read when
// opening an account, and keeping it up to date each time a new // opening an account, and keeping it up to date each time a new
// received tx arrives. // received tx arrives.
@ -142,6 +142,8 @@ func (a *Account) AddressUsed(pkHash []byte) bool {
a.TxStore.RLock() a.TxStore.RLock()
defer a.TxStore.RUnlock() defer a.TxStore.RUnlock()
pkHash := addr.ScriptAddress()
for i := range a.TxStore.s { for i := range a.TxStore.s {
rtx, ok := a.TxStore.s[i].(*tx.RecvTx) rtx, ok := a.TxStore.s[i].(*tx.RecvTx)
if !ok { if !ok {
@ -193,7 +195,7 @@ func (a *Account) CalculateBalance(confirms int) float64 {
// a UTXO must be in a block. If confirmations is 1 or greater, // a UTXO must be in a block. If confirmations is 1 or greater,
// the balance will be calculated based on how many how many blocks // the balance will be calculated based on how many how many blocks
// include a UTXO. // include a UTXO.
func (a *Account) CalculateAddressBalance(pubkeyHash []byte, confirms int) float64 { func (a *Account) CalculateAddressBalance(addr *btcutil.AddressPubKeyHash, confirms int) float64 {
var bal uint64 // Measured in satoshi var bal uint64 // Measured in satoshi
bs, err := GetCurBlock() bs, err := GetCurBlock()
@ -206,7 +208,7 @@ func (a *Account) CalculateAddressBalance(pubkeyHash []byte, confirms int) float
// Utxos not yet in blocks (height -1) should only be // Utxos not yet in blocks (height -1) should only be
// added if confirmations is 0. // added if confirmations is 0.
if confirms == 0 || (u.Height != -1 && int(bs.Height-u.Height+1) >= confirms) { if confirms == 0 || (u.Height != -1 && int(bs.Height-u.Height+1) >= confirms) {
if bytes.Equal(pubkeyHash, u.AddrHash[:]) { if bytes.Equal(addr.ScriptAddress(), u.AddrHash[:]) {
bal += u.Amt bal += u.Amt
} }
} }
@ -219,22 +221,17 @@ func (a *Account) CalculateAddressBalance(pubkeyHash []byte, confirms int) float
// from an account. If the address has already been used (there is at least // from an account. If the address has already been used (there is at least
// one transaction spending to it in the blockchain or btcd mempool), the next // one transaction spending to it in the blockchain or btcd mempool), the next
// chained address is returned. // chained address is returned.
func (a *Account) CurrentAddress() (string, error) { func (a *Account) CurrentAddress() (btcutil.Address, error) {
a.mtx.RLock() a.mtx.RLock()
addr, err := a.Wallet.LastChainedAddress() addr := a.Wallet.LastChainedAddress()
a.mtx.RUnlock() a.mtx.RUnlock()
if err != nil {
return "", err
}
// Get next chained address if the last one has already been used. // Get next chained address if the last one has already been used.
pkHash, _, _ := btcutil.DecodeAddress(addr) if a.AddressUsed(addr) {
if a.AddressUsed(pkHash) { return a.NewAddress()
addr, err = a.NewAddress()
} }
return addr, err return addr, nil
} }
// ListTransactions returns a slice of maps with details about a recorded // ListTransactions returns a slice of maps with details about a recorded
@ -337,8 +334,8 @@ func (a *Account) ListAllTransactions() ([]map[string]interface{}, error) {
return txInfoList, nil return txInfoList, nil
} }
// DumpPrivKeys returns the WIF-encoded private keys for all addresses // DumpPrivKeys returns the WIF-encoded private keys for all addresses with
// non-watching addresses in a wallets. // private keys in a wallet.
func (a *Account) DumpPrivKeys() ([]string, error) { func (a *Account) DumpPrivKeys() ([]string, error) {
a.mtx.RLock() a.mtx.RLock()
defer a.mtx.RUnlock() defer a.mtx.RUnlock()
@ -346,13 +343,13 @@ func (a *Account) DumpPrivKeys() ([]string, error) {
// Iterate over each active address, appending the private // Iterate over each active address, appending the private
// key to privkeys. // key to privkeys.
var privkeys []string var privkeys []string
for _, addr := range a.ActiveAddresses() { for addr, info := range a.ActiveAddresses() {
key, err := a.AddressKey(addr.Address) key, err := a.AddressKey(addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
encKey, err := btcutil.EncodePrivateKey(key.D.Bytes(), encKey, err := btcutil.EncodePrivateKey(key.D.Bytes(),
a.Net(), addr.Compressed) a.Net(), info.Compressed)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -364,19 +361,19 @@ func (a *Account) DumpPrivKeys() ([]string, error) {
// DumpWIFPrivateKey returns the WIF encoded private key for a // DumpWIFPrivateKey returns the WIF encoded private key for a
// single wallet address. // single wallet address.
func (a *Account) DumpWIFPrivateKey(address string) (string, error) { func (a *Account) DumpWIFPrivateKey(addr btcutil.Address) (string, error) {
a.mtx.RLock() a.mtx.RLock()
defer a.mtx.RUnlock() defer a.mtx.RUnlock()
// Get private key from wallet if it exists. // Get private key from wallet if it exists.
key, err := a.AddressKey(address) key, err := a.AddressKey(addr)
if err != nil { if err != nil {
return "", err return "", err
} }
// Get address info. This is needed to determine whether // Get address info. This is needed to determine whether
// the pubkey is compressed or not. // the pubkey is compressed or not.
info, err := a.AddressInfo(address) info, err := a.AddressInfo(addr)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -573,8 +570,8 @@ func (a *Account) SortedActivePaymentAddresses() []string {
infos := a.SortedActiveAddresses() infos := a.SortedActiveAddresses()
addrs := make([]string, len(infos)) addrs := make([]string, len(infos))
for i, addr := range infos { for i, info := range infos {
addrs[i] = addr.Address addrs[i] = info.Address.EncodeAddress()
} }
return addrs return addrs
@ -590,29 +587,31 @@ func (a *Account) ActivePaymentAddresses() map[string]struct{} {
addrs := make(map[string]struct{}, len(infos)) addrs := make(map[string]struct{}, len(infos))
for _, info := range infos { for _, info := range infos {
addrs[info.Address] = struct{}{} addrs[info.Address.EncodeAddress()] = struct{}{}
} }
return addrs return addrs
} }
// NewAddress returns a new payment address for an account. // NewAddress returns a new payment address for an account.
func (a *Account) NewAddress() (string, error) { func (a *Account) NewAddress() (btcutil.Address, error) {
a.mtx.Lock() a.mtx.Lock()
// Get current block's height and hash. // Get current block's height and hash.
bs, err := GetCurBlock() bs, err := GetCurBlock()
if err != nil { if err != nil {
return "", err a.mtx.Unlock()
return nil, err
} }
// Get next address from wallet. // Get next address from wallet.
addr, err := a.NextChainedAddress(&bs) addr, err := a.NextChainedAddress(&bs)
if err != nil { if err != nil {
return "", err a.mtx.Unlock()
return nil, err
} }
// Write updated wallet to disk. // Immediately write updated wallet to disk.
a.dirty = true a.dirty = true
a.mtx.Unlock() a.mtx.Unlock()
if err = a.writeDirtyToDisk(); err != nil { if err = a.writeDirtyToDisk(); err != nil {
@ -620,7 +619,7 @@ func (a *Account) NewAddress() (string, error) {
} }
// Mark this new address as belonging to this account. // Mark this new address as belonging to this account.
MarkAddressForAccount(addr, a.Name()) MarkAddressForAccount(addr.EncodeAddress(), a.Name())
// Request updates from btcd for new transactions sent to this address. // Request updates from btcd for new transactions sent to this address.
a.ReqNewTxsForAddress(addr) a.ReqNewTxsForAddress(addr)
@ -630,15 +629,21 @@ func (a *Account) NewAddress() (string, error) {
// ReqNewTxsForAddress sends a message to btcd to request tx updates // ReqNewTxsForAddress sends a message to btcd to request tx updates
// for addr for each new block that is added to the blockchain. // for addr for each new block that is added to the blockchain.
func (a *Account) ReqNewTxsForAddress(addr string) { func (a *Account) ReqNewTxsForAddress(addr btcutil.Address) {
log.Debugf("Requesting notifications of TXs sending to address %v", addr) // Only support P2PKH addresses currently.
apkh, ok := addr.(*btcutil.AddressPubKeyHash)
if !ok {
return
}
log.Debugf("Requesting notifications of TXs sending to address %v", apkh)
a.mtx.RLock() a.mtx.RLock()
n := a.NewBlockTxJSONID n := a.NewBlockTxJSONID
a.mtx.RUnlock() a.mtx.RUnlock()
cmd := btcws.NewNotifyNewTXsCmd(fmt.Sprintf("btcwallet(%d)", n), cmd := btcws.NewNotifyNewTXsCmd(fmt.Sprintf("btcwallet(%d)", n),
[]string{addr}) []string{apkh.EncodeAddress()})
mcmd, err := cmd.MarshalJSON() mcmd, err := cmd.MarshalJSON()
if err != nil { if err != nil {
log.Errorf("cannot request transaction notifications: %v", err) log.Errorf("cannot request transaction notifications: %v", err)
@ -719,12 +724,12 @@ func (a *Account) newBlockTxOutHandler(result interface{}, e *btcjson.Error) boo
} }
return false return false
} }
receiver, ok := v["receiver"].(string) receiverStr, ok := v["receiver"].(string)
if !ok { if !ok {
log.Error("Tx Handler: Unspecified receiver.") log.Error("Tx Handler: Unspecified receiver.")
return false return false
} }
receiverHash, _, err := btcutil.DecodeAddress(receiver) receiver, err := btcutil.DecodeAddr(receiverStr)
if err != nil { if err != nil {
log.Errorf("Tx Handler: receiver address can not be decoded: %v", err) log.Errorf("Tx Handler: receiver address can not be decoded: %v", err)
return false return false
@ -810,7 +815,7 @@ func (a *Account) newBlockTxOutHandler(result interface{}, e *btcjson.Error) boo
BlockIndex: blockIndex, BlockIndex: blockIndex,
BlockTime: blockTime, BlockTime: blockTime,
Amount: int64(amt), Amount: int64(amt),
ReceiverHash: receiverHash, ReceiverHash: receiver.ScriptAddress(),
} }
// For transactions originating from this wallet, the sent tx history should // For transactions originating from this wallet, the sent tx history should
@ -863,7 +868,7 @@ func (a *Account) newBlockTxOutHandler(result interface{}, e *btcjson.Error) boo
} }
copy(u.Out.Hash[:], txID[:]) copy(u.Out.Hash[:], txID[:])
u.Out.Index = uint32(txOutIndex) u.Out.Index = uint32(txOutIndex)
copy(u.AddrHash[:], receiverHash) copy(u.AddrHash[:], receiver.ScriptAddress())
copy(u.BlockHash[:], blockHash[:]) copy(u.BlockHash[:], blockHash[:])
a.UtxoStore.Lock() a.UtxoStore.Lock()
a.UtxoStore.s.Insert(u) a.UtxoStore.s.Insert(u)

View file

@ -20,6 +20,7 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"github.com/conformal/btcutil"
"github.com/conformal/btcwallet/tx" "github.com/conformal/btcwallet/tx"
"github.com/conformal/btcwallet/wallet" "github.com/conformal/btcwallet/wallet"
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
@ -247,7 +248,7 @@ func (store *AccountStore) DumpKeys() ([]string, error) {
// DumpWIFPrivateKey searches through all accounts for the bitcoin // DumpWIFPrivateKey searches through all accounts for the bitcoin
// payment address addr and returns the WIF-encdoded private key. // payment address addr and returns the WIF-encdoded private key.
func (store *AccountStore) DumpWIFPrivateKey(addr string) (string, error) { func (store *AccountStore) DumpWIFPrivateKey(addr btcutil.Address) (string, error) {
store.Lock() store.Lock()
defer store.Unlock() defer store.Unlock()

View file

@ -233,7 +233,13 @@ func DumpPrivKey(frontend chan []byte, icmd btcjson.Cmd) {
return return
} }
switch key, err := accountstore.DumpWIFPrivateKey(cmd.Address); err { addr, err := btcutil.DecodeAddr(cmd.Address)
if err != nil {
ReplyError(frontend, cmd.Id(), &btcjson.ErrInvalidAddressOrKey)
return
}
switch key, err := accountstore.DumpWIFPrivateKey(addr); err {
case nil: case nil:
// Key was found. // Key was found.
ReplySuccess(frontend, cmd.Id(), key) ReplySuccess(frontend, cmd.Id(), key)
@ -349,8 +355,24 @@ func GetAccount(frontend chan []byte, icmd btcjson.Cmd) {
} }
// Is address valid? // Is address valid?
_, net, err := btcutil.DecodeAddress(cmd.Address) addr, err := btcutil.DecodeAddr(cmd.Address)
if err != nil || net != cfg.Net() { if err != nil {
ReplyError(frontend, cmd.Id(), &btcjson.ErrInvalidAddressOrKey)
return
}
var net btcwire.BitcoinNet
switch a := addr.(type) {
case *btcutil.AddressPubKeyHash:
net = a.Net()
case *btcutil.AddressScriptHash:
net = a.Net()
default:
ReplyError(frontend, cmd.Id(), &btcjson.ErrInvalidAddressOrKey)
return
}
if net != cfg.Net() {
ReplyError(frontend, cmd.Id(), &btcjson.ErrInvalidAddressOrKey) ReplyError(frontend, cmd.Id(), &btcjson.ErrInvalidAddressOrKey)
return return
} }
@ -429,8 +451,13 @@ func GetAddressBalance(frontend chan []byte, icmd btcjson.Cmd) {
} }
// Is address valid? // Is address valid?
pkhash, net, err := btcutil.DecodeAddress(cmd.Address) addr, err := btcutil.DecodeAddr(cmd.Address)
if err != nil || net != cfg.Net() { if err != nil {
ReplyError(frontend, cmd.Id(), &btcjson.ErrInvalidAddressOrKey)
return
}
apkh, ok := addr.(*btcutil.AddressPubKeyHash)
if !ok || apkh.Net() != cfg.Net() {
ReplyError(frontend, cmd.Id(), &btcjson.ErrInvalidAddressOrKey) ReplyError(frontend, cmd.Id(), &btcjson.ErrInvalidAddressOrKey)
return return
} }
@ -455,7 +482,7 @@ func GetAddressBalance(frontend chan []byte, icmd btcjson.Cmd) {
return return
} }
bal := a.CalculateAddressBalance(pkhash, int(cmd.Minconf)) bal := a.CalculateAddressBalance(apkh, int(cmd.Minconf))
ReplySuccess(frontend, cmd.Id(), bal) ReplySuccess(frontend, cmd.Id(), bal)
} }
@ -712,19 +739,20 @@ func ListAddressTransactions(frontend chan []byte, icmd btcjson.Cmd) {
return return
} }
// Parse hash160s out of addresses. // Decode addresses.
pkHashMap := make(map[string]struct{}) pkHashMap := make(map[string]struct{})
for _, addr := range cmd.Addresses { for _, addrStr := range cmd.Addresses {
pkHash, net, err := btcutil.DecodeAddress(addr) addr, err := btcutil.DecodeAddr(addrStr)
if err != nil || net != cfg.Net() { if err != nil {
e := &btcjson.Error{ ReplyError(frontend, cmd.Id(), &btcjson.ErrInvalidAddressOrKey)
Code: btcjson.ErrInvalidParams.Code,
Message: "invalid address",
}
ReplyError(frontend, cmd.Id(), e)
return return
} }
pkHashMap[string(pkHash)] = struct{}{} apkh, ok := addr.(*btcutil.AddressPubKeyHash)
if !ok || apkh.Net() != cfg.Net() {
ReplyError(frontend, cmd.Id(), &btcjson.ErrInvalidAddressOrKey)
return
}
pkHashMap[string(addr.ScriptAddress())] = struct{}{}
} }
txList, err := a.ListAddressTransactions(pkHashMap) txList, err := a.ListAddressTransactions(pkHashMap)
@ -863,7 +891,7 @@ func SendFrom(frontend chan []byte, icmd btcjson.Cmd) {
// If a change address was added, mark wallet as dirty, sync to disk, // If a change address was added, mark wallet as dirty, sync to disk,
// and request updates for change address. // and request updates for change address.
if len(createdTx.changeAddr) != 0 { if createdTx.changeAddr != nil {
a.dirty = true a.dirty = true
if err := a.writeDirtyToDisk(); err != nil { if err := a.writeDirtyToDisk(); err != nil {
log.Errorf("cannot write dirty wallet: %v", err) log.Errorf("cannot write dirty wallet: %v", err)
@ -955,7 +983,7 @@ func SendMany(frontend chan []byte, icmd btcjson.Cmd) {
// If a change address was added, mark wallet as dirty, sync to disk, // If a change address was added, mark wallet as dirty, sync to disk,
// and request updates for change address. // and request updates for change address.
if len(createdTx.changeAddr) != 0 { if createdTx.changeAddr != nil {
a.dirty = true a.dirty = true
if err := a.writeDirtyToDisk(); err != nil { if err := a.writeDirtyToDisk(); err != nil {
log.Errorf("cannot write dirty wallet: %v", err) log.Errorf("cannot write dirty wallet: %v", err)

View file

@ -71,7 +71,7 @@ type CreatedTx struct {
outputs []tx.Pair outputs []tx.Pair
btcspent int64 btcspent int64
fee int64 fee int64
changeAddr string changeAddr *btcutil.AddressPubKeyHash
changeUtxo *tx.Utxo changeUtxo *tx.Utxo
} }
@ -181,14 +181,14 @@ func (a *Account) txToPairs(pairs map[string]int64, minconf int) (*CreatedTx, er
outputs := make([]tx.Pair, 0, len(pairs)+1) outputs := make([]tx.Pair, 0, len(pairs)+1)
// Add outputs to new tx. // Add outputs to new tx.
for addr, amt := range pairs { for addrStr, amt := range pairs {
addr160, _, err := btcutil.DecodeAddress(addr) addr, err := btcutil.DecodeAddr(addrStr)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot decode address: %s", err) return nil, fmt.Errorf("cannot decode address: %s", err)
} }
// Spend amt to addr160 // Add output to spend amt to addr.
pkScript, err := btcscript.PayToPubKeyHashScript(addr160) pkScript, err := btcscript.PayToAddrScript(addr)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot create txout script: %s", err) return nil, fmt.Errorf("cannot create txout script: %s", err)
} }
@ -198,7 +198,7 @@ func (a *Account) txToPairs(pairs map[string]int64, minconf int) (*CreatedTx, er
// Create amount, address pair and add to outputs. // Create amount, address pair and add to outputs.
out := tx.Pair{ out := tx.Pair{
Amount: amt, Amount: amt,
PubkeyHash: addr160, PubkeyHash: addr.ScriptAddress(),
} }
outputs = append(outputs, out) outputs = append(outputs, out)
} }
@ -216,8 +216,7 @@ func (a *Account) txToPairs(pairs map[string]int64, minconf int) (*CreatedTx, er
// These are nil/zeroed until a change address is needed, and reused // These are nil/zeroed until a change address is needed, and reused
// again in case a change utxo has already been chosen. // again in case a change utxo has already been chosen.
var changeAddrHash []byte var changeAddr *btcutil.AddressPubKeyHash
var changeAddr string
var btcspent int64 var btcspent int64
var selectedInputs []*tx.Utxo var selectedInputs []*tx.Utxo
@ -245,23 +244,18 @@ func (a *Account) txToPairs(pairs map[string]int64, minconf int) (*CreatedTx, er
// Create a new address to spend leftover outputs to. // Create a new address to spend leftover outputs to.
// Get a new change address if one has not already been found. // Get a new change address if one has not already been found.
if changeAddrHash == nil { if changeAddr == nil {
changeAddr, err = a.NextChainedAddress(&bs) changeAddr, err = a.NextChainedAddress(&bs)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get next address: %s", err) return nil, fmt.Errorf("failed to get next address: %s", err)
} }
// Mark change address as belonging to this account. // Mark change address as belonging to this account.
MarkAddressForAccount(changeAddr, a.Name()) MarkAddressForAccount(changeAddr.EncodeAddress(), a.Name())
changeAddrHash, _, err = btcutil.DecodeAddress(changeAddr)
if err != nil {
return nil, fmt.Errorf("cannot decode new address: %s", err)
}
} }
// Spend change. // Spend change.
pkScript, err := btcscript.PayToPubKeyHashScript(changeAddrHash) pkScript, err := btcscript.PayToAddrScript(changeAddr)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot create txout script: %s", err) return nil, fmt.Errorf("cannot create txout script: %s", err)
} }
@ -278,7 +272,7 @@ func (a *Account) txToPairs(pairs map[string]int64, minconf int) (*CreatedTx, er
Height: -1, Height: -1,
Subscript: pkScript, Subscript: pkScript,
} }
copy(changeUtxo.AddrHash[:], changeAddrHash) copy(changeUtxo.AddrHash[:], changeAddr.ScriptAddress())
} }
// Selected unspent outputs become new transaction's inputs. // Selected unspent outputs become new transaction's inputs.
@ -286,18 +280,17 @@ func (a *Account) txToPairs(pairs map[string]int64, minconf int) (*CreatedTx, er
msgtx.AddTxIn(btcwire.NewTxIn((*btcwire.OutPoint)(&ip.Out), nil)) msgtx.AddTxIn(btcwire.NewTxIn((*btcwire.OutPoint)(&ip.Out), nil))
} }
for i, ip := range inputs { for i, ip := range inputs {
addrstr, err := btcutil.EncodeAddress(ip.AddrHash[:], // Error is ignored as the length and network checks can never fail
// for these inputs.
addr, _ := btcutil.NewAddressPubKeyHash(ip.AddrHash[:],
a.Wallet.Net()) a.Wallet.Net())
if err != nil { privkey, err := a.AddressKey(addr)
return nil, err
}
privkey, err := a.AddressKey(addrstr)
if err == wallet.ErrWalletLocked { if err == wallet.ErrWalletLocked {
return nil, wallet.ErrWalletLocked return nil, wallet.ErrWalletLocked
} else if err != nil { } else if err != nil {
return nil, fmt.Errorf("cannot get address key: %v", err) return nil, fmt.Errorf("cannot get address key: %v", err)
} }
ai, err := a.AddressInfo(addrstr) ai, err := a.AddressInfo(addr)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot get address info: %v", err) return nil, fmt.Errorf("cannot get address info: %v", err)
} }
@ -326,7 +319,7 @@ func (a *Account) txToPairs(pairs map[string]int64, minconf int) (*CreatedTx, er
// Add change to outputs. // Add change to outputs.
out := tx.Pair{ out := tx.Pair{
Amount: int64(change), Amount: int64(change),
PubkeyHash: changeAddrHash, PubkeyHash: changeAddr.ScriptAddress(),
Change: true, Change: true,
} }
outputs = append(outputs, out) outputs = append(outputs, out)

View file

@ -89,18 +89,13 @@ func TestFakeTxs(t *testing.T) {
t.Errorf("Cannot get next address: %s", err) t.Errorf("Cannot get next address: %s", err)
return return
} }
addr160, _, err := btcutil.DecodeAddress(addr) copy(utxo.AddrHash[:], addr.ScriptAddress())
if err != nil {
t.Errorf("Cannot decode address: %s", err)
return
}
copy(utxo.AddrHash[:], addr160)
ophash := (btcwire.ShaHash)([...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ophash := (btcwire.ShaHash)([...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32}) 28, 29, 30, 31, 32})
out := btcwire.NewOutPoint(&ophash, 0) out := btcwire.NewOutPoint(&ophash, 0)
utxo.Out = tx.OutPoint(*out) utxo.Out = tx.OutPoint(*out)
ss, err := btcscript.PayToPubKeyHashScript(addr160) ss, err := btcscript.PayToAddrScript(addr)
if err != nil { if err != nil {
t.Errorf("Could not create utxo PkScript: %s", err) t.Errorf("Could not create utxo PkScript: %s", err)
return return

View file

@ -1004,9 +1004,10 @@ func (tx *RecvTx) WriteTo(w io.Writer) (n int64, err error) {
func (tx *RecvTx) TxInfo(account string, curheight int32, func (tx *RecvTx) TxInfo(account string, curheight int32,
net btcwire.BitcoinNet) map[string]interface{} { net btcwire.BitcoinNet) map[string]interface{} {
address, err := btcutil.EncodeAddress(tx.ReceiverHash, net) address := "Unknown"
if err != nil { addr, err := btcutil.NewAddressPubKeyHash(tx.ReceiverHash, net)
address = "Unknown" if err == nil {
address = addr.String()
} }
txInfo := map[string]interface{}{ txInfo := map[string]interface{}{
@ -1137,10 +1138,10 @@ func (tx *SendTx) TxInfo(account string, curheight int32,
blockHashStr := blockHash.String() blockHashStr := blockHash.String()
for i, pair := range tx.Receivers { for i, pair := range tx.Receivers {
// EncodeAddress cannot error with these inputs. address := "Unknown"
address, err := btcutil.EncodeAddress(pair.PubkeyHash, net) addr, err := btcutil.NewAddressPubKeyHash(pair.PubkeyHash, net)
if err != nil { if err == nil {
address = "Unknown" address = addr.String()
} }
info := map[string]interface{}{ info := map[string]interface{}{
"account": account, "account": account,

View file

@ -32,7 +32,7 @@ import (
"github.com/conformal/btcec" "github.com/conformal/btcec"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
"hash" "github.com/davecgh/go-spew/spew"
"io" "io"
"math/big" "math/big"
"sync" "sync"
@ -111,27 +111,6 @@ func binaryWrite(w io.Writer, order binary.ByteOrder, data interface{}) (n int64
return int64(written), err return int64(written), err
} }
// Calculate the hash of hasher over buf.
func calcHash(buf []byte, hasher hash.Hash) []byte {
hasher.Write(buf)
return hasher.Sum(nil)
}
// calculate hash160 which is ripemd160(sha256(data))
func calcHash160(buf []byte) []byte {
return calcHash(calcHash(buf, sha256.New()), ripemd160.New())
}
// calculate hash256 which is sha256(sha256(data))
func calcHash256(buf []byte) []byte {
return calcHash(calcHash(buf, sha256.New()), sha256.New())
}
// calculate sha512(data)
func calcSha512(buf []byte) []byte {
return calcHash(buf, sha512.New())
}
// pubkeyFromPrivkey creates an encoded pubkey based on a // pubkeyFromPrivkey creates an encoded pubkey based on a
// 32-byte privkey. The returned pubkey is 33 bytes if compressed, // 32-byte privkey. The returned pubkey is 33 bytes if compressed,
// or 65 bytes if uncompressed. // or 65 bytes if uncompressed.
@ -154,11 +133,11 @@ func keyOneIter(passphrase, salt []byte, memReqts uint64) []byte {
lutbl := make([]byte, memReqts) lutbl := make([]byte, memReqts)
// Seed for lookup table // Seed for lookup table
seed := calcSha512(saltedpass) seed := sha512.Sum512(saltedpass)
copy(lutbl[:sha512.Size], seed) copy(lutbl[:sha512.Size], seed[:])
for nByte := 0; nByte < (int(memReqts) - sha512.Size); nByte += sha512.Size { for nByte := 0; nByte < (int(memReqts) - sha512.Size); nByte += sha512.Size {
hash := calcSha512(lutbl[nByte : nByte+sha512.Size]) hash := sha512.Sum512(lutbl[nByte : nByte+sha512.Size])
copy(lutbl[nByte+sha512.Size:nByte+2*sha512.Size], hash[:]) copy(lutbl[nByte+sha512.Size:nByte+2*sha512.Size], hash[:])
} }
@ -180,7 +159,7 @@ func keyOneIter(passphrase, salt []byte, memReqts uint64) []byte {
} }
// Save new hash to x // Save new hash to x
hash := calcSha512(x) hash := sha512.Sum512(x)
copy(x, hash[:]) copy(x, hash[:])
} }
@ -230,7 +209,7 @@ func ChainedPrivKey(privkey, pubkey, chaincode []byte) ([]byte, error) {
} }
xorbytes := make([]byte, 32) xorbytes := make([]byte, 32)
chainMod := calcHash256(pubkey) chainMod := sha256.Sum256(pubkey)
for i := range xorbytes { for i := range xorbytes {
xorbytes[i] = chainMod[i] ^ chaincode[i] xorbytes[i] = chainMod[i] ^ chaincode[i]
} }
@ -450,7 +429,6 @@ func (v *varEntries) ReadFrom(r io.Reader) (n int64, err error) {
} }
} }
type addressHashKey string
type transactionHashKey string type transactionHashKey string
type comment []byte type comment []byte
@ -472,8 +450,8 @@ type Wallet struct {
// root address and the appended entries. // root address and the appended entries.
recent recentBlocks recent recentBlocks
addrMap map[addressHashKey]*btcAddress addrMap map[btcutil.AddressPubKeyHash]*btcAddress
addrCommentMap map[addressHashKey]comment addrCommentMap map[btcutil.AddressPubKeyHash]comment
txCommentMap map[transactionHashKey]comment txCommentMap map[transactionHashKey]comment
// These are not serialized. // These are not serialized.
@ -481,7 +459,7 @@ type Wallet struct {
sync.Mutex sync.Mutex
key []byte key []byte
} }
chainIdxMap map[int64]addressHashKey chainIdxMap map[int64]*btcutil.AddressPubKeyHash
importedAddrs []*btcAddress importedAddrs []*btcAddress
lastChainIdx int64 lastChainIdx int64
} }
@ -490,9 +468,7 @@ type Wallet struct {
// desc's binary representation must not exceed 32 and 256 bytes, // desc's binary representation must not exceed 32 and 256 bytes,
// respectively. All address private keys are encrypted with passphrase. // respectively. All address private keys are encrypted with passphrase.
// The wallet is returned unlocked. // The wallet is returned unlocked.
func NewWallet(name, desc string, passphrase []byte, net btcwire.BitcoinNet, func NewWallet(name, desc string, passphrase []byte, net btcwire.BitcoinNet, createdAt *BlockStamp) (*Wallet, error) {
createdAt *BlockStamp) (*Wallet, error) {
// Check sizes of inputs. // Check sizes of inputs.
if len([]byte(name)) > 32 { if len([]byte(name)) > 32 {
return nil, errors.New("name exceeds 32 byte maximum size") return nil, errors.New("name exceeds 32 byte maximum size")
@ -501,6 +477,11 @@ func NewWallet(name, desc string, passphrase []byte, net btcwire.BitcoinNet,
return nil, errors.New("desc exceeds 256 byte maximum size") return nil, errors.New("desc exceeds 256 byte maximum size")
} }
// Check for a valid network.
if !(net == btcwire.MainNet || net == btcwire.TestNet3) {
return nil, errors.New("wallets must use mainnet or testnet3")
}
// Randomly-generate rootkey and chaincode. // Randomly-generate rootkey and chaincode.
rootkey, chaincode := make([]byte, 32), make([]byte, 32) rootkey, chaincode := make([]byte, 32), make([]byte, 32)
if _, err := rand.Read(rootkey); err != nil { if _, err := rand.Read(rootkey); err != nil {
@ -550,18 +531,18 @@ func NewWallet(name, desc string, passphrase []byte, net btcwire.BitcoinNet,
&createdAt.Hash, &createdAt.Hash,
}, },
}, },
addrMap: make(map[addressHashKey]*btcAddress), addrMap: make(map[btcutil.AddressPubKeyHash]*btcAddress),
addrCommentMap: make(map[addressHashKey]comment), addrCommentMap: make(map[btcutil.AddressPubKeyHash]comment),
txCommentMap: make(map[transactionHashKey]comment), txCommentMap: make(map[transactionHashKey]comment),
chainIdxMap: make(map[int64]addressHashKey), chainIdxMap: make(map[int64]*btcutil.AddressPubKeyHash),
lastChainIdx: rootKeyChainIdx, lastChainIdx: rootKeyChainIdx,
} }
copy(w.name[:], []byte(name)) copy(w.name[:], []byte(name))
copy(w.desc[:], []byte(desc)) copy(w.desc[:], []byte(desc))
// Add root address to maps. // Add root address to maps.
w.addrMap[addressHashKey(w.keyGenerator.pubKeyHash[:])] = &w.keyGenerator w.addrMap[*w.keyGenerator.address(net)] = &w.keyGenerator
w.chainIdxMap[rootKeyChainIdx] = addressHashKey(w.keyGenerator.pubKeyHash[:]) w.chainIdxMap[rootKeyChainIdx] = w.keyGenerator.address(net)
// Fill keypool. // Fill keypool.
if err := w.extendKeypool(nKeypoolIncrement, aeskey, createdAt); err != nil { if err := w.extendKeypool(nKeypoolIncrement, aeskey, createdAt); err != nil {
@ -589,9 +570,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[addressHashKey]*btcAddress) w.addrMap = make(map[btcutil.AddressPubKeyHash]*btcAddress)
w.addrCommentMap = make(map[addressHashKey]comment) w.addrCommentMap = make(map[btcutil.AddressPubKeyHash]comment)
w.chainIdxMap = make(map[int64]addressHashKey) w.chainIdxMap = make(map[int64]*btcutil.AddressPubKeyHash)
w.txCommentMap = make(map[transactionHashKey]comment) w.txCommentMap = make(map[transactionHashKey]comment)
var id [8]byte var id [8]byte
@ -640,29 +621,29 @@ func (w *Wallet) ReadFrom(r io.Reader) (n int64, err error) {
} }
// Add root address to address map. // Add root address to address map.
rootAddrKey := addressHashKey(w.keyGenerator.pubKeyHash[:]) rootAddr := w.keyGenerator.address(w.net)
w.addrMap[rootAddrKey] = &w.keyGenerator w.addrMap[*rootAddr] = &w.keyGenerator
w.chainIdxMap[rootKeyChainIdx] = rootAddrKey w.chainIdxMap[rootKeyChainIdx] = rootAddr
// Fill unserializied fields. // Fill unserializied fields.
wts := ([]io.WriterTo)(appendedEntries) wts := ([]io.WriterTo)(appendedEntries)
for _, wt := range wts { for _, wt := range wts {
switch e := wt.(type) { switch e := wt.(type) {
case *addrEntry: case *addrEntry:
addrKey := addressHashKey(e.pubKeyHash160[:]) addr := e.addr.address(w.net)
w.addrMap[addrKey] = &e.addr w.addrMap[*addr] = &e.addr
if e.addr.chainIndex == importedKeyChainIdx { if e.addr.chainIndex == importedKeyChainIdx {
w.importedAddrs = append(w.importedAddrs, &e.addr) w.importedAddrs = append(w.importedAddrs, &e.addr)
} else { } else {
w.chainIdxMap[e.addr.chainIndex] = addrKey w.chainIdxMap[e.addr.chainIndex] = addr
if w.lastChainIdx < e.addr.chainIndex { if w.lastChainIdx < e.addr.chainIndex {
w.lastChainIdx = e.addr.chainIndex w.lastChainIdx = e.addr.chainIndex
} }
} }
case *addrCommentEntry: case *addrCommentEntry:
addrKey := addressHashKey(e.pubKeyHash160[:]) addr := e.address(w.net)
w.addrCommentMap[addrKey] = comment(e.comment) w.addrCommentMap[*addr] = comment(e.comment)
case *txCommentEntry: case *txCommentEntry:
txKey := transactionHashKey(e.txHash[:]) txKey := transactionHashKey(e.txHash[:])
@ -682,26 +663,26 @@ 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 hash, addr := range w.addrMap { for addr, btcAddr := range w.addrMap {
e := &addrEntry{ e := &addrEntry{
addr: *addr, addr: *btcAddr,
} }
copy(e.pubKeyHash160[:], []byte(hash)) copy(e.pubKeyHash160[:], addr.ScriptAddress())
if addr.chainIndex >= 0 { 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[addr.chainIndex] = e chainedAddrs[btcAddr.chainIndex] = e
} else if addr.chainIndex == importedKeyChainIdx { } else if btcAddr.chainIndex == importedKeyChainIdx {
// No order for imported addresses. // No order for imported addresses.
importedAddrs = append(importedAddrs, e) importedAddrs = append(importedAddrs, e)
} }
} }
wts = append(chainedAddrs, importedAddrs...) wts = append(chainedAddrs, importedAddrs...)
for hash, comment := range w.addrCommentMap { for addr, comment := range w.addrCommentMap {
e := &addrCommentEntry{ e := &addrCommentEntry{
comment: []byte(comment), comment: []byte(comment),
} }
copy(e.pubKeyHash160[:], []byte(hash)) copy(e.pubKeyHash160[:], addr.ScriptAddress())
wts = append(wts, e) wts = append(wts, e)
} }
for hash, comment := range w.txCommentMap { for hash, comment := range w.txCommentMap {
@ -814,7 +795,7 @@ func (w *Wallet) Version() (string, int) {
// NextChainedAddress attempts to get the next chained address, // NextChainedAddress attempts to get the next chained address,
// refilling the keypool if necessary. // refilling the keypool if necessary.
func (w *Wallet) NextChainedAddress(bs *BlockStamp) (string, error) { func (w *Wallet) NextChainedAddress(bs *BlockStamp) (*btcutil.AddressPubKeyHash, 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 {
@ -823,61 +804,50 @@ func (w *Wallet) NextChainedAddress(bs *BlockStamp) (string, error) {
w.secret.Lock() w.secret.Lock()
if len(w.secret.key) != 32 { if len(w.secret.key) != 32 {
w.secret.Unlock() w.secret.Unlock()
return "", ErrWalletLocked return nil, ErrWalletLocked
} }
copy(aeskey, w.secret.key) copy(aeskey, w.secret.key)
w.secret.Unlock() w.secret.Unlock()
err := w.extendKeypool(nKeypoolIncrement, aeskey, bs) err := w.extendKeypool(nKeypoolIncrement, aeskey, bs)
if err != nil { if err != nil {
return "", err return nil, err
} }
next160, ok = w.chainIdxMap[w.highestUsed+1] next160, ok = w.chainIdxMap[w.highestUsed+1]
if !ok { if !ok {
return "", errors.New("chain index map inproperly updated") return nil, errors.New("chain index map inproperly updated")
} }
} }
// Look up address. // Look up address.
addr, ok := w.addrMap[next160] addr, ok := w.addrMap[*next160]
if !ok { if !ok {
return "", errors.New("cannot find generated address") return nil, errors.New("cannot find generated address")
} }
w.highestUsed++ 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.address(w.net), 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() (string, error) { func (w *Wallet) LastChainedAddress() btcutil.Address {
// Lookup pubkey hash for last used chained address. return w.chainIdxMap[w.highestUsed]
pkHash, ok := w.chainIdxMap[w.highestUsed]
if !ok {
return "", errors.New("chain index references unknown address")
}
// Lookup address with this pubkey hash.
addr, ok := w.addrMap[pkHash]
if !ok {
return "", errors.New("cannot find address by pubkey hash")
}
// Create and return payment address from serialized pubkey.
return addr.paymentAddress(w.net)
} }
// extendKeypool grows the keypool by n addresses. // extendKeypool grows the keypool by n addresses.
func (w *Wallet) extendKeypool(n uint, aeskey []byte, bs *BlockStamp) error { func (w *Wallet) extendKeypool(n uint, aeskey []byte, 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.
addrKey := w.chainIdxMap[w.lastChainIdx] a := w.chainIdxMap[w.lastChainIdx]
addr, ok := w.addrMap[addrKey] addr, ok := w.addrMap[*a]
if !ok { if !ok {
spew.Dump(a)
spew.Dump(w.addrMap)
return errors.New("expected last chained address not found") return errors.New("expected last chained address not found")
} }
privkey, err := addr.unlock(aeskey) privkey, err := addr.unlock(aeskey)
@ -903,10 +873,10 @@ func (w *Wallet) extendKeypool(n uint, aeskey []byte, bs *BlockStamp) error {
if err = newaddr.encrypt(aeskey); err != nil { if err = newaddr.encrypt(aeskey); err != nil {
return err return err
} }
addrKey := addressHashKey(newaddr.pubKeyHash[:]) a := newaddr.address(w.net)
w.addrMap[addrKey] = newaddr w.addrMap[*a] = newaddr
newaddr.chainIndex = addr.chainIndex + 1 newaddr.chainIndex = addr.chainIndex + 1
w.chainIdxMap[newaddr.chainIndex] = addrKey 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?
@ -917,40 +887,22 @@ func (w *Wallet) extendKeypool(n uint, aeskey []byte, bs *BlockStamp) error {
return nil return nil
} }
// addrHashForAddress decodes and returns the address hash for a
// payment address string, performing some basic sanity checking that it
// matches the Bitcoin network used by the wallet.
func (w *Wallet) addrHashForAddress(addr string) ([]byte, error) {
addr160, net, err := btcutil.DecodeAddress(addr)
if err != nil {
return nil, err
}
// Return error if address is for the wrong Bitcoin network.
switch {
case net == btcutil.MainNetAddr && w.net != btcwire.MainNet:
fallthrough
case net == btcutil.TestNetAddr && w.net != btcwire.TestNet:
return nil, ErrNetworkMismatch
}
return addr160, nil
}
// AddressKey returns the private key for a payment address stored // AddressKey returns the private key for a payment address stored
// in a wallet. This can fail if the payment address is for a different // in a wallet. This can fail if the payment address is for a different
// Bitcoin network than what this wallet uses, the address is not // Bitcoin network than what this wallet uses, the address is not
// contained in the wallet, the address does not include a public and // contained in the wallet, the address does not include a public and
// private key, or if the wallet is locked. // private key, or if the wallet is locked.
func (w *Wallet) AddressKey(addr string) (key *ecdsa.PrivateKey, err error) { func (w *Wallet) AddressKey(a btcutil.Address) (key *ecdsa.PrivateKey, err error) {
// Get address hash for payment address string. // Currently, only P2PKH addresses are supported. This should
addr160, err := w.addrHashForAddress(addr) // be extended to a switch-case statement when support for other
if err != nil { // addresses are added.
return nil, err 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[addressHashKey(addr160)] btcaddr, ok := w.addrMap[*addr]
if !ok { if !ok {
return nil, ErrAddressNotFound return nil, ErrAddressNotFound
} }
@ -996,17 +948,19 @@ func (w *Wallet) AddressKey(addr string) (key *ecdsa.PrivateKey, err error) {
} }
// AddressInfo returns an AddressInfo structure for an address in a wallet. // AddressInfo returns an AddressInfo structure for an address in a wallet.
func (w *Wallet) AddressInfo(addr string) (*AddressInfo, error) { func (w *Wallet) AddressInfo(a btcutil.Address) (*AddressInfo, error) {
// Get address hash for addr. // Currently, only P2PKH addresses are supported. This should
addr160, err := w.addrHashForAddress(addr) // be extended to a switch-case statement when support for other
if err != nil { // addresses are added.
return nil, err addr, ok := a.(*btcutil.AddressPubKeyHash)
if !ok {
return nil, errors.New("unsupported address")
} }
// Look up address by address hash. // Look up address by address hash.
btcaddr, ok := w.addrMap[addressHashKey(addr160)] btcaddr, ok := w.addrMap[*addr]
if !ok { if !ok {
return nil, errors.New("address not in wallet") return nil, ErrAddressNotFound
} }
return btcaddr.info(w.net) return btcaddr.info(w.net)
@ -1116,11 +1070,8 @@ 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. If the // user-provided private key and adds it to the wallet. If the
// import is successful, the payment address string is returned. // import is successful, the payment address string is returned.
func (w *Wallet) ImportPrivateKey(privkey []byte, compressed bool, func (w *Wallet) ImportPrivateKey(privkey []byte, compressed bool, bs *BlockStamp) (string, error) {
bs *BlockStamp) (string, error) { // The wallet's secret will be zeroed on lock, so make a local copy.
// The wallet's secret will be zeroed on lock, so make a local
// copy.
w.secret.Lock() w.secret.Lock()
if len(w.secret.key) != 32 { if len(w.secret.key) != 32 {
w.secret.Unlock() w.secret.Unlock()
@ -1131,32 +1082,28 @@ func (w *Wallet) ImportPrivateKey(privkey []byte, compressed bool,
w.secret.Unlock() w.secret.Unlock()
// Create new address with this private key. // Create new address with this private key.
addr, err := newBtcAddress(privkey, nil, bs, compressed) btcaddr, err := newBtcAddress(privkey, nil, bs, compressed)
if err != nil { if err != nil {
return "", err return "", err
} }
addr.chainIndex = importedKeyChainIdx btcaddr.chainIndex = importedKeyChainIdx
// Encrypt imported address with the derived AES key. // Encrypt imported address with the derived AES key.
if err = addr.encrypt(localSecret); err != nil { if err = btcaddr.encrypt(localSecret); err != nil {
return "", err
}
// Create payment address string. If this fails, return an error
// before adding the address to the wallet.
addr160 := addr.pubKeyHash[:]
addrstr, err := btcutil.EncodeAddress(addr160, w.Net())
if err != nil {
return "", err return "", err
} }
// 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[addressHashKey(addr160)] = addr w.addrMap[*btcaddr.address(w.net)] = btcaddr
w.importedAddrs = append(w.importedAddrs, addr) w.importedAddrs = append(w.importedAddrs, btcaddr)
return addrstr, nil // Create and return encoded payment address string. Error is
// ignored as the length of the pubkey hash and net will always
// be valid.
addr, _ := btcutil.NewAddressPubKeyHash(btcaddr.pubKeyHash[:], w.Net())
return addr.String(), nil
} }
// CreateDate returns the Unix time of the wallet creation time. This // CreateDate returns the Unix time of the wallet creation time. This
@ -1169,7 +1116,7 @@ func (w *Wallet) CreateDate() int64 {
// AddressInfo holds information regarding an address needed to manage // AddressInfo holds information regarding an address needed to manage
// a complete wallet. // a complete wallet.
type AddressInfo struct { type AddressInfo struct {
Address string btcutil.Address
AddrHash string AddrHash string
Compressed bool Compressed bool
FirstBlock int32 FirstBlock int32
@ -1185,12 +1132,8 @@ func (w *Wallet) SortedActiveAddresses() []*AddressInfo {
addrs := make([]*AddressInfo, 0, addrs := make([]*AddressInfo, 0,
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++ {
addr160, ok := w.chainIdxMap[i] a := w.chainIdxMap[i]
if !ok { info, err := w.addrMap[*a].info(w.Net())
return addrs
}
addr := w.addrMap[addr160]
info, err := addr.info(w.Net())
if err == nil { if err == nil {
addrs = append(addrs, info) addrs = append(addrs, info)
} }
@ -1207,15 +1150,11 @@ func (w *Wallet) SortedActiveAddresses() []*AddressInfo {
// ActiveAddresses returns a map between active payment addresses // ActiveAddresses returns a map between active payment addresses
// and their full info. These do not include unused addresses in the // and their full info. These do not include unused addresses in the
// key pool. If addresses must be sorted, use SortedActiveAddresses. // key pool. If addresses must be sorted, use SortedActiveAddresses.
func (w *Wallet) ActiveAddresses() map[string]*AddressInfo { func (w *Wallet) ActiveAddresses() map[btcutil.Address]*AddressInfo {
addrs := make(map[string]*AddressInfo) addrs := make(map[btcutil.Address]*AddressInfo)
for i := int64(rootKeyChainIdx); i <= w.highestUsed; i++ { for i := int64(rootKeyChainIdx); i <= w.highestUsed; i++ {
addr160, ok := w.chainIdxMap[i] a := w.chainIdxMap[i]
if !ok { info, err := w.addrMap[*a].info(w.Net())
return addrs
}
addr := w.addrMap[addr160]
info, err := addr.info(w.Net())
if err == nil { if err == nil {
addrs[info.Address] = info addrs[info.Address] = info
} }
@ -1676,7 +1615,7 @@ func newBtcAddress(privkey, iv []byte, bs *BlockStamp, compressed bool) (addr *b
addr.privKeyCT.key = privkey addr.privKeyCT.key = privkey
copy(addr.initVector[:], iv) copy(addr.initVector[:], iv)
addr.pubKey = pubkeyFromPrivkey(privkey, compressed) addr.pubKey = pubkeyFromPrivkey(privkey, compressed)
copy(addr.pubKeyHash[:], calcHash160(addr.pubKey)) copy(addr.pubKeyHash[:], btcutil.Hash160(addr.pubKey))
return addr, nil return addr, nil
} }
@ -1932,19 +1871,18 @@ func (a *btcAddress) changeEncryptionKey(oldkey, newkey []byte) error {
return errors.New("unimplemented") return errors.New("unimplemented")
} }
// paymentAddress returns a human readable payment address string for // address returns a btcutil.AddressPubKeyHash for a btcAddress.
// an address. func (a *btcAddress) address(net btcwire.BitcoinNet) *btcutil.AddressPubKeyHash {
func (a *btcAddress) paymentAddress(net btcwire.BitcoinNet) (string, error) { // error is not returned because the hash will always be 20
return btcutil.EncodeAddress(a.pubKeyHash[:], net) // bytes, and net is assumed to be valid.
addr, _ := btcutil.NewAddressPubKeyHash(a.pubKeyHash[:], net)
return addr
} }
// info returns information about a btcAddress stored in a AddressInfo // info returns information about a btcAddress stored in a AddressInfo
// struct. // struct.
func (a *btcAddress) info(net btcwire.BitcoinNet) (*AddressInfo, error) { func (a *btcAddress) info(net btcwire.BitcoinNet) (*AddressInfo, error) {
address, err := a.paymentAddress(net) address := a.address(net)
if err != nil {
return nil, err
}
return &AddressInfo{ return &AddressInfo{
Address: address, Address: address,
@ -2124,6 +2062,13 @@ type addrCommentEntry struct {
comment []byte comment []byte
} }
func (e *addrCommentEntry) address(net btcwire.BitcoinNet) *btcutil.AddressPubKeyHash {
// error is not returned because the hash will always be 20
// bytes, and net is assumed to be valid.
addr, _ := btcutil.NewAddressPubKeyHash(e.pubKeyHash160[:], net)
return addr
}
func (e *addrCommentEntry) WriteTo(w io.Writer) (n int64, err error) { func (e *addrCommentEntry) WriteTo(w io.Writer) (n int64, err error) {
var written int64 var written int64

View file

@ -90,6 +90,7 @@ func TestWalletCreationSerialization(t *testing.T) {
[]byte("banana"), btcwire.MainNet, createdAt) []byte("banana"), btcwire.MainNet, createdAt)
if err != nil { if err != nil {
t.Error("Error creating new wallet: " + err.Error()) t.Error("Error creating new wallet: " + err.Error())
return
} }
file, err := os.Create("newwallet.bin") file, err := os.Create("newwallet.bin")