Handle Bitcoin taproot addresses

This commit is contained in:
Martin Boehm 2021-09-23 00:29:28 +02:00
parent dff928456d
commit ba75e60950
7 changed files with 185 additions and 20 deletions

View File

@ -186,7 +186,7 @@ func (p *BitcoinLikeParser) outputScriptToAddresses(script []byte) ([]string, bo
rv[i] = a.EncodeAddress()
}
var s bool
if sc == txscript.PubKeyHashTy || sc == txscript.WitnessV0PubKeyHashTy || sc == txscript.ScriptHashTy || sc == txscript.WitnessV0ScriptHashTy {
if sc == txscript.PubKeyHashTy || sc == txscript.WitnessV0PubKeyHashTy || sc == txscript.ScriptHashTy || sc == txscript.WitnessV0ScriptHashTy || sc == txscript.WitnessV1TaprootTy {
s = true
} else if len(rv) == 0 {
or := p.TryParseOPReturn(script)
@ -345,13 +345,13 @@ func (p *BitcoinLikeParser) DeriveAddressDescriptors(xpub string, change uint32,
if err != nil {
return nil, err
}
changeExtKey, err := extKey.Child(change)
changeExtKey, err := extKey.Derive(change)
if err != nil {
return nil, err
}
ad := make([]bchain.AddressDescriptor, len(indexes))
for i, index := range indexes {
indexExtKey, err := changeExtKey.Child(index)
indexExtKey, err := changeExtKey.Derive(index)
if err != nil {
return nil, err
}
@ -372,13 +372,13 @@ func (p *BitcoinLikeParser) DeriveAddressDescriptorsFromTo(xpub string, change u
if err != nil {
return nil, err
}
changeExtKey, err := extKey.Child(change)
changeExtKey, err := extKey.Derive(change)
if err != nil {
return nil, err
}
ad := make([]bchain.AddressDescriptor, toIndex-fromIndex)
for index := fromIndex; index < toIndex; index++ {
indexExtKey, err := changeExtKey.Child(index)
indexExtKey, err := changeExtKey.Derive(index)
if err != nil {
return nil, err
}

View File

@ -1,3 +1,4 @@
//go:build unittest
// +build unittest
package btc
@ -59,6 +60,18 @@ func TestGetAddrDescFromAddress(t *testing.T) {
want: "002003973a40ec94c0d10f6f6f0e7a62ba2044b7d19db6ff2bf60651e17fb29d8d29",
wantErr: false,
},
{
name: " witness_unknown v1",
args: args{address: "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y"},
want: "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6",
wantErr: false,
},
{
name: " witness_unknown v16",
args: args{address: "bc1sw50qgdz25j"},
want: "6002751e",
wantErr: false,
},
}
parser := NewBitcoinParser(GetChainParams("main"), &Configuration{})
@ -77,6 +90,64 @@ func TestGetAddrDescFromAddress(t *testing.T) {
}
}
func TestGetAddrDescFromAddressTestnet(t *testing.T) {
type args struct {
address string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "pubkeyhash",
args: args{address: "mtkbaiLiUH3fvGJeSzuN3kUgmJzqinLejJ"},
want: "76a914912e2b234f941f30b18afbb4fa46171214bf66c888ac",
wantErr: false,
},
{
name: "scripthash",
args: args{address: "2Mv28xcUJdFXBTfGMtja6fVBMCEbsH3r2AW"},
want: "a9141e6ec5a1d12912b396d77d98dcb000e91f517fa487",
wantErr: false,
},
{
name: "witness_v0_keyhash",
args: args{address: "tb1qupjdck20as3y4l95cd5wepkv0grcz0p7d8rd5s"},
want: "0014e064dc594fec224afcb4c368ec86cc7a07813c3e",
wantErr: false,
},
{
name: "witness_v0_scripthash",
args: args{address: "tb1qqwtn5s8vjnqdzrm0du885c46ypzt05vakmljhasx28shlv5a355seu0fjv"},
want: "002003973a40ec94c0d10f6f6f0e7a62ba2044b7d19db6ff2bf60651e17fb29d8d29",
wantErr: false,
},
{
name: "witness_v1_taproot",
args: args{address: "tb1pqsv2qyp8hsma46422ecfd3ek02jayumkkzjx7vkf3cqpmfd4ucpsx0cc9h"},
want: "51200418a01027bc37daeaaa567096c7367aa5d27376b0a46f32c98e001da5b5e603",
wantErr: false,
},
}
parser := NewBitcoinParser(GetChainParams("test"), &Configuration{})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
}
func TestGetAddrDescFromVout(t *testing.T) {
type args struct {
vout bchain.Vout
@ -307,6 +378,95 @@ We know the game and we're gonna play it (TO FRONT)
}
}
func TestGetAddressesFromAddrDescTestnet(t *testing.T) {
type args struct {
script string
}
tests := []struct {
name string
args args
want []string
want2 bool
wantErr bool
}{
{
name: "pubkeyhash",
args: args{script: "76a914912e2b234f941f30b18afbb4fa46171214bf66c888ac"},
want: []string{"mtkbaiLiUH3fvGJeSzuN3kUgmJzqinLejJ"},
want2: true,
wantErr: false,
},
{
name: "pubkey compressed",
args: args{script: "2102a741071164b40b01c4ad28913c4aa2a1015cc5b064f0c802272552f17ae08750ac"},
want: []string{"mkMe1fsfCWFext2qxf4bk3yiruBTvnici4"},
want2: false,
wantErr: false,
},
{
name: "pubkey uncompressed",
args: args{script: "41041057356b91bfd3efeff5fc0fa8b865faafafb67bd653c5da2cd16ce15c7b86db0e622c8e1e135f68918a23601eb49208c1ac72c7b64a4ee99c396cf788da16ccac"},
want: []string{"mx43tNdg4JYY29ifrHjJpdbcCqqDGVSng5"},
want2: false,
wantErr: false,
},
{
name: "scripthash",
args: args{script: "a9141e6ec5a1d12912b396d77d98dcb000e91f517fa487"},
want: []string{"2Mv28xcUJdFXBTfGMtja6fVBMCEbsH3r2AW"},
want2: true,
wantErr: false,
},
{
name: "witness_v0_keyhash",
args: args{script: "0014e064dc594fec224afcb4c368ec86cc7a07813c3e"},
want: []string{"tb1qupjdck20as3y4l95cd5wepkv0grcz0p7d8rd5s"},
want2: true,
wantErr: false,
},
{
name: "witness_v0_scripthash",
args: args{script: "002003973a40ec94c0d10f6f6f0e7a62ba2044b7d19db6ff2bf60651e17fb29d8d29"},
want: []string{"tb1qqwtn5s8vjnqdzrm0du885c46ypzt05vakmljhasx28shlv5a355seu0fjv"},
want2: true,
wantErr: false,
},
{
name: "witness_v1_taproot",
args: args{script: "51200418a01027bc37daeaaa567096c7367aa5d27376b0a46f32c98e001da5b5e603"},
want: []string{"tb1pqsv2qyp8hsma46422ecfd3ek02jayumkkzjx7vkf3cqpmfd4ucpsx0cc9h"},
want2: true,
wantErr: false,
},
{
name: "OP_RETURN ascii",
args: args{script: "6a0461686f6a"},
want: []string{"OP_RETURN (ahoj)"},
want2: false,
wantErr: false,
},
}
parser := NewBitcoinParser(GetChainParams("test"), &Configuration{})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b, _ := hex.DecodeString(tt.args.script)
got, got2, err := parser.GetAddressesFromAddrDesc(b)
if (err != nil) != tt.wantErr {
t.Errorf("TestGetAddressesFromAddrDesc_Testnet() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("TestGetAddressesFromAddrDesc_Testnet() = %v, want %v", got, tt.want)
}
if !reflect.DeepEqual(got2, tt.want2) {
t.Errorf("TestGetAddressesFromAddrDesc_Testnet() = %v, want %v", got2, tt.want2)
}
})
}
}
var (
testTx1, testTx2, testTx3 bchain.Tx

View File

@ -678,10 +678,8 @@ func (b *BitcoinRPC) GetMempoolTransactions() ([]string, error) {
// IsMissingTx return true if error means missing tx
func IsMissingTx(err *bchain.RPCError) bool {
if err.Code == -5 { // "No such mempool or blockchain transaction"
return true
}
return false
// err.Code == -5 "No such mempool or blockchain transaction"
return err.Code == -5
}
// GetTransactionForMempool returns a transaction by the transaction ID

View File

@ -169,13 +169,13 @@ func (p *NulsParser) DeriveAddressDescriptorsFromTo(xpub string, change uint32,
if err != nil {
return nil, err
}
changeExtKey, err := extKey.Child(change)
changeExtKey, err := extKey.Derive(change)
if err != nil {
return nil, err
}
ad := make([]bchain.AddressDescriptor, toIndex-fromIndex)
for index := fromIndex; index < toIndex; index++ {
indexExtKey, err := changeExtKey.Child(index)
indexExtKey, err := changeExtKey.Derive(index)
if err != nil {
return nil, err
}

View File

@ -1,3 +1,4 @@
//go:build unittest
// +build unittest
package nuls
@ -359,13 +360,13 @@ func TestDeriveAddressDescriptorsFromTo(t *testing.T) {
t.Errorf("DeriveAddressDescriptorsFromTo() error = %v", err)
return
}
changeExtKey, err := extKey.Child(0)
changeExtKey, err := extKey.Derive(0)
if err != nil {
t.Errorf("DeriveAddressDescriptorsFromTo() error = %v", err)
return
}
key1, _ := changeExtKey.Child(0)
key1, _ := changeExtKey.Derive(0)
priKey1, _ := key1.ECPrivKey()
wantPriKey1 := "0x995c98115809359eb57a5e179558faddd55ef88f88e5cf58617a5f9f3d6bb3a1"
if !reflect.DeepEqual(hexutil.MustDecode(wantPriKey1), priKey1.Serialize()) {
@ -379,7 +380,7 @@ func TestDeriveAddressDescriptorsFromTo(t *testing.T) {
return
}
key2, _ := changeExtKey.Child(1)
key2, _ := changeExtKey.Derive(1)
priKey2, _ := key2.ECPrivKey()
wantPriKey2 := "0x0f65dee42d3c974c1a4bcc79f141be89715dc8d6406faa9ad4f1f55ca95fabc8"
if !reflect.DeepEqual(hexutil.MustDecode(wantPriKey2), priKey2.Serialize()) {
@ -393,7 +394,7 @@ func TestDeriveAddressDescriptorsFromTo(t *testing.T) {
return
}
key3, _ := changeExtKey.Child(2)
key3, _ := changeExtKey.Derive(2)
priKey3, _ := key3.ECPrivKey()
wantPriKey3 := "0x6fd98d1d9c3f3ac1ff61bbf3f20e89f00ffa8d43a554f2a7d73fd464b6666f45"
if !reflect.DeepEqual(hexutil.MustDecode(wantPriKey3), priKey3.Serialize()) {
@ -407,7 +408,7 @@ func TestDeriveAddressDescriptorsFromTo(t *testing.T) {
return
}
key4, _ := changeExtKey.Child(3)
key4, _ := changeExtKey.Derive(3)
priKey4, _ := key4.ECPrivKey()
wantPriKey4 := "0x21412d9e33aba493faf4bc7d408ed5290bea5b36a7beec554b858051f8d4bff3"
if !reflect.DeepEqual(hexutil.MustDecode(wantPriKey4), priKey4.Serialize()) {
@ -421,7 +422,7 @@ func TestDeriveAddressDescriptorsFromTo(t *testing.T) {
return
}
key5, _ := changeExtKey.Child(4)
key5, _ := changeExtKey.Derive(4)
priKey5, _ := key5.ECPrivKey()
wantPriKey5 := "0xdc3d290e32a4e0f38bc26c25a78ceb1c8779110883d9cb0be54629043c1f8724"
if !reflect.DeepEqual(hexutil.MustDecode(wantPriKey5), priKey5.Serialize()) {

2
go.mod
View File

@ -29,7 +29,7 @@ require (
github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect
github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe
github.com/martinboehm/btcd v0.0.0-20200313230603-83af86142d93
github.com/martinboehm/btcutil v0.0.0-20200229134221-d7706467ae8f
github.com/martinboehm/btcutil v0.0.0-20210922221517-e83b0c752949
github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect

10
go.sum
View File

@ -378,6 +378,7 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/kkdai/bstream v0.0.0-20171226095907-f71540b9dfdc/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
@ -401,11 +402,16 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe h1:khZWpHuxJNh2EGzBbaS6EQ2d6KxgK31WeG0TnlTMUD4=
github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe/go.mod h1:0hw4tpGU+9slqN/DrevhjTMb0iR9esxzpCdx8I6/UzU=
github.com/martinboehm/btcd v0.0.0-20190104121910-8e7c0427fee5/go.mod h1:rKQj/jGwFruYjpM6vN+syReFoR0DsLQaajhyH/5mwUE=
github.com/martinboehm/btcd v0.0.0-20200313230603-83af86142d93 h1:SBXxF9UMEPAswVhAt3Y275Lx59Do8C/rpAmg6k73HYY=
github.com/martinboehm/btcd v0.0.0-20200313230603-83af86142d93/go.mod h1:rKQj/jGwFruYjpM6vN+syReFoR0DsLQaajhyH/5mwUE=
github.com/martinboehm/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:NIviPmxe43yBgIB4HGB4w4kv9/s5kaDa/pi+wZAAxQo=
github.com/martinboehm/btcutil v0.0.0-20200229134221-d7706467ae8f h1:MMI9XvVjNHkqQDDyud0Ll0qd/w4jAhgkLZMY8q6KbR8=
github.com/martinboehm/btcutil v0.0.0-20200229134221-d7706467ae8f/go.mod h1:NIviPmxe43yBgIB4HGB4w4kv9/s5kaDa/pi+wZAAxQo=
github.com/martinboehm/btcutil v0.0.0-20210914231321-8ece5dcd9f5f h1:zDEDlafs4y1CjDqcowXFzcttj/wt6N2wV9U2NihjgcU=
github.com/martinboehm/btcutil v0.0.0-20210914231321-8ece5dcd9f5f/go.mod h1:8iJaVY/VHW6lnojpTXf5X4gF2dx81Xtj2R6lJp2colA=
github.com/martinboehm/btcutil v0.0.0-20210922133746-0042eb304b5b h1:xm/0cKQ6PffWGXfPyfanJaZ7skZo/6uT6pb+mtVEU1o=
github.com/martinboehm/btcutil v0.0.0-20210922133746-0042eb304b5b/go.mod h1:8iJaVY/VHW6lnojpTXf5X4gF2dx81Xtj2R6lJp2colA=
github.com/martinboehm/btcutil v0.0.0-20210922221517-e83b0c752949 h1:GdmOPmno0QNl9jVMJZOTj7ekgGA9zRG6gn1X9jutrkU=
github.com/martinboehm/btcutil v0.0.0-20210922221517-e83b0c752949/go.mod h1:8iJaVY/VHW6lnojpTXf5X4gF2dx81Xtj2R6lJp2colA=
github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde h1:Tz7WkXgQjeQVymqSQkEapbe/ZuzKCvb6GANFHnl0uAE=
github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde/go.mod h1:p35TWcm7GkAwvPcUCEq4H+yTm0gA8Aq7UvGnbK6olQk=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=