Implement parser.DeriveAddressDescriptors from xpub
This commit is contained in:
parent
dafe19cf29
commit
986275bb76
4
Gopkg.lock
generated
4
Gopkg.lock
generated
@ -94,8 +94,8 @@
|
|||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/martinboehm/btcutil"
|
name = "github.com/martinboehm/btcutil"
|
||||||
packages = [".","base58","bech32","chaincfg","txscript"]
|
packages = [".","base58","bech32","chaincfg","hdkeychain","txscript"]
|
||||||
revision = "613fec26904062ae125fb073762af3a77c77b6c7"
|
revision = "9b332d8046124a83bab2830696e8ebddaf3f1788"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
|||||||
@ -267,6 +267,11 @@ func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) {
|
|||||||
return &tx, pt.Height, nil
|
return &tx, pt.Height, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeriveAddressDescriptors is unsupported
|
||||||
|
func (p *BaseParser) DeriveAddressDescriptors(xpub string, change uint32, fromIndex uint32, toIndex uint32) ([]AddressDescriptor, error) {
|
||||||
|
return nil, errors.New("Not supported")
|
||||||
|
}
|
||||||
|
|
||||||
// EthereumTypeGetErc20FromTx is unsupported
|
// EthereumTypeGetErc20FromTx is unsupported
|
||||||
func (p *BaseParser) EthereumTypeGetErc20FromTx(tx *Tx) ([]Erc20Transfer, error) {
|
func (p *BaseParser) EthereumTypeGetErc20FromTx(tx *Tx) ([]Erc20Transfer, error) {
|
||||||
return nil, errors.New("Not supported")
|
return nil, errors.New("Not supported")
|
||||||
|
|||||||
@ -8,10 +8,12 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
vlq "github.com/bsm/go-vlq"
|
vlq "github.com/bsm/go-vlq"
|
||||||
|
"github.com/juju/errors"
|
||||||
"github.com/martinboehm/btcd/blockchain"
|
"github.com/martinboehm/btcd/blockchain"
|
||||||
"github.com/martinboehm/btcd/wire"
|
"github.com/martinboehm/btcd/wire"
|
||||||
"github.com/martinboehm/btcutil"
|
"github.com/martinboehm/btcutil"
|
||||||
"github.com/martinboehm/btcutil/chaincfg"
|
"github.com/martinboehm/btcutil/chaincfg"
|
||||||
|
"github.com/martinboehm/btcutil/hdkeychain"
|
||||||
"github.com/martinboehm/btcutil/txscript"
|
"github.com/martinboehm/btcutil/txscript"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,6 +25,9 @@ type BitcoinParser struct {
|
|||||||
*bchain.BaseParser
|
*bchain.BaseParser
|
||||||
Params *chaincfg.Params
|
Params *chaincfg.Params
|
||||||
OutputScriptToAddressesFunc OutputScriptToAddressesFunc
|
OutputScriptToAddressesFunc OutputScriptToAddressesFunc
|
||||||
|
XPubMagic uint32
|
||||||
|
XPubMagicSegwitP2sh uint32
|
||||||
|
XPubMagicSegwitNative uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBitcoinParser returns new BitcoinParser instance
|
// NewBitcoinParser returns new BitcoinParser instance
|
||||||
@ -32,7 +37,10 @@ func NewBitcoinParser(params *chaincfg.Params, c *Configuration) *BitcoinParser
|
|||||||
BlockAddressesToKeep: c.BlockAddressesToKeep,
|
BlockAddressesToKeep: c.BlockAddressesToKeep,
|
||||||
AmountDecimalPoint: 8,
|
AmountDecimalPoint: 8,
|
||||||
},
|
},
|
||||||
Params: params,
|
Params: params,
|
||||||
|
XPubMagic: c.XPubMagic,
|
||||||
|
XPubMagicSegwitP2sh: c.XPubMagicSegwitP2sh,
|
||||||
|
XPubMagicSegwitNative: c.XPubMagicSegwitNative,
|
||||||
}
|
}
|
||||||
p.OutputScriptToAddressesFunc = p.outputScriptToAddresses
|
p.OutputScriptToAddressesFunc = p.outputScriptToAddresses
|
||||||
return p
|
return p
|
||||||
@ -266,3 +274,54 @@ func (p *BitcoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
|
|||||||
|
|
||||||
return tx, height, nil
|
return tx, height, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *BitcoinParser) addrDescFromExtKey(extKey *hdkeychain.ExtendedKey) (bchain.AddressDescriptor, error) {
|
||||||
|
var a btcutil.Address
|
||||||
|
var err error
|
||||||
|
if extKey.Version() == p.XPubMagicSegwitP2sh {
|
||||||
|
// redeemScript <witness version: OP_0><len pubKeyHash: 20><20-byte-pubKeyHash>
|
||||||
|
pubKeyHash := btcutil.Hash160(extKey.PubKeyBytes())
|
||||||
|
redeemScript := make([]byte, len(pubKeyHash)+2)
|
||||||
|
redeemScript[0] = 0
|
||||||
|
redeemScript[1] = byte(len(pubKeyHash))
|
||||||
|
copy(redeemScript[2:], pubKeyHash)
|
||||||
|
hash := btcutil.Hash160(redeemScript)
|
||||||
|
a, err = btcutil.NewAddressScriptHashFromHash(hash, p.Params)
|
||||||
|
} else if extKey.Version() == p.XPubMagicSegwitNative {
|
||||||
|
a, err = btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(extKey.PubKeyBytes()), p.Params)
|
||||||
|
} else {
|
||||||
|
// default to P2PKH address
|
||||||
|
a, err = extKey.Address(p.Params)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
if toIndex <= fromIndex {
|
||||||
|
return nil, errors.New("toIndex<=fromIndex")
|
||||||
|
}
|
||||||
|
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, toIndex-fromIndex)
|
||||||
|
for index := fromIndex; index < toIndex; index++ {
|
||||||
|
indexExtKey, err := changeExtKey.Child(index)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ad[index-fromIndex], err = p.addrDescFromExtKey(indexExtKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ad, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -417,3 +417,75 @@ 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
|
||||||
|
fromIndex uint32
|
||||||
|
toIndex uint32
|
||||||
|
parser *BitcoinParser
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "m/44'/0'/0'",
|
||||||
|
args: args{
|
||||||
|
xpub: "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj",
|
||||||
|
change: 0,
|
||||||
|
fromIndex: 0,
|
||||||
|
toIndex: 1,
|
||||||
|
parser: btcMainParser,
|
||||||
|
},
|
||||||
|
want: []string{"1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "m/49'/0'/0'",
|
||||||
|
args: args{
|
||||||
|
xpub: "ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP",
|
||||||
|
change: 0,
|
||||||
|
fromIndex: 0,
|
||||||
|
toIndex: 1,
|
||||||
|
parser: btcMainParser,
|
||||||
|
},
|
||||||
|
want: []string{"37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "m/84'/0'/0'",
|
||||||
|
args: args{
|
||||||
|
xpub: "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs",
|
||||||
|
change: 0,
|
||||||
|
fromIndex: 0,
|
||||||
|
toIndex: 1,
|
||||||
|
parser: btcMainParser,
|
||||||
|
},
|
||||||
|
want: []string{"bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("DeriveAddressDescriptors() 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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gotAddresses[i] = aa[0]
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(gotAddresses, tt.want) {
|
||||||
|
t.Errorf("DeriveAddressDescriptors() = %v, want %v", gotAddresses, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -51,6 +51,9 @@ type Configuration struct {
|
|||||||
AddressFormat string `json:"address_format"`
|
AddressFormat string `json:"address_format"`
|
||||||
SupportsEstimateFee bool `json:"supports_estimate_fee"`
|
SupportsEstimateFee bool `json:"supports_estimate_fee"`
|
||||||
SupportsEstimateSmartFee bool `json:"supports_estimate_smart_fee"`
|
SupportsEstimateSmartFee bool `json:"supports_estimate_smart_fee"`
|
||||||
|
XPubMagic uint32 `json:"xpub_magic,omitempty"`
|
||||||
|
XPubMagicSegwitP2sh uint32 `json:"xpub_magic_segwit_p2sh,omitempty"`
|
||||||
|
XPubMagicSegwitNative uint32 `json:"xpub_magic_segwit_native,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBitcoinRPC returns new BitcoinRPC instance.
|
// NewBitcoinRPC returns new BitcoinRPC instance.
|
||||||
|
|||||||
@ -263,6 +263,8 @@ type BlockChainParser interface {
|
|||||||
PackBlockHash(hash string) ([]byte, error)
|
PackBlockHash(hash string) ([]byte, error)
|
||||||
UnpackBlockHash(buf []byte) (string, error)
|
UnpackBlockHash(buf []byte) (string, error)
|
||||||
ParseBlock(b []byte) (*Block, error)
|
ParseBlock(b []byte) (*Block, error)
|
||||||
|
// xpub
|
||||||
|
DeriveAddressDescriptors(xpub string, change uint32, fromIndex uint32, toIndex uint32) ([]AddressDescriptor, error)
|
||||||
// EthereumType specific
|
// EthereumType specific
|
||||||
EthereumTypeGetErc20FromTx(tx *Tx) ([]Erc20Transfer, error)
|
EthereumTypeGetErc20FromTx(tx *Tx) ([]Erc20Transfer, error)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user