Switch to new btcutil Address encoding/decoding API.
This commit is contained in:
parent
ac79a59c90
commit
e8265eca41
8 changed files with 223 additions and 254 deletions
77
account.go
77
account.go
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
64
cmdmgr.go
64
cmdmgr.go
|
@ -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)
|
||||||
|
|
41
createtx.go
41
createtx.go
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
15
tx/tx.go
15
tx/tx.go
|
@ -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,
|
||||||
|
|
267
wallet/wallet.go
267
wallet/wallet.go
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
Loading…
Reference in a new issue