diff --git a/bchain/baseparser.go b/bchain/baseparser.go index e62b28f7..dbba33a5 100644 --- a/bchain/baseparser.go +++ b/bchain/baseparser.go @@ -268,7 +268,12 @@ func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) { } // DeriveAddressDescriptors is unsupported -func (p *BaseParser) DeriveAddressDescriptors(xpub string, change uint32, fromIndex uint32, toIndex uint32) ([]AddressDescriptor, error) { +func (p *BaseParser) DeriveAddressDescriptors(xpub string, change uint32, indexes []uint32) ([]AddressDescriptor, error) { + return nil, errors.New("Not supported") +} + +// DeriveAddressDescriptorsFromTo is unsupported +func (p *BaseParser) DeriveAddressDescriptorsFromTo(xpub string, change uint32, fromIndex uint32, toIndex uint32) ([]AddressDescriptor, error) { return nil, errors.New("Not supported") } diff --git a/bchain/coins/btc/bitcoinparser.go b/bchain/coins/btc/bitcoinparser.go index 15264fd3..d4f13b02 100644 --- a/bchain/coins/btc/bitcoinparser.go +++ b/bchain/coins/btc/bitcoinparser.go @@ -299,8 +299,32 @@ func (p *BitcoinParser) addrDescFromExtKey(extKey *hdkeychain.ExtendedKey) (bcha return txscript.PayToAddrScript(a) } -// DeriveAddressDescriptors derives address descriptors from given xpub -func (p *BitcoinParser) DeriveAddressDescriptors(xpub string, change uint32, fromIndex uint32, toIndex uint32) ([]bchain.AddressDescriptor, error) { +// DeriveAddressDescriptors derives address descriptors from given xpub for listed indexes +func (p *BitcoinParser) DeriveAddressDescriptors(xpub string, change uint32, indexes []uint32) ([]bchain.AddressDescriptor, error) { + extKey, err := hdkeychain.NewKeyFromString(xpub) + if err != nil { + return nil, err + } + changeExtKey, err := extKey.Child(change) + if err != nil { + return nil, err + } + ad := make([]bchain.AddressDescriptor, len(indexes)) + for i, index := range indexes { + indexExtKey, err := changeExtKey.Child(index) + if err != nil { + return nil, err + } + ad[i], err = p.addrDescFromExtKey(indexExtKey) + if err != nil { + return nil, err + } + } + return ad, nil +} + +// DeriveAddressDescriptorsFromTo derives address descriptors from given xpub for addresses in index range +func (p *BitcoinParser) DeriveAddressDescriptorsFromTo(xpub string, change uint32, fromIndex uint32, toIndex uint32) ([]bchain.AddressDescriptor, error) { if toIndex <= fromIndex { return nil, errors.New("toIndex<=fromIndex") } diff --git a/bchain/coins/btc/bitcoinparser_test.go b/bchain/coins/btc/bitcoinparser_test.go index 8aefdda0..e610cca2 100644 --- a/bchain/coins/btc/bitcoinparser_test.go +++ b/bchain/coins/btc/bitcoinparser_test.go @@ -419,6 +419,74 @@ func Test_UnpackTx(t *testing.T) { } func Test_DeriveAddressDescriptors(t *testing.T) { + btcMainParser := NewBitcoinParser(GetChainParams("main"), &Configuration{XPubMagic: 76067358, XPubMagicSegwitP2sh: 77429938, XPubMagicSegwitNative: 78792518}) + type args struct { + xpub string + change uint32 + indexes []uint32 + parser *BitcoinParser + } + tests := []struct { + name string + args args + want []string + wantErr bool + }{ + { + name: "m/44'/0'/0'", + args: args{ + xpub: "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj", + change: 0, + indexes: []uint32{0, 1234}, + parser: btcMainParser, + }, + want: []string{"1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA", "1P9w11dXAmG3QBjKLAvCsek8izs1iR2iFi"}, + }, + { + name: "m/49'/0'/0'", + args: args{ + xpub: "ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP", + change: 0, + indexes: []uint32{0, 1234}, + parser: btcMainParser, + }, + want: []string{"37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf", "367meFzJ9KqDLm9PX6U8Z8RdmkSNBuxX8T"}, + }, + { + name: "m/84'/0'/0'", + args: args{ + xpub: "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs", + change: 0, + indexes: []uint32{0, 1234}, + parser: btcMainParser, + }, + want: []string{"bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", "bc1q4nm6g46ujzyjaeusralaz2nfv2rf04jjfyamkw"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.args.parser.DeriveAddressDescriptors(tt.args.xpub, tt.args.change, tt.args.indexes) + if (err != nil) != tt.wantErr { + t.Errorf("DeriveAddressDescriptorsFromTo() error = %v, wantErr %v", err, tt.wantErr) + return + } + gotAddresses := make([]string, len(got)) + for i, ad := range got { + aa, _, err := tt.args.parser.GetAddressesFromAddrDesc(ad) + if err != nil || len(aa) != 1 { + t.Errorf("DeriveAddressDescriptorsFromTo() got incorrect address descriptor %v, error %v", ad, err) + return + } + gotAddresses[i] = aa[0] + } + if !reflect.DeepEqual(gotAddresses, tt.want) { + t.Errorf("DeriveAddressDescriptorsFromTo() = %v, want %v", gotAddresses, tt.want) + } + }) + } +} + +func Test_DeriveAddressDescriptorsFromTo(t *testing.T) { btcMainParser := NewBitcoinParser(GetChainParams("main"), &Configuration{XPubMagic: 76067358, XPubMagicSegwitP2sh: 77429938, XPubMagicSegwitNative: 78792518}) type args struct { xpub string @@ -469,22 +537,22 @@ func Test_DeriveAddressDescriptors(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := tt.args.parser.DeriveAddressDescriptors(tt.args.xpub, tt.args.change, tt.args.fromIndex, tt.args.toIndex) + got, err := tt.args.parser.DeriveAddressDescriptorsFromTo(tt.args.xpub, tt.args.change, tt.args.fromIndex, tt.args.toIndex) if (err != nil) != tt.wantErr { - t.Errorf("DeriveAddressDescriptors() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("DeriveAddressDescriptorsFromTo() error = %v, wantErr %v", err, tt.wantErr) return } gotAddresses := make([]string, len(got)) for i, ad := range got { aa, _, err := tt.args.parser.GetAddressesFromAddrDesc(ad) if err != nil || len(aa) != 1 { - t.Errorf("DeriveAddressDescriptors() got incorrect address descriptor %v, error %v", ad, err) + t.Errorf("DeriveAddressDescriptorsFromTo() got incorrect address descriptor %v, error %v", ad, err) return } gotAddresses[i] = aa[0] } if !reflect.DeepEqual(gotAddresses, tt.want) { - t.Errorf("DeriveAddressDescriptors() = %v, want %v", gotAddresses, tt.want) + t.Errorf("DeriveAddressDescriptorsFromTo() = %v, want %v", gotAddresses, tt.want) } }) } diff --git a/bchain/types.go b/bchain/types.go index 9f44940e..f9e9245a 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -264,7 +264,8 @@ type BlockChainParser interface { UnpackBlockHash(buf []byte) (string, error) ParseBlock(b []byte) (*Block, error) // xpub - DeriveAddressDescriptors(xpub string, change uint32, fromIndex uint32, toIndex uint32) ([]AddressDescriptor, error) + DeriveAddressDescriptors(xpub string, change uint32, indexes []uint32) ([]AddressDescriptor, error) + DeriveAddressDescriptorsFromTo(xpub string, change uint32, fromIndex uint32, toIndex uint32) ([]AddressDescriptor, error) // EthereumType specific EthereumTypeGetErc20FromTx(tx *Tx) ([]Erc20Transfer, error) }