diff --git a/address.go b/address.go index 5fbc7b7..e2cb06d 100644 --- a/address.go +++ b/address.go @@ -26,7 +26,15 @@ var ( // checkBitcoinNet returns an error if the bitcoin network is not supported. func checkBitcoinNet(net btcwire.BitcoinNet) error { // Check for a valid bitcoin network. - if !(net == btcwire.MainNet || net == btcwire.TestNet3) { + switch net { + case btcwire.MainNet: + fallthrough + case btcwire.TestNet: + fallthrough + case btcwire.TestNet3: + return nil + + default: return ErrUnknownNet } @@ -66,6 +74,10 @@ type Address interface { // ScriptAddress returns the raw bytes of the address to be used // when inserting the address into a txout's script. ScriptAddress() []byte + + // IsForNet returns whether or not the address is associated with the + // passed bitcoin network. + IsForNet(btcwire.BitcoinNet) bool } // DecodeAddr decodes the string encoding of an address and returns @@ -127,8 +139,8 @@ func DecodeAddr(addr string) (Address, error) { // AddressPubKeyHash is an Address for a pay-to-pubkey-hash (P2PKH) // transaction. type AddressPubKeyHash struct { - hash [ripemd160.Size]byte - net btcwire.BitcoinNet + hash [ripemd160.Size]byte + netID byte } // NewAddressPubKeyHash returns a new AddressPubKeyHash. pkHash must @@ -144,7 +156,20 @@ func NewAddressPubKeyHash(pkHash []byte, net btcwire.BitcoinNet) (*AddressPubKey return nil, err } - addr := &AddressPubKeyHash{net: net} + // Choose the appropriate network ID for the address based on the + // network. + var netID byte + switch net { + case btcwire.MainNet: + netID = MainNetAddr + + case btcwire.TestNet: + fallthrough + case btcwire.TestNet3: + netID = TestNetAddr + } + + addr := &AddressPubKeyHash{netID: netID} copy(addr.hash[:], pkHash) return addr, nil } @@ -152,15 +177,7 @@ func NewAddressPubKeyHash(pkHash []byte, net btcwire.BitcoinNet) (*AddressPubKey // EncodeAddress returns the string encoding of a pay-to-pubkey-hash // address. Part of the Address interface. func (a *AddressPubKeyHash) EncodeAddress() string { - var netID byte - switch a.net { - case btcwire.MainNet: - netID = MainNetAddr - case btcwire.TestNet3: - netID = TestNetAddr - } - - return encodeAddress(a.hash[:], netID) + return encodeAddress(a.hash[:], a.netID) } // ScriptAddress returns the bytes to be included in a txout script to pay @@ -169,10 +186,20 @@ func (a *AddressPubKeyHash) ScriptAddress() []byte { return a.hash[:] } -// Net returns the bitcoin network associated with the pay-to-pubkey-hash -// address. -func (a *AddressPubKeyHash) Net() btcwire.BitcoinNet { - return a.net +// IsForNet returns whether or not the pay-to-pubkey-hash address is associated +// with the passed bitcoin network. +func (a *AddressPubKeyHash) IsForNet(net btcwire.BitcoinNet) bool { + switch net { + case btcwire.MainNet: + return a.netID == MainNetAddr + + case btcwire.TestNet: + fallthrough + case btcwire.TestNet3: + return a.netID == TestNetAddr + } + + return false } // String returns a human-readable string for the pay-to-pubkey-hash address. @@ -185,8 +212,8 @@ func (a *AddressPubKeyHash) String() string { // AddressScriptHash is an Address for a pay-to-script-hash (P2SH) // transaction. type AddressScriptHash struct { - hash [ripemd160.Size]byte - net btcwire.BitcoinNet + hash [ripemd160.Size]byte + netID byte } // NewAddressScriptHash returns a new AddressScriptHash. net must be @@ -211,7 +238,20 @@ func NewAddressScriptHashFromHash(scriptHash []byte, net btcwire.BitcoinNet) (*A return nil, err } - addr := &AddressScriptHash{net: net} + // Choose the appropriate network ID for the address based on the + // network. + var netID byte + switch net { + case btcwire.MainNet: + netID = MainNetScriptHash + + case btcwire.TestNet: + fallthrough + case btcwire.TestNet3: + netID = TestNetScriptHash + } + + addr := &AddressScriptHash{netID: netID} copy(addr.hash[:], scriptHash) return addr, nil } @@ -219,15 +259,7 @@ func NewAddressScriptHashFromHash(scriptHash []byte, net btcwire.BitcoinNet) (*A // EncodeAddress returns the string encoding of a pay-to-script-hash // address. Part of the Address interface. func (a *AddressScriptHash) EncodeAddress() string { - var netID byte - switch a.net { - case btcwire.MainNet: - netID = MainNetScriptHash - case btcwire.TestNet3: - netID = TestNetScriptHash - } - - return encodeAddress(a.hash[:], netID) + return encodeAddress(a.hash[:], a.netID) } // ScriptAddress returns the bytes to be included in a txout script to pay @@ -236,10 +268,19 @@ func (a *AddressScriptHash) ScriptAddress() []byte { return a.hash[:] } -// Net returns the bitcoin network associated with the pay-to-script-hash -// address. -func (a *AddressScriptHash) Net() btcwire.BitcoinNet { - return a.net +// IsForNet returns whether or not the pay-to-script-hash address is associated +// with the passed bitcoin network. +func (a *AddressScriptHash) IsForNet(net btcwire.BitcoinNet) bool { + switch net { + case btcwire.MainNet: + return a.netID == MainNetScriptHash + case btcwire.TestNet: + fallthrough + case btcwire.TestNet3: + return a.netID == TestNetScriptHash + } + + return false } // String returns a human-readable string for the pay-to-script-hash address. @@ -270,7 +311,7 @@ const ( type AddressPubKey struct { pubKeyFormat PubKeyFormat pubKey *btcec.PublicKey - net btcwire.BitcoinNet + netID byte } // NewAddressPubKey returns a new AddressPubKey which represents a pay-to-pubkey @@ -300,9 +341,30 @@ func NewAddressPubKey(serializedPubKey []byte, net btcwire.BitcoinNet) (*Address pkFormat = PKFHybrid } + // Check for a valid bitcoin network. + if err := checkBitcoinNet(net); err != nil { + return nil, err + } + + // Choose the appropriate network ID for the address based on the + // network. + var netID byte + switch net { + case btcwire.MainNet: + netID = MainNetAddr + + case btcwire.TestNet: + fallthrough + case btcwire.TestNet3: + netID = TestNetAddr + } + ecPubKey := (*btcec.PublicKey)(pubKey) - addr := &AddressPubKey{pubKeyFormat: pkFormat, pubKey: ecPubKey, net: net} - return addr, nil + return &AddressPubKey{ + pubKeyFormat: pkFormat, + pubKey: ecPubKey, + netID: netID, + }, nil } // serialize returns the serialization of the public key according to the @@ -334,15 +396,7 @@ func (a *AddressPubKey) serialize() []byte { // // Part of the Address interface. func (a *AddressPubKey) EncodeAddress() string { - var netID byte - switch a.net { - case btcwire.MainNet: - netID = MainNetAddr - case btcwire.TestNet3: - netID = TestNetAddr - } - - return encodeAddress(Hash160(a.serialize()), netID) + return encodeAddress(Hash160(a.serialize()), a.netID) } // ScriptAddress returns the bytes to be included in a txout script to pay @@ -352,9 +406,20 @@ func (a *AddressPubKey) ScriptAddress() []byte { return a.serialize() } -// Net returns the bitcoin network associated with the pay-to-pubkey address. -func (a *AddressPubKey) Net() btcwire.BitcoinNet { - return a.net +// IsForNet returns whether or not the pay-to-pubkey address is associated +// with the passed bitcoin network. +func (a *AddressPubKey) IsForNet(net btcwire.BitcoinNet) bool { + switch net { + case btcwire.MainNet: + return a.netID == MainNetAddr + + case btcwire.TestNet: + fallthrough + case btcwire.TestNet3: + return a.netID == TestNetAddr + } + + return false } // String returns the hex-encoded human-readable string for the pay-to-pubkey @@ -382,8 +447,8 @@ func (a *AddressPubKey) SetFormat(pkFormat PubKeyFormat) { // differs with the format. At the time of this writing, most Bitcoin addresses // are pay-to-pubkey-hash constructed from the uncompressed public key. func (a *AddressPubKey) AddressPubKeyHash() *AddressPubKeyHash { - // All potential error conditions are already checked, so it's safe to - // ignore the error here. - addr, _ := NewAddressPubKeyHash(Hash160(a.serialize()), a.net) + addr := &AddressPubKeyHash{netID: a.netID} + copy(addr.hash[:], Hash160(a.serialize())) return addr + } diff --git a/address_test.go b/address_test.go index 4f7b732..acfb6ef 100644 --- a/address_test.go +++ b/address_test.go @@ -14,6 +14,9 @@ import ( "testing" ) +// invalidNet is an invalid bitcoin network. +const invalidNet = btcwire.BitcoinNet(0xffffffff) + func TestAddresses(t *testing.T) { tests := []struct { name string @@ -34,7 +37,7 @@ func TestAddresses(t *testing.T) { [ripemd160.Size]byte{ 0xe3, 0x4c, 0xce, 0x70, 0xc8, 0x63, 0x73, 0x27, 0x3e, 0xfc, 0xc5, 0x4c, 0xe7, 0xd2, 0xa4, 0x91, 0xbb, 0x4a, 0x0e, 0x84}, - btcwire.MainNet), + btcutil.MainNetAddr), f: func() (btcutil.Address, error) { pkHash := []byte{ 0xe3, 0x4c, 0xce, 0x70, 0xc8, 0x63, 0x73, 0x27, 0x3e, 0xfc, @@ -52,7 +55,7 @@ func TestAddresses(t *testing.T) { [ripemd160.Size]byte{ 0x0e, 0xf0, 0x30, 0x10, 0x7f, 0xd2, 0x6e, 0x0b, 0x6b, 0xf4, 0x05, 0x12, 0xbc, 0xa2, 0xce, 0xb1, 0xdd, 0x80, 0xad, 0xaa}, - btcwire.MainNet), + btcutil.MainNetAddr), f: func() (btcutil.Address, error) { pkHash := []byte{ 0x0e, 0xf0, 0x30, 0x10, 0x7f, 0xd2, 0x6e, 0x0b, 0x6b, 0xf4, @@ -70,7 +73,7 @@ func TestAddresses(t *testing.T) { [ripemd160.Size]byte{ 0x78, 0xb3, 0x16, 0xa0, 0x86, 0x47, 0xd5, 0xb7, 0x72, 0x83, 0xe5, 0x12, 0xd3, 0x60, 0x3f, 0x1f, 0x1c, 0x8d, 0xe6, 0x8f}, - btcwire.TestNet3), + btcutil.TestNetAddr), f: func() (btcutil.Address, error) { pkHash := []byte{ 0x78, 0xb3, 0x16, 0xa0, 0x86, 0x47, 0xd5, 0xb7, 0x72, 0x83, @@ -90,7 +93,7 @@ func TestAddresses(t *testing.T) { pkHash := []byte{ 0x78, 0xb3, 0x16, 0xa0, 0x86, 0x47, 0xd5, 0xb7, 0x72, 0x83, 0xe5, 0x12, 0xd3, 0x60, 0x3f, 0x1f, 0x1c, 0x8d, 0xe6, 0x8f} - return btcutil.NewAddressPubKeyHash(pkHash, btcwire.TestNet) + return btcutil.NewAddressPubKeyHash(pkHash, invalidNet) }, }, { @@ -126,7 +129,7 @@ func TestAddresses(t *testing.T) { [ripemd160.Size]byte{ 0xf8, 0x15, 0xb0, 0x36, 0xd9, 0xbb, 0xbc, 0xe5, 0xe9, 0xf2, 0xa0, 0x0a, 0xbd, 0x1b, 0xf3, 0xdc, 0x91, 0xe9, 0x55, 0x10}, - btcwire.MainNet), + btcutil.MainNetScriptHash), f: func() (btcutil.Address, error) { script := []byte{ 0x52, 0x41, 0x04, 0x91, 0xbb, 0xa2, 0x51, 0x09, 0x12, 0xa5, @@ -166,7 +169,7 @@ func TestAddresses(t *testing.T) { [ripemd160.Size]byte{ 0xe8, 0xc3, 0x00, 0xc8, 0x79, 0x86, 0xef, 0xa8, 0x4c, 0x37, 0xc0, 0x51, 0x99, 0x29, 0x01, 0x9e, 0xf8, 0x6e, 0xb5, 0xb4}, - btcwire.MainNet), + btcutil.MainNetScriptHash), f: func() (btcutil.Address, error) { hash := []byte{ 0xe8, 0xc3, 0x00, 0xc8, 0x79, 0x86, 0xef, 0xa8, 0x4c, 0x37, @@ -185,7 +188,7 @@ func TestAddresses(t *testing.T) { [ripemd160.Size]byte{ 0xc5, 0x79, 0x34, 0x2c, 0x2c, 0x4c, 0x92, 0x20, 0x20, 0x5e, 0x2c, 0xdc, 0x28, 0x56, 0x17, 0x04, 0x0c, 0x92, 0x4a, 0x0a}, - btcwire.TestNet3), + btcutil.TestNetScriptHash), f: func() (btcutil.Address, error) { hash := []byte{ 0xc5, 0x79, 0x34, 0x2c, 0x2c, 0x4c, 0x92, 0x20, 0x20, 0x5e, @@ -218,7 +221,7 @@ func TestAddresses(t *testing.T) { hash := []byte{ 0xc5, 0x79, 0x34, 0x2c, 0x2c, 0x4c, 0x92, 0x20, 0x20, 0x5e, 0x2c, 0xdc, 0x28, 0x56, 0x17, 0x04, 0x0c, 0x92, 0x4a, 0x0a} - return btcutil.NewAddressScriptHashFromHash(hash, btcwire.TestNet) + return btcutil.NewAddressScriptHashFromHash(hash, invalidNet) }, }, @@ -234,7 +237,7 @@ func TestAddresses(t *testing.T) { 0x69, 0xc2, 0xe7, 0x79, 0x01, 0x57, 0x3d, 0x8d, 0x79, 0x03, 0xc3, 0xeb, 0xec, 0x3a, 0x95, 0x77, 0x24, 0x89, 0x5d, 0xca, 0x52, 0xc6, 0xb4}, - btcutil.PKFCompressed, btcwire.MainNet), + btcutil.PKFCompressed, btcutil.MainNetAddr), f: func() (btcutil.Address, error) { serializedPubKey := []byte{ 0x02, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95, @@ -256,7 +259,7 @@ func TestAddresses(t *testing.T) { 0xe9, 0x86, 0xe8, 0x84, 0x18, 0x5c, 0x61, 0xcf, 0x43, 0xe0, 0x01, 0xf9, 0x13, 0x7f, 0x23, 0xc2, 0xc4, 0x09, 0x27, 0x3e, 0xb1, 0x6e, 0x65}, - btcutil.PKFCompressed, btcwire.MainNet), + btcutil.PKFCompressed, btcutil.MainNetAddr), f: func() (btcutil.Address, error) { serializedPubKey := []byte{ 0x03, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1, @@ -281,7 +284,7 @@ func TestAddresses(t *testing.T) { 0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 0xb4, 0x12, 0xa3}, - btcutil.PKFUncompressed, btcwire.MainNet), + btcutil.PKFUncompressed, btcutil.MainNetAddr), f: func() (btcutil.Address, error) { serializedPubKey := []byte{ 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, 0x6b, @@ -309,7 +312,7 @@ func TestAddresses(t *testing.T) { 0x96, 0x85, 0x26, 0x62, 0xce, 0x6a, 0x84, 0x7b, 0x19, 0x73, 0x76, 0x83, 0x01, 0x60, 0xc6, 0xd2, 0xeb, 0x5e, 0x6a, 0x4c, 0x44, 0xd3, 0x3f, 0x45, 0x3e}, - btcutil.PKFHybrid, btcwire.MainNet), + btcutil.PKFHybrid, btcutil.MainNetAddr), f: func() (btcutil.Address, error) { serializedPubKey := []byte{ 0x06, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95, @@ -337,7 +340,7 @@ func TestAddresses(t *testing.T) { 0x8a, 0x7e, 0xf8, 0xbd, 0x3b, 0x3c, 0xfb, 0x1e, 0xdb, 0x71, 0x17, 0xab, 0x65, 0x12, 0x9b, 0x8a, 0x2e, 0x68, 0x1f, 0x3c, 0x1e, 0x09, 0x08, 0xef, 0x7b}, - btcutil.PKFHybrid, btcwire.MainNet), + btcutil.PKFHybrid, btcutil.MainNetAddr), f: func() (btcutil.Address, error) { serializedPubKey := []byte{ 0x07, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1, @@ -362,7 +365,7 @@ func TestAddresses(t *testing.T) { 0x69, 0xc2, 0xe7, 0x79, 0x01, 0x57, 0x3d, 0x8d, 0x79, 0x03, 0xc3, 0xeb, 0xec, 0x3a, 0x95, 0x77, 0x24, 0x89, 0x5d, 0xca, 0x52, 0xc6, 0xb4}, - btcutil.PKFCompressed, btcwire.TestNet3), + btcutil.PKFCompressed, btcutil.TestNetAddr), f: func() (btcutil.Address, error) { serializedPubKey := []byte{ 0x02, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95, @@ -384,7 +387,7 @@ func TestAddresses(t *testing.T) { 0xe9, 0x86, 0xe8, 0x84, 0x18, 0x5c, 0x61, 0xcf, 0x43, 0xe0, 0x01, 0xf9, 0x13, 0x7f, 0x23, 0xc2, 0xc4, 0x09, 0x27, 0x3e, 0xb1, 0x6e, 0x65}, - btcutil.PKFCompressed, btcwire.TestNet3), + btcutil.PKFCompressed, btcutil.TestNetAddr), f: func() (btcutil.Address, error) { serializedPubKey := []byte{ 0x03, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1, @@ -409,7 +412,7 @@ func TestAddresses(t *testing.T) { 0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 0xb4, 0x12, 0xa3}, - btcutil.PKFUncompressed, btcwire.TestNet3), + btcutil.PKFUncompressed, btcutil.TestNetAddr), f: func() (btcutil.Address, error) { serializedPubKey := []byte{ 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, 0x6b, @@ -437,7 +440,7 @@ func TestAddresses(t *testing.T) { 0x96, 0x85, 0x26, 0x62, 0xce, 0x6a, 0x84, 0x7b, 0x19, 0x73, 0x76, 0x83, 0x01, 0x60, 0xc6, 0xd2, 0xeb, 0x5e, 0x6a, 0x4c, 0x44, 0xd3, 0x3f, 0x45, 0x3e}, - btcutil.PKFHybrid, btcwire.TestNet3), + btcutil.PKFHybrid, btcutil.TestNetAddr), f: func() (btcutil.Address, error) { serializedPubKey := []byte{ 0x06, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95, @@ -465,7 +468,7 @@ func TestAddresses(t *testing.T) { 0x8a, 0x7e, 0xf8, 0xbd, 0x3b, 0x3c, 0xfb, 0x1e, 0xdb, 0x71, 0x17, 0xab, 0x65, 0x12, 0x9b, 0x8a, 0x2e, 0x68, 0x1f, 0x3c, 0x1e, 0x09, 0x08, 0xef, 0x7b}, - btcutil.PKFHybrid, btcwire.TestNet3), + btcutil.PKFHybrid, btcutil.TestNetAddr), f: func() (btcutil.Address, error) { serializedPubKey := []byte{ 0x07, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1, @@ -514,30 +517,17 @@ func TestAddresses(t *testing.T) { // Perform type-specific calculations. var saddr []byte - var net btcwire.BitcoinNet switch d := decoded.(type) { case *btcutil.AddressPubKeyHash: saddr = btcutil.TstAddressSAddr(encoded) - // Net is not part of the Address interface and - // must be calculated here. - net = d.Net() - case *btcutil.AddressScriptHash: saddr = btcutil.TstAddressSAddr(encoded) - // Net is not part of the Address interface and - // must be calculated here. - net = d.Net() - case *btcutil.AddressPubKey: // Ignore the error here since the script // address is checked below. saddr, _ = hex.DecodeString(d.String()) - - // Net is not part of the Address interface and - // must be calculated here. - net = d.Net() } // Check script address. @@ -549,7 +539,7 @@ func TestAddresses(t *testing.T) { // Check networks. This check always succeeds for non-P2PKH and // non-P2SH addresses as both nets will be Go's default zero value. - if net != test.net { + if !decoded.IsForNet(test.net) { t.Errorf("%v: calculated network does not match expected", test.name) return diff --git a/internal_test.go b/internal_test.go index 71debcc..2a0937f 100644 --- a/internal_test.go +++ b/internal_test.go @@ -14,7 +14,6 @@ package btcutil import ( "code.google.com/p/go.crypto/ripemd160" "github.com/conformal/btcec" - "github.com/conformal/btcwire" ) // SetBlockBytes sets the internal serialized block byte buffer to the passed @@ -31,37 +30,37 @@ func TstAppDataDir(goos, appName string, roaming bool) string { } // TstAddressPubKeyHash makes an AddressPubKeyHash, setting the -// unexported fields with the parameters hash and net. +// unexported fields with the parameters hash and netID. func TstAddressPubKeyHash(hash [ripemd160.Size]byte, - net btcwire.BitcoinNet) *AddressPubKeyHash { + netID byte) *AddressPubKeyHash { return &AddressPubKeyHash{ - hash: hash, - net: net, + hash: hash, + netID: netID, } } // TstAddressScriptHash makes an AddressScriptHash, setting the -// unexported fields with the parameters hash and net. +// unexported fields with the parameters hash and netID. func TstAddressScriptHash(hash [ripemd160.Size]byte, - net btcwire.BitcoinNet) *AddressScriptHash { + netID byte) *AddressScriptHash { return &AddressScriptHash{ - hash: hash, - net: net, + hash: hash, + netID: netID, } } // TstAddressPubKey makes an AddressPubKey, setting the unexported fields with // the parameters. func TstAddressPubKey(serializedPubKey []byte, pubKeyFormat PubKeyFormat, - net btcwire.BitcoinNet) *AddressPubKey { + netID byte) *AddressPubKey { pubKey, _ := btcec.ParsePubKey(serializedPubKey, btcec.S256()) return &AddressPubKey{ pubKeyFormat: pubKeyFormat, pubKey: (*btcec.PublicKey)(pubKey), - net: net, + netID: netID, } }