Replace ScriptToAddress with ScriptToAddrHash to return an address hash.

This commit is contained in:
Josh Rickmar 2013-10-08 10:49:45 -04:00
parent 52a1488eaf
commit 83a19b239d
3 changed files with 60 additions and 68 deletions

View file

@ -4,11 +4,6 @@
package btcscript package btcscript
import (
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
)
// ScriptType is an enum type that represents the type of a script. It is // ScriptType is an enum type that represents the type of a script. It is
// returned from ScriptToAddress as part of the metadata about the script. // returned from ScriptToAddress as part of the metadata about the script.
// It implements the Stringer interface for nice printing. // It implements the Stringer interface for nice printing.
@ -62,8 +57,8 @@ const (
scrNoAddr scrNoAddr
) )
// ScriptToAddress extracts a payment address and the type out of a PkScript // ScriptToAddrHash extracts a 20-byte public key hash and the type out of a PkScript
func ScriptToAddress(script []byte) (ScriptType, string, error) { func ScriptToAddrHash(script []byte) (ScriptType, []byte, error) {
// Currently this only understands one form of PkScript // Currently this only understands one form of PkScript
validformats := []pkformat{ validformats := []pkformat{
{ScriptAddr, scrPayAddr, 25, []pkbytes{{0, OP_DUP}, {1, OP_HASH160}, {2, OP_DATA_20}, {23, OP_EQUALVERIFY}, {24, OP_CHECKSIG}}, true}, {ScriptAddr, scrPayAddr, 25, []pkbytes{{0, OP_DUP}, {1, OP_HASH160}, {2, OP_DATA_20}, {23, OP_EQUALVERIFY}, {24, OP_CHECKSIG}}, true},
@ -91,10 +86,10 @@ func ScriptToAddress(script []byte) (ScriptType, string, error) {
{ScriptStrange, scrNoAddr, 33, []pkbytes{{0, OP_DATA_32}}, false}, {ScriptStrange, scrNoAddr, 33, []pkbytes{{0, OP_DATA_32}}, false},
{ScriptStrange, scrNoAddr, 33, []pkbytes{{0, OP_HASH160}, {1, OP_DATA_20}, {22, OP_EQUAL}}, false}, {ScriptStrange, scrNoAddr, 33, []pkbytes{{0, OP_HASH160}, {1, OP_DATA_20}, {22, OP_EQUAL}}, false},
} }
return scriptToAddressTemplate(script, validformats) return scriptToAddrHashTemplate(script, validformats)
} }
func scriptToAddressTemplate(script []byte, validformats []pkformat) (ScriptType, string, error) { func scriptToAddrHashTemplate(script []byte, validformats []pkformat) (ScriptType, []byte, error) {
var format pkformat var format pkformat
var success bool var success bool
for _, format = range validformats { for _, format = range validformats {
@ -109,7 +104,7 @@ func scriptToAddressTemplate(script []byte, validformats []pkformat) (ScriptType
success = true success = true
for _, pkbyte := range format.databytes { for _, pkbyte := range format.databytes {
if pkbyte.off >= len(script) { if pkbyte.off >= len(script) {
return ScriptUnknown, "Unknown", return ScriptUnknown, nil,
StackErrInvalidAddrOffset StackErrInvalidAddrOffset
} }
if script[pkbyte.off] != pkbyte.val { if script[pkbyte.off] != pkbyte.val {
@ -129,64 +124,50 @@ func scriptToAddressTemplate(script []byte, validformats []pkformat) (ScriptType
if len(script) > 1 { if len(script) > 1 {
// check for a few special case // check for a few special case
if script[len(script)-1] == OP_CHECK_MULTISIG { if script[len(script)-1] == OP_CHECK_MULTISIG {
return ScriptStrange, "Unknown", nil return ScriptStrange, nil, nil
} }
if script[0] == OP_0 && (len(script) <= 75 && byte(len(script)) == script[1]+2) { if script[0] == OP_0 && (len(script) <= 75 && byte(len(script)) == script[1]+2) {
return ScriptStrange, "Unknown", nil return ScriptStrange, nil, nil
} }
if script[0] == OP_HASH160 && len(script) == 23 && script[22] == OP_EQUAL { if script[0] == OP_HASH160 && len(script) == 23 && script[22] == OP_EQUAL {
return ScriptStrange, "Unknown", nil return ScriptStrange, nil, nil
} }
if script[0] == OP_DATA_36 && len(script) == 37 { if script[0] == OP_DATA_36 && len(script) == 37 {
// Multisig ScriptSig // Multisig ScriptSig
return ScriptStrange, "Unknown", nil return ScriptStrange, nil, nil
} }
} }
return ScriptUnknown, "Unknown", StackErrUnknownAddress return ScriptUnknown, nil, StackErrUnknownAddress
} }
var atype byte var addrhash []byte
var abuf []byte
var addr string
switch format.parsetype { switch format.parsetype {
case scrPayAddr: case scrPayAddr:
atype = 0x00 addrhash = script[3:23]
abuf = script[3:23]
case scrCollectAddr: case scrCollectAddr:
// script is replaced with the md160 of the pubkey // script is replaced with the md160 of the pubkey
slen := len(script) slen := len(script)
pubkey := script[slen-65:] pubkey := script[slen-65:]
abuf = calcHash160(pubkey) addrhash = calcHash160(pubkey)
case scrCollectAddrComp: case scrCollectAddrComp:
// script is replaced with the md160 of the pubkey // script is replaced with the md160 of the pubkey
slen := len(script) slen := len(script)
pubkey := script[slen-33:] pubkey := script[slen-33:]
abuf = calcHash160(pubkey) addrhash = calcHash160(pubkey)
case scrGeneratePubkeyAddr: case scrGeneratePubkeyAddr:
atype = 0x00 // unable to determine address hash from script
addr = "Unknown"
case scrNoAddr: case scrNoAddr:
addr = "Unknown" // unable to determine address hash from script
case scrPubkeyAddr: case scrPubkeyAddr:
atype = 0x00
pubkey := script[1:66] pubkey := script[1:66]
abuf = calcHash160(pubkey) addrhash = calcHash160(pubkey)
case scrPubkeyAddrComp: case scrPubkeyAddrComp:
atype = 0x00
pubkey := script[1:34] pubkey := script[1:34]
abuf = calcHash160(pubkey) addrhash = calcHash160(pubkey)
default: default:
return ScriptUnknown, "Unknown", StackErrInvalidParseType return ScriptUnknown, nil, StackErrInvalidParseType
} }
if abuf != nil { return format.addrtype, addrhash, nil
addrbytes := append([]byte{atype}, abuf[:]...)
cksum := btcwire.DoubleSha256(addrbytes)
addrbytes = append(addrbytes, cksum[:4]...)
addr = btcutil.Base58Encode(addrbytes)
}
return format.addrtype, addr, nil
} }

View file

@ -4,13 +4,14 @@
package btcscript_test package btcscript_test
import ( import (
"bytes"
"github.com/conformal/btcscript" "github.com/conformal/btcscript"
"testing" "testing"
) )
type addressTest struct { type addressTest struct {
script []byte script []byte
address string addrhash []byte
shouldFail error shouldFail error
class btcscript.ScriptType class btcscript.ScriptType
} }
@ -26,8 +27,10 @@ var addressTests = []addressTest{
0xfa, 0x9b, 0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f,
0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 0xb4, 0x12, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 0xb4, 0x12,
0xa3, btcscript.OP_CHECKSIG}, 0xa3, btcscript.OP_CHECKSIG},
address: "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S", addrhash: []byte{0x11, 0xb3, 0x66, 0xed, 0xfc, 0x0a,
class: btcscript.ScriptPubKey, 0x8b, 0x66, 0xfe, 0xeb, 0xae, 0x5c, 0x2e,
0x25, 0xa7, 0xb6, 0xa5, 0xd1, 0xcf, 0x31},
class: btcscript.ScriptPubKey,
}, },
{script: []byte{btcscript.OP_DATA_65, {script: []byte{btcscript.OP_DATA_65,
0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a,
@ -51,8 +54,8 @@ var addressTests = []addressTest{
0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c,
0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, 0x08, 0x22, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, 0x08, 0x22,
0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, 0x01}, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, 0x01},
address: "Unknown", addrhash: nil,
class: btcscript.ScriptPubKey, class: btcscript.ScriptPubKey,
}, },
{script: []byte{btcscript.OP_DUP, btcscript.OP_HASH160, {script: []byte{btcscript.OP_DUP, btcscript.OP_HASH160,
btcscript.OP_DATA_20, btcscript.OP_DATA_20,
@ -61,8 +64,10 @@ var addressTests = []addressTest{
0x09, 0xa3, 0x05, 0x64, 0x09, 0xa3, 0x05, 0x64,
btcscript.OP_EQUALVERIFY, btcscript.OP_CHECKSIG, btcscript.OP_EQUALVERIFY, btcscript.OP_CHECKSIG,
}, },
address: "1Gmt8AzabtngttF3PcZzLR1p7uCMaHNuGY", addrhash: []byte{0xad, 0x06, 0xdd, 0x6d, 0xde, 0xe5,
class: btcscript.ScriptAddr, 0x5c, 0xbc, 0xa9, 0xa9, 0xe3, 0x71, 0x3b,
0xd7, 0x58, 0x75, 0x09, 0xa3, 0x05, 0x64},
class: btcscript.ScriptAddr,
}, },
{script: []byte{btcscript.OP_DATA_73, {script: []byte{btcscript.OP_DATA_73,
0x30, 0x46, 0x02, 0x21, 0x00, 0xdd, 0xc6, 0x97, 0x30, 0x46, 0x02, 0x21, 0x00, 0xdd, 0xc6, 0x97,
@ -85,8 +90,10 @@ var addressTests = []addressTest{
0x93, 0x7d, 0x58, 0xe5, 0xa7, 0x5a, 0x71, 0x04, 0x93, 0x7d, 0x58, 0xe5, 0xa7, 0x5a, 0x71, 0x04,
0x2d, 0x40, 0x38, 0x8a, 0x4d, 0x30, 0x7f, 0x88, 0x2d, 0x40, 0x38, 0x8a, 0x4d, 0x30, 0x7f, 0x88,
0x7d}, 0x7d},
address: "16tRBxwU7t5hEHaPLqiE35gS3jaGBppraH", addrhash: []byte{0x40, 0x92, 0x08, 0xf3, 0x87, 0xf4,
class: btcscript.ScriptAddr, 0x7f, 0xd2, 0x3a, 0x9f, 0x44, 0x5e, 0x14,
0xdc, 0x1f, 0x99, 0xbb, 0xb8, 0x0d, 0xaa},
class: btcscript.ScriptAddr,
}, },
{script: []byte{btcscript.OP_DATA_73, {script: []byte{btcscript.OP_DATA_73,
0x30, 0x46, 0x02, 0x21, 0x00, 0xac, 0x7e, 0x4e, 0x30, 0x46, 0x02, 0x21, 0x00, 0xac, 0x7e, 0x4e,
@ -106,8 +113,10 @@ var addressTests = []addressTest{
0x72, 0x3d, 0x5a, 0xd4, 0x06, 0x8d, 0xdd, 0x30, 0x72, 0x3d, 0x5a, 0xd4, 0x06, 0x8d, 0xdd, 0x30,
0x36, 0x36,
}, },
address: "1272555ceTPn2WepjzVgFESWdfNQjqdjgp", addrhash: []byte{0x0c, 0x1b, 0x83, 0xd0, 0x1d, 0x0f,
class: btcscript.ScriptAddr, 0xfb, 0x2b, 0xcc, 0xae, 0x60, 0x69, 0x63,
0x37, 0x6c, 0xca, 0x38, 0x63, 0xa7, 0xce},
class: btcscript.ScriptAddr,
}, },
{script: []byte{btcscript.OP_DATA_32, {script: []byte{btcscript.OP_DATA_32,
0x30, 0x46, 0x02, 0x21, 0x00, 0xac, 0x7e, 0x4e, 0x30, 0x46, 0x02, 0x21, 0x00, 0xac, 0x7e, 0x4e,
@ -115,8 +124,8 @@ var addressTests = []addressTest{
0x87, 0x2a, 0xb9, 0xd3, 0x2c, 0xdc, 0x08, 0x33, 0x87, 0x2a, 0xb9, 0xd3, 0x2c, 0xdc, 0x08, 0x33,
0x80, 0x73, 0x3e, 0x3e, 0x98, 0x47, 0xff, 0x77, 0x80, 0x73, 0x3e, 0x3e, 0x98, 0x47, 0xff, 0x77,
}, },
address: "Unknown", addrhash: nil,
class: btcscript.ScriptStrange, class: btcscript.ScriptStrange,
}, },
{script: []byte{btcscript.OP_DATA_33, {script: []byte{btcscript.OP_DATA_33,
0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6, 0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6,
@ -126,8 +135,10 @@ var addressTests = []addressTest{
0x36, 0x36,
btcscript.OP_CHECKSIG, btcscript.OP_CHECKSIG,
}, },
address: "1272555ceTPn2WepjzVgFESWdfNQjqdjgp", addrhash: []byte{0x0c, 0x1b, 0x83, 0xd0, 0x1d, 0x0f,
class: btcscript.ScriptPubKey, 0xfb, 0x2b, 0xcc, 0xae, 0x60, 0x69, 0x63,
0x37, 0x6c, 0xca, 0x38, 0x63, 0xa7, 0xce},
class: btcscript.ScriptPubKey,
}, },
{script: []byte{btcscript.OP_DATA_33, {script: []byte{btcscript.OP_DATA_33,
0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6, 0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6,
@ -137,8 +148,8 @@ var addressTests = []addressTest{
0x36, 0x36,
btcscript.OP_CHECK_MULTISIG, // note this isn't a real tx btcscript.OP_CHECK_MULTISIG, // note this isn't a real tx
}, },
address: "Unknown", addrhash: nil,
class: btcscript.ScriptStrange, class: btcscript.ScriptStrange,
}, },
{script: []byte{btcscript.OP_0, btcscript.OP_DATA_33, {script: []byte{btcscript.OP_0, btcscript.OP_DATA_33,
0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6, 0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6,
@ -147,8 +158,8 @@ var addressTests = []addressTest{
0x72, 0x3d, 0x5a, 0xd4, 0x06, 0x8d, 0xdd, 0x30, 0x72, 0x3d, 0x5a, 0xd4, 0x06, 0x8d, 0xdd, 0x30,
0x36, // note this isn't a real tx 0x36, // note this isn't a real tx
}, },
address: "Unknown", addrhash: nil,
class: btcscript.ScriptStrange, class: btcscript.ScriptStrange,
}, },
{script: []byte{btcscript.OP_HASH160, btcscript.OP_DATA_20, {script: []byte{btcscript.OP_HASH160, btcscript.OP_DATA_20,
0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6, 0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6,
@ -156,8 +167,8 @@ var addressTests = []addressTest{
0xa7, 0xa8, 0x45, 0xbd, 0xa7, 0xa8, 0x45, 0xbd,
btcscript.OP_EQUAL, // note this isn't a real tx btcscript.OP_EQUAL, // note this isn't a real tx
}, },
address: "Unknown", addrhash: nil,
class: btcscript.ScriptStrange, class: btcscript.ScriptStrange,
}, },
{script: []byte{btcscript.OP_DATA_36, {script: []byte{btcscript.OP_DATA_36,
0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6, 0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6,
@ -167,8 +178,8 @@ var addressTests = []addressTest{
0xa7, 0xa8, 0x45, 0xbd, 0xa7, 0xa8, 0x45, 0xbd,
// note this isn't a real tx // note this isn't a real tx
}, },
address: "Unknown", addrhash: nil,
class: btcscript.ScriptStrange, class: btcscript.ScriptStrange,
}, },
{script: []byte{}, {script: []byte{},
shouldFail: btcscript.StackErrUnknownAddress, shouldFail: btcscript.StackErrUnknownAddress,
@ -177,7 +188,7 @@ var addressTests = []addressTest{
func TestAddresses(t *testing.T) { func TestAddresses(t *testing.T) {
for i, s := range addressTests { for i, s := range addressTests {
class, address, err := btcscript.ScriptToAddress(s.script) class, addrhash, err := btcscript.ScriptToAddrHash(s.script)
if s.shouldFail != nil { if s.shouldFail != nil {
if err != s.shouldFail { if err != s.shouldFail {
t.Errorf("Address test %v failed is err [%v] should be [%v]", i, err, s.shouldFail) t.Errorf("Address test %v failed is err [%v] should be [%v]", i, err, s.shouldFail)
@ -186,8 +197,8 @@ func TestAddresses(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("Address test %v failed err %v", i, err) t.Errorf("Address test %v failed err %v", i, err)
} else { } else {
if s.address != address { if !bytes.Equal(s.addrhash, addrhash) {
t.Errorf("Address test %v mismatch is [%v] want [%v]", i, address, s.address) t.Errorf("Address test %v mismatch is [%v] want [%v]", i, addrhash, s.addrhash)
} }
if s.class != class { if s.class != class {
t.Errorf("Address test %v class mismatch is [%v] want [%v]", i, class, s.class) t.Errorf("Address test %v class mismatch is [%v] want [%v]", i, class, s.class)

View file

@ -49,8 +49,8 @@ func TstSignatureScriptCustomReader(reader io.Reader, tx *btcwire.MsgTx, idx int
hashType, privkey, compress) hashType, privkey, compress)
} }
// Tests for internal error cases in ScriptToAddress. // Tests for internal error cases in ScriptToAddrHash.
// We pass bad format definitions to ScriptToAddrss to make sure the internal // We pass bad format definitions to ScriptToAddrHash to make sure the internal
// checks work correctly. This is located in internal_test.go and not address.go // checks work correctly. This is located in internal_test.go and not address.go
// because of the ridiculous amount of internal types/constants that would // because of the ridiculous amount of internal types/constants that would
// otherwise need to be exported here. // otherwise need to be exported here.
@ -94,7 +94,7 @@ var TstPkFormats = []pkformatTest{
func TestBadPkFormat(t *testing.T) { func TestBadPkFormat(t *testing.T) {
for _, test := range TstPkFormats { for _, test := range TstPkFormats {
ty, addr, err := scriptToAddressTemplate(test.script, ty, addr, err := scriptToAddrHashTemplate(test.script,
[]pkformat{test.format}) []pkformat{test.format})
if err != nil { if err != nil {
if err != test.err { if err != test.err {