Make AddressInfo an interface.
Shortly we will add new types of address, so make AddressInfo an interface, with concrete types providing address-specific information. Adapt existing code to this new status quo.
This commit is contained in:
parent
34e4c0be35
commit
df31e30839
5 changed files with 100 additions and 38 deletions
11
account.go
11
account.go
|
@ -315,7 +315,7 @@ func (a *Account) DumpPrivKeys() ([]string, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
encKey, err := btcutil.EncodePrivateKey(key.D.Bytes(),
|
encKey, err := btcutil.EncodePrivateKey(key.D.Bytes(),
|
||||||
a.Wallet.Net(), info.Compressed)
|
a.Wallet.Net(), info.Compressed())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -342,7 +342,8 @@ func (a *Account) DumpWIFPrivateKey(addr btcutil.Address) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return WIF-encoding of the private key.
|
// Return WIF-encoding of the private key.
|
||||||
return btcutil.EncodePrivateKey(key.D.Bytes(), a.Net(), info.Compressed)
|
return btcutil.EncodePrivateKey(key.D.Bytes(), a.Net(),
|
||||||
|
info.Compressed())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImportPrivateKey imports a private key to the account's wallet and
|
// ImportPrivateKey imports a private key to the account's wallet and
|
||||||
|
@ -497,7 +498,7 @@ func (a *Account) SortedActivePaymentAddresses() []string {
|
||||||
|
|
||||||
addrs := make([]string, len(infos))
|
addrs := make([]string, len(infos))
|
||||||
for i, info := range infos {
|
for i, info := range infos {
|
||||||
addrs[i] = info.Address.EncodeAddress()
|
addrs[i] = info.Address().EncodeAddress()
|
||||||
}
|
}
|
||||||
|
|
||||||
return addrs
|
return addrs
|
||||||
|
@ -510,7 +511,7 @@ 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.EncodeAddress()] = struct{}{}
|
addrs[info.Address().EncodeAddress()] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return addrs
|
return addrs
|
||||||
|
@ -595,7 +596,7 @@ func (a *Account) RecoverAddresses(n int) error {
|
||||||
m[addrs[i].EncodeAddress()] = struct{}{}
|
m[addrs[i].EncodeAddress()] = struct{}{}
|
||||||
}
|
}
|
||||||
go func(addrs map[string]struct{}) {
|
go func(addrs map[string]struct{}) {
|
||||||
jsonErr := Rescan(CurrentServerConn(), lastInfo.FirstBlock, addrs)
|
jsonErr := Rescan(CurrentServerConn(), lastInfo.FirstBlock(), addrs)
|
||||||
if jsonErr != nil {
|
if jsonErr != nil {
|
||||||
log.Errorf("Rescanning for recovered addresses failed: %v",
|
log.Errorf("Rescanning for recovered addresses failed: %v",
|
||||||
jsonErr.Message)
|
jsonErr.Message)
|
||||||
|
|
|
@ -257,7 +257,7 @@ func (a *Account) txToPairs(pairs map[string]int64, minconf int) (*CreatedTx, er
|
||||||
|
|
||||||
sigscript, err := btcscript.SignatureScript(msgtx, i,
|
sigscript, err := btcscript.SignatureScript(msgtx, i,
|
||||||
input.PkScript(), btcscript.SigHashAll, privkey,
|
input.PkScript(), btcscript.SigHashAll, privkey,
|
||||||
ai.Compressed)
|
ai.Compressed())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot create sigscript: %s", err)
|
return nil, fmt.Errorf("cannot create sigscript: %s", err)
|
||||||
}
|
}
|
||||||
|
|
13
rpcserver.go
13
rpcserver.go
|
@ -1493,7 +1493,7 @@ func SignMessage(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
|
||||||
|
|
||||||
fullmsg := "Bitcoin Signed Message:\n" + cmd.Message
|
fullmsg := "Bitcoin Signed Message:\n" + cmd.Message
|
||||||
sigbytes, err := btcec.SignCompact(btcec.S256(), privkey,
|
sigbytes, err := btcec.SignCompact(btcec.S256(), privkey,
|
||||||
btcwire.DoubleSha256([]byte(fullmsg)), ainfo.Compressed)
|
btcwire.DoubleSha256([]byte(fullmsg)), ainfo.Compressed())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &btcjson.Error{
|
return nil, &btcjson.Error{
|
||||||
Code: btcjson.ErrWallet.Code,
|
Code: btcjson.ErrWallet.Code,
|
||||||
|
@ -1600,13 +1600,18 @@ func ValidateAddress(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
|
||||||
result["ismine"] = true
|
result["ismine"] = true
|
||||||
result["account"] = account
|
result["account"] = account
|
||||||
|
|
||||||
|
switch info := ainfo.(type) {
|
||||||
|
case *wallet.AddressPubKeyInfo:
|
||||||
|
result["compressed"] = info.Compressed()
|
||||||
|
result["pubkey"] = info.Pubkey
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(oga) when we handle different types of addresses then
|
// TODO(oga) when we handle different types of addresses then
|
||||||
// we will need to check here and only provide the script,
|
// we will need to check here and only provide the script,
|
||||||
// hexsript and list of addresses.
|
// hexsript and list of addresses.
|
||||||
// if scripthash, the pubkey if pubkey/pubkeyhash, etc.
|
// if scripthash, the pubkey if pubkey/pubkeyhash, etc.
|
||||||
// for now we only support p2pkh so is irrelavent
|
// for now we only support p2pkh so is irrelavent
|
||||||
result["compressed"] = ainfo.Compressed
|
|
||||||
result["pubkey"] = ainfo.Pubkey
|
|
||||||
} else {
|
} else {
|
||||||
result["ismine"] = false
|
result["ismine"] = false
|
||||||
}
|
}
|
||||||
|
@ -1681,7 +1686,7 @@ func VerifyMessage(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
|
||||||
|
|
||||||
// Return boolean if keys match.
|
// Return boolean if keys match.
|
||||||
return (pk.X.Cmp(privkey.X) == 0 && pk.Y.Cmp(privkey.Y) == 0 &&
|
return (pk.X.Cmp(privkey.X) == 0 && pk.Y.Cmp(privkey.Y) == 0 &&
|
||||||
ainfo.Compressed == wasCompressed), nil
|
ainfo.Compressed() == wasCompressed), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WalletIsLocked handles the walletislocked extension request by
|
// WalletIsLocked handles the walletislocked extension request by
|
||||||
|
|
101
wallet/wallet.go
101
wallet/wallet.go
|
@ -1165,7 +1165,7 @@ func (w *Wallet) AddressKey(a btcutil.Address) (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(a btcutil.Address) (*AddressInfo, error) {
|
func (w *Wallet) AddressInfo(a btcutil.Address) (AddressInfo, error) {
|
||||||
// Currently, only P2PKH addresses are supported. This should
|
// Currently, only P2PKH addresses are supported. This should
|
||||||
// be extended to a switch-case statement when support for other
|
// be extended to a switch-case statement when support for other
|
||||||
// addresses are added.
|
// addresses are added.
|
||||||
|
@ -1404,24 +1404,77 @@ func (w *Wallet) ExportWatchingWallet() (*Wallet, error) {
|
||||||
return ww, nil
|
return ww, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddressInfo holds information regarding an address needed to manage
|
// AddressInfo is an interface that provides acces to information regarding an
|
||||||
// a complete wallet.
|
// address managed by a wallet. Concrete implementations of this type may
|
||||||
type AddressInfo struct {
|
// provide further fields to provide information specific to that type of
|
||||||
btcutil.Address
|
// address.
|
||||||
AddrHash string
|
type AddressInfo interface {
|
||||||
Compressed bool
|
// Address returns a btcutil.Address for the backing address.
|
||||||
FirstBlock int32
|
Address() btcutil.Address
|
||||||
Imported bool
|
// FirstBlock returns the first block an address could be in.
|
||||||
|
FirstBlock() int32
|
||||||
|
// Compressed returns true if the backing address was imported instead
|
||||||
|
// of being part of an address chain.
|
||||||
|
Imported() bool
|
||||||
|
// Compressed returns true if the backing address was created for a
|
||||||
|
// change output of a transaction.
|
||||||
|
Change() bool
|
||||||
|
// Compressed returns true if the backing address is compressed.
|
||||||
|
Compressed() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressPubKeyInfo implements AddressInfo and additionally provides the
|
||||||
|
// pubkey for a pubkey-based address.
|
||||||
|
type AddressPubKeyInfo struct {
|
||||||
|
address btcutil.Address
|
||||||
|
addrHash string
|
||||||
|
compressed bool
|
||||||
|
firstBlock int32
|
||||||
|
imported bool
|
||||||
Pubkey string
|
Pubkey string
|
||||||
Change bool
|
change bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address returns the pub key address, implementing AddressInfo.
|
||||||
|
func (ai *AddressPubKeyInfo) Address() btcutil.Address {
|
||||||
|
return ai.address
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddrHash returns the pub key hash, implementing AddressInfo.
|
||||||
|
func (ai *AddressPubKeyInfo) AddrHash() string {
|
||||||
|
return ai.addrHash
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstBlock returns the first block the address is seen in, implementing
|
||||||
|
// AddressInfo.
|
||||||
|
func (ai *AddressPubKeyInfo) FirstBlock() int32 {
|
||||||
|
return ai.firstBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
// Imported returns the pub if the address was imported, or a chained address,
|
||||||
|
// implementing AddressInfo.
|
||||||
|
func (ai *AddressPubKeyInfo) Imported() bool {
|
||||||
|
return ai.imported
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddrHash returns true if the address was created as a change address,
|
||||||
|
// implementing AddressInfo.
|
||||||
|
func (ai *AddressPubKeyInfo) Change() bool {
|
||||||
|
return ai.change
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddrHash returns true if the address backing key is compressed,
|
||||||
|
// implementing AddressInfo.
|
||||||
|
func (ai *AddressPubKeyInfo) Compressed() bool {
|
||||||
|
return ai.compressed
|
||||||
}
|
}
|
||||||
|
|
||||||
// SortedActiveAddresses returns all wallet addresses that have been
|
// SortedActiveAddresses returns all wallet addresses that have been
|
||||||
// requested to be generated. These do not include unused addresses in
|
// requested to be generated. These do not include unused addresses in
|
||||||
// the key pool. Use this when ordered addresses are needed. Otherwise,
|
// the key pool. Use this when ordered addresses are needed. Otherwise,
|
||||||
// ActiveAddresses is preferred.
|
// ActiveAddresses is preferred.
|
||||||
func (w *Wallet) SortedActiveAddresses() []*AddressInfo {
|
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++ {
|
||||||
a := w.chainIdxMap[i]
|
a := w.chainIdxMap[i]
|
||||||
|
@ -1442,19 +1495,19 @@ 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[btcutil.Address]*AddressInfo {
|
func (w *Wallet) ActiveAddresses() map[btcutil.Address]AddressInfo {
|
||||||
addrs := make(map[btcutil.Address]*AddressInfo)
|
addrs := make(map[btcutil.Address]AddressInfo)
|
||||||
for i := int64(rootKeyChainIdx); i <= w.highestUsed; i++ {
|
for i := int64(rootKeyChainIdx); i <= w.highestUsed; i++ {
|
||||||
a := w.chainIdxMap[i]
|
a := w.chainIdxMap[i]
|
||||||
info, err := w.addrMap[*a].info(w.Net())
|
info, err := w.addrMap[*a].info(w.Net())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
addrs[info.Address] = info
|
addrs[info.Address()] = info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, addr := range w.importedAddrs {
|
for _, addr := range w.importedAddrs {
|
||||||
info, err := addr.info(w.Net())
|
info, err := addr.info(w.Net())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
addrs[info.Address] = info
|
addrs[info.Address()] = info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return addrs
|
return addrs
|
||||||
|
@ -2273,17 +2326,17 @@ func (a *btcAddress) address(net btcwire.BitcoinNet) *btcutil.AddressPubKeyHash
|
||||||
|
|
||||||
// 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 := a.address(net)
|
address := a.address(net)
|
||||||
|
|
||||||
return &AddressInfo{
|
return &AddressPubKeyInfo{
|
||||||
Address: address,
|
address: address,
|
||||||
AddrHash: string(a.pubKeyHash[:]),
|
addrHash: string(a.pubKeyHash[:]),
|
||||||
Compressed: a.flags.compressed,
|
compressed: a.flags.compressed,
|
||||||
FirstBlock: a.firstBlock,
|
firstBlock: a.firstBlock,
|
||||||
Imported: a.chainIndex == importedKeyChainIdx,
|
imported: a.chainIndex == importedKeyChainIdx,
|
||||||
Pubkey: hex.EncodeToString(a.pubKey),
|
Pubkey: hex.EncodeToString(a.pubKey),
|
||||||
Change: a.flags.change,
|
change: a.flags.change,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -347,12 +347,14 @@ func TestWalletPubkeyChaining(t *testing.T) {
|
||||||
t.Errorf("Failed to get info about address without private key: %v", err)
|
t.Errorf("Failed to get info about address without private key: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pkinfo := info.(*AddressPubKeyInfo)
|
||||||
// sanity checks
|
// sanity checks
|
||||||
if !info.Compressed {
|
if !info.Compressed() {
|
||||||
t.Errorf("Pubkey should be compressed.")
|
t.Errorf("Pubkey should be compressed.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if info.Imported {
|
if info.Imported() {
|
||||||
t.Errorf("Should not be marked as imported.")
|
t.Errorf("Should not be marked as imported.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -419,7 +421,7 @@ func TestWalletPubkeyChaining(t *testing.T) {
|
||||||
t.Errorf("Unable to sign hash with the created private key: %v", err)
|
t.Errorf("Unable to sign hash with the created private key: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pubKeyStr, _ := hex.DecodeString(info.Pubkey)
|
pubKeyStr, _ := hex.DecodeString(pkinfo.Pubkey)
|
||||||
pubKey, err := btcec.ParsePubKey(pubKeyStr, btcec.S256())
|
pubKey, err := btcec.ParsePubKey(pubKeyStr, btcec.S256())
|
||||||
ok := ecdsa.Verify(pubKey, hash, r, s)
|
ok := ecdsa.Verify(pubKey, hash, r, s)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -442,6 +444,7 @@ func TestWalletPubkeyChaining(t *testing.T) {
|
||||||
t.Errorf("Couldn't get info about the next address in the chain: %v", err)
|
t.Errorf("Couldn't get info about the next address in the chain: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
nextPkInfo := nextInfo.(*AddressPubKeyInfo)
|
||||||
nextKey, err := w.AddressKey(nextAddr)
|
nextKey, err := w.AddressKey(nextAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Couldn't get private key for the next address in the chain: %v", err)
|
t.Errorf("Couldn't get private key for the next address in the chain: %v", err)
|
||||||
|
@ -455,7 +458,7 @@ func TestWalletPubkeyChaining(t *testing.T) {
|
||||||
t.Errorf("Unable to sign hash with the created private key: %v", err)
|
t.Errorf("Unable to sign hash with the created private key: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pubKeyStr, _ = hex.DecodeString(nextInfo.Pubkey)
|
pubKeyStr, _ = hex.DecodeString(nextPkInfo.Pubkey)
|
||||||
pubKey, err = btcec.ParsePubKey(pubKeyStr, btcec.S256())
|
pubKey, err = btcec.ParsePubKey(pubKeyStr, btcec.S256())
|
||||||
ok = ecdsa.Verify(pubKey, hash, r, s)
|
ok = ecdsa.Verify(pubKey, hash, r, s)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
Loading…
Reference in a new issue