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:
Owain G. Ainsworth 2014-03-06 00:34:44 +00:00
parent 34e4c0be35
commit df31e30839
5 changed files with 100 additions and 38 deletions

View file

@ -315,7 +315,7 @@ func (a *Account) DumpPrivKeys() ([]string, error) {
return nil, err
}
encKey, err := btcutil.EncodePrivateKey(key.D.Bytes(),
a.Wallet.Net(), info.Compressed)
a.Wallet.Net(), info.Compressed())
if err != nil {
return nil, err
}
@ -342,7 +342,8 @@ func (a *Account) DumpWIFPrivateKey(addr btcutil.Address) (string, error) {
}
// 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
@ -497,7 +498,7 @@ func (a *Account) SortedActivePaymentAddresses() []string {
addrs := make([]string, len(infos))
for i, info := range infos {
addrs[i] = info.Address.EncodeAddress()
addrs[i] = info.Address().EncodeAddress()
}
return addrs
@ -510,7 +511,7 @@ func (a *Account) ActivePaymentAddresses() map[string]struct{} {
addrs := make(map[string]struct{}, len(infos))
for _, info := range infos {
addrs[info.Address.EncodeAddress()] = struct{}{}
addrs[info.Address().EncodeAddress()] = struct{}{}
}
return addrs
@ -595,7 +596,7 @@ func (a *Account) RecoverAddresses(n int) error {
m[addrs[i].EncodeAddress()] = struct{}{}
}
go func(addrs map[string]struct{}) {
jsonErr := Rescan(CurrentServerConn(), lastInfo.FirstBlock, addrs)
jsonErr := Rescan(CurrentServerConn(), lastInfo.FirstBlock(), addrs)
if jsonErr != nil {
log.Errorf("Rescanning for recovered addresses failed: %v",
jsonErr.Message)

View file

@ -257,7 +257,7 @@ func (a *Account) txToPairs(pairs map[string]int64, minconf int) (*CreatedTx, er
sigscript, err := btcscript.SignatureScript(msgtx, i,
input.PkScript(), btcscript.SigHashAll, privkey,
ai.Compressed)
ai.Compressed())
if err != nil {
return nil, fmt.Errorf("cannot create sigscript: %s", err)
}

View file

@ -1493,7 +1493,7 @@ func SignMessage(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
fullmsg := "Bitcoin Signed Message:\n" + cmd.Message
sigbytes, err := btcec.SignCompact(btcec.S256(), privkey,
btcwire.DoubleSha256([]byte(fullmsg)), ainfo.Compressed)
btcwire.DoubleSha256([]byte(fullmsg)), ainfo.Compressed())
if err != nil {
return nil, &btcjson.Error{
Code: btcjson.ErrWallet.Code,
@ -1600,13 +1600,18 @@ func ValidateAddress(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
result["ismine"] = true
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
// we will need to check here and only provide the script,
// hexsript and list of addresses.
// if scripthash, the pubkey if pubkey/pubkeyhash, etc.
// for now we only support p2pkh so is irrelavent
result["compressed"] = ainfo.Compressed
result["pubkey"] = ainfo.Pubkey
} else {
result["ismine"] = false
}
@ -1681,7 +1686,7 @@ func VerifyMessage(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
// Return boolean if keys match.
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

View file

@ -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.
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
// be extended to a switch-case statement when support for other
// addresses are added.
@ -1404,24 +1404,77 @@ func (w *Wallet) ExportWatchingWallet() (*Wallet, error) {
return ww, nil
}
// AddressInfo holds information regarding an address needed to manage
// a complete wallet.
type AddressInfo struct {
btcutil.Address
AddrHash string
Compressed bool
FirstBlock int32
Imported bool
// AddressInfo is an interface that provides acces to information regarding an
// address managed by a wallet. Concrete implementations of this type may
// provide further fields to provide information specific to that type of
// address.
type AddressInfo interface {
// Address returns a btcutil.Address for the backing address.
Address() btcutil.Address
// 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
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
// requested to be generated. These do not include unused addresses in
// the key pool. Use this when ordered addresses are needed. Otherwise,
// ActiveAddresses is preferred.
func (w *Wallet) SortedActiveAddresses() []*AddressInfo {
addrs := make([]*AddressInfo, 0,
func (w *Wallet) SortedActiveAddresses() []AddressInfo {
addrs := make([]AddressInfo, 0,
w.highestUsed+int64(len(w.importedAddrs))+1)
for i := int64(rootKeyChainIdx); i <= w.highestUsed; i++ {
a := w.chainIdxMap[i]
@ -1442,19 +1495,19 @@ func (w *Wallet) SortedActiveAddresses() []*AddressInfo {
// ActiveAddresses returns a map between active payment addresses
// and their full info. These do not include unused addresses in the
// key pool. If addresses must be sorted, use SortedActiveAddresses.
func (w *Wallet) ActiveAddresses() map[btcutil.Address]*AddressInfo {
addrs := make(map[btcutil.Address]*AddressInfo)
func (w *Wallet) ActiveAddresses() map[btcutil.Address]AddressInfo {
addrs := make(map[btcutil.Address]AddressInfo)
for i := int64(rootKeyChainIdx); i <= w.highestUsed; i++ {
a := w.chainIdxMap[i]
info, err := w.addrMap[*a].info(w.Net())
if err == nil {
addrs[info.Address] = info
addrs[info.Address()] = info
}
}
for _, addr := range w.importedAddrs {
info, err := addr.info(w.Net())
if err == nil {
addrs[info.Address] = info
addrs[info.Address()] = info
}
}
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
// struct.
func (a *btcAddress) info(net btcwire.BitcoinNet) (*AddressInfo, error) {
func (a *btcAddress) info(net btcwire.BitcoinNet) (AddressInfo, error) {
address := a.address(net)
return &AddressInfo{
Address: address,
AddrHash: string(a.pubKeyHash[:]),
Compressed: a.flags.compressed,
FirstBlock: a.firstBlock,
Imported: a.chainIndex == importedKeyChainIdx,
return &AddressPubKeyInfo{
address: address,
addrHash: string(a.pubKeyHash[:]),
compressed: a.flags.compressed,
firstBlock: a.firstBlock,
imported: a.chainIndex == importedKeyChainIdx,
Pubkey: hex.EncodeToString(a.pubKey),
Change: a.flags.change,
change: a.flags.change,
}, nil
}

View file

@ -347,12 +347,14 @@ func TestWalletPubkeyChaining(t *testing.T) {
t.Errorf("Failed to get info about address without private key: %v", err)
return
}
pkinfo := info.(*AddressPubKeyInfo)
// sanity checks
if !info.Compressed {
if !info.Compressed() {
t.Errorf("Pubkey should be compressed.")
return
}
if info.Imported {
if info.Imported() {
t.Errorf("Should not be marked as imported.")
return
}
@ -419,7 +421,7 @@ func TestWalletPubkeyChaining(t *testing.T) {
t.Errorf("Unable to sign hash with the created private key: %v", err)
return
}
pubKeyStr, _ := hex.DecodeString(info.Pubkey)
pubKeyStr, _ := hex.DecodeString(pkinfo.Pubkey)
pubKey, err := btcec.ParsePubKey(pubKeyStr, btcec.S256())
ok := ecdsa.Verify(pubKey, hash, r, s)
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)
return
}
nextPkInfo := nextInfo.(*AddressPubKeyInfo)
nextKey, err := w.AddressKey(nextAddr)
if err != nil {
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)
return
}
pubKeyStr, _ = hex.DecodeString(nextInfo.Pubkey)
pubKeyStr, _ = hex.DecodeString(nextPkInfo.Pubkey)
pubKey, err = btcec.ParsePubKey(pubKeyStr, btcec.S256())
ok = ecdsa.Verify(pubKey, hash, r, s)
if !ok {