Some updates to NULS (#183)
parent
3e67307a5d
commit
a4e3db6fbb
|
@ -6,12 +6,13 @@ import (
|
|||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
|
||||
"errors"
|
||||
vlq "github.com/bsm/go-vlq"
|
||||
"github.com/martinboehm/btcutil/base58"
|
||||
|
||||
"github.com/martinboehm/btcd/wire"
|
||||
"github.com/martinboehm/btcutil/chaincfg"
|
||||
"github.com/martinboehm/btcutil/hdkeychain"
|
||||
)
|
||||
|
||||
// magic numbers
|
||||
|
@ -19,6 +20,8 @@ const (
|
|||
MainnetMagic wire.BitcoinNet = 0xbd6b0cbf
|
||||
TestnetMagic wire.BitcoinNet = 0xffcae2ce
|
||||
RegtestMagic wire.BitcoinNet = 0xdcb7c1fc
|
||||
|
||||
AddressHashLength = 24
|
||||
)
|
||||
|
||||
// chain parameters
|
||||
|
@ -33,8 +36,11 @@ func init() {
|
|||
MainNetParams.Net = MainnetMagic
|
||||
|
||||
// Address encoding magics
|
||||
MainNetParams.PubKeyHashAddrID = []byte{38} // base58 prefix: G
|
||||
MainNetParams.ScriptHashAddrID = []byte{10} // base58 prefix: W
|
||||
MainNetParams.AddressMagicLen = 3
|
||||
|
||||
// Address encoding magics
|
||||
MainNetParams.PubKeyHashAddrID = []byte{4, 35, 1} // base58 prefix: Ns
|
||||
MainNetParams.ScriptHashAddrID = []byte{4, 35, 1} // base58 prefix: Ns
|
||||
|
||||
TestNetParams = chaincfg.TestNet3Params
|
||||
TestNetParams.Net = TestnetMagic
|
||||
|
@ -153,3 +159,46 @@ func (p *NulsParser) ParseTx(b []byte) (*bchain.Tx, error) {
|
|||
}
|
||||
return &tx, err
|
||||
}
|
||||
|
||||
// DeriveAddressDescriptorsFromTo derives address descriptors from given xpub for addresses in index range
|
||||
func (p *NulsParser) DeriveAddressDescriptorsFromTo(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, p.Params.Base58CksumHasher)
|
||||
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
|
||||
}
|
||||
s, err := indexExtKey.Address(p.Params)
|
||||
|
||||
if err != nil && indexExtKey != nil {
|
||||
return nil, err
|
||||
}
|
||||
addHashs := make([]byte, AddressHashLength)
|
||||
copy(addHashs[0:3], p.Params.PubKeyHashAddrID)
|
||||
copy(addHashs[3:], s.ScriptAddress())
|
||||
copy(addHashs[23:], []byte{p.xor(addHashs[0:23])})
|
||||
|
||||
//addressStr := base58.Encode(addHashs)
|
||||
ad[index-fromIndex] = addHashs
|
||||
}
|
||||
return ad, nil
|
||||
}
|
||||
|
||||
func (p *NulsParser) xor(body []byte) byte {
|
||||
var xor byte = 0x00
|
||||
for i := 0; i < len(body); i++ {
|
||||
xor ^= body[i]
|
||||
}
|
||||
return xor
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"blockbook/bchain/coins/btc"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/martinboehm/btcutil/hdkeychain"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
@ -341,3 +343,171 @@ func TestUnpackTx(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeriveAddressDescriptorsFromTo(t *testing.T) {
|
||||
|
||||
parser := NewNulsParser(GetChainParams("main"), &btc.Configuration{})
|
||||
|
||||
// test xpub xprv math ,and get private key
|
||||
xprv := "xprv9yEvwSfPanK5gLYVnYvNyF2CEWJx1RsktQtKDeT6jnCnqASBiPCvFYHFSApXv39bZbF6hRaha1kWQBVhN1xjo7NHuhAn5uUfzy79TBuGiHh"
|
||||
xpub := "xpub6CEHLxCHR9sNtpcxtaTPLNxvnY9SQtbcFdov22riJ7jmhxmLFvXAoLbjHSzwXwNNuxC1jUP6tsHzFV9rhW9YKELfmR9pJaKFaM8C3zMPgjw"
|
||||
extKey, err := hdkeychain.NewKeyFromString(xprv, parser.Params.Base58CksumHasher)
|
||||
if err != nil {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() error = %v", err)
|
||||
return
|
||||
}
|
||||
changeExtKey, err := extKey.Child(0)
|
||||
if err != nil {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() error = %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
key1, err := changeExtKey.Child(0)
|
||||
priKey1, _ := key1.ECPrivKey()
|
||||
wantPriKey1 := "0x995c98115809359eb57a5e179558faddd55ef88f88e5cf58617a5f9f3d6bb3a1"
|
||||
if !reflect.DeepEqual(hexutil.MustDecode(wantPriKey1), priKey1.Serialize()) {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() %v, want %v", wantPriKey1, hexutil.Encode(priKey1.Serialize()))
|
||||
return
|
||||
}
|
||||
pubKey1, _ := key1.ECPubKey()
|
||||
wantPubKey1 := "0x028855d37e8b1d2760289ea51996df05f3297d86fae4e113aea696a0f02a420ae2"
|
||||
if !reflect.DeepEqual(hexutil.MustDecode(wantPubKey1), pubKey1.SerializeCompressed()) {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() %v, want %v", wantPubKey1, hexutil.Encode(pubKey1.SerializeCompressed()))
|
||||
return
|
||||
}
|
||||
|
||||
key2, err := changeExtKey.Child(1)
|
||||
priKey2, _ := key2.ECPrivKey()
|
||||
wantPriKey2 := "0x0f65dee42d3c974c1a4bcc79f141be89715dc8d6406faa9ad4f1f55ca95fabc8"
|
||||
if !reflect.DeepEqual(hexutil.MustDecode(wantPriKey2), priKey2.Serialize()) {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() %v, want %v", wantPriKey2, hexutil.Encode(priKey2.Serialize()))
|
||||
return
|
||||
}
|
||||
pubKey2, _ := key2.ECPubKey()
|
||||
wantPubKey2 := "0x0216f460ea59194464a6c981560e3f52899203496ed8a20f8f9a57a9225d841293"
|
||||
if !reflect.DeepEqual(hexutil.MustDecode(wantPubKey2), pubKey2.SerializeCompressed()) {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() %v, want %v", wantPubKey2, hexutil.Encode(pubKey2.SerializeCompressed()))
|
||||
return
|
||||
}
|
||||
|
||||
key3, err := changeExtKey.Child(2)
|
||||
priKey3, _ := key3.ECPrivKey()
|
||||
wantPriKey3 := "0x6fd98d1d9c3f3ac1ff61bbf3f20e89f00ffa8d43a554f2a7d73fd464b6666f45"
|
||||
if !reflect.DeepEqual(hexutil.MustDecode(wantPriKey3), priKey3.Serialize()) {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() %v, want %v", wantPriKey3, hexutil.Encode(priKey3.Serialize()))
|
||||
return
|
||||
}
|
||||
pubKey3, _ := key3.ECPubKey()
|
||||
wantPubKey3 := "0x0327ef15c2eaf99365610d6ef89d9ad1e89d1ddf888fc0ec7eb8a94d97153ee482"
|
||||
if !reflect.DeepEqual(hexutil.MustDecode(wantPubKey3), pubKey3.SerializeCompressed()) {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() %v, want %v", wantPubKey3, hexutil.Encode(pubKey3.SerializeCompressed()))
|
||||
return
|
||||
}
|
||||
|
||||
key4, err := changeExtKey.Child(3)
|
||||
priKey4, _ := key4.ECPrivKey()
|
||||
wantPriKey4 := "0x21412d9e33aba493faf4bc7d408ed5290bea5b36a7beec554b858051f8d4bff3"
|
||||
if !reflect.DeepEqual(hexutil.MustDecode(wantPriKey4), priKey4.Serialize()) {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() %v, want %v", wantPriKey4, hexutil.Encode(priKey4.Serialize()))
|
||||
return
|
||||
}
|
||||
pubKey4, _ := key4.ECPubKey()
|
||||
wantPubKey4 := "0x02a73aebd08c6f70fa97f616b1c0b63756efe9eb070a14628de3d850b2b970a9a7"
|
||||
if !reflect.DeepEqual(hexutil.MustDecode(wantPubKey4), pubKey4.SerializeCompressed()) {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() %v, want %v", wantPubKey4, hexutil.Encode(pubKey4.SerializeCompressed()))
|
||||
return
|
||||
}
|
||||
|
||||
key5, err := changeExtKey.Child(4)
|
||||
priKey5, _ := key5.ECPrivKey()
|
||||
wantPriKey5 := "0xdc3d290e32a4e0f38bc26c25a78ceb1c8779110883d9cb0be54629043c1f8724"
|
||||
if !reflect.DeepEqual(hexutil.MustDecode(wantPriKey5), priKey5.Serialize()) {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() %v, want %v", wantPriKey5, hexutil.Encode(priKey5.Serialize()))
|
||||
return
|
||||
}
|
||||
pubKey5, _ := key5.ECPubKey()
|
||||
wantPubKey5 := "0x02f87eb70b985a857d7238bc9423dab7d5930f3fcfc2118ccac0634a9342b9d324"
|
||||
if !reflect.DeepEqual(hexutil.MustDecode(wantPubKey5), pubKey5.SerializeCompressed()) {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() %v, want %v", wantPubKey5, hexutil.Encode(pubKey5.SerializeCompressed()))
|
||||
return
|
||||
}
|
||||
|
||||
type args struct {
|
||||
xpub string
|
||||
change uint32
|
||||
fromIndex uint32
|
||||
toIndex uint32
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "test-xpub",
|
||||
args: args{
|
||||
xpub: xpub,
|
||||
change: 0,
|
||||
fromIndex: 0,
|
||||
toIndex: 5,
|
||||
},
|
||||
want: []string{
|
||||
"NsdtwhD8hb8H72J7FyQpGta2sqLngrXZ",
|
||||
"Nse51sBAzRTVtm48wYQLb4TH7MGAHAER",
|
||||
"NsdvoFSwfh1oW238SFM6p5wL4J834Gv2",
|
||||
"Nse4wVWsJ4v3jPcpE4vRkAiZLFyQSNKd",
|
||||
"Nse5NzUcZybsvFQeNgqfuWmmmwCfhdxF",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "test-xprv",
|
||||
args: args{
|
||||
xpub: xprv,
|
||||
change: 0,
|
||||
fromIndex: 0,
|
||||
toIndex: 5,
|
||||
},
|
||||
want: []string{
|
||||
"NsdtwhD8hb8H72J7FyQpGta2sqLngrXZ",
|
||||
"Nse51sBAzRTVtm48wYQLb4TH7MGAHAER",
|
||||
"NsdvoFSwfh1oW238SFM6p5wL4J834Gv2",
|
||||
"Nse4wVWsJ4v3jPcpE4vRkAiZLFyQSNKd",
|
||||
"Nse5NzUcZybsvFQeNgqfuWmmmwCfhdxF",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := parser.DeriveAddressDescriptorsFromTo(tt.args.xpub, tt.args.change, tt.args.fromIndex, tt.args.toIndex)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if len(got) != len(tt.want) {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() result count = %v, want %v", len(got), len(tt.want))
|
||||
return
|
||||
}
|
||||
|
||||
for i, add := range tt.want {
|
||||
addStrs, ok, error := parser.GetAddressesFromAddrDesc(got[i])
|
||||
if !ok || error != nil {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() fail %v - %v , %v", i, ok, error)
|
||||
return
|
||||
}
|
||||
if len(addStrs) != 1 {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() len(adds) != 1, %v", addStrs)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(addStrs[0], add) {
|
||||
t.Errorf("DeriveAddressDescriptorsFromTo() of index %v = %v, want %v", i, addStrs, add)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -389,7 +389,7 @@ func (n *NulsRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
|
|||
return blockInfo, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetMempool() ([]string, error) {
|
||||
func (n *NulsRPC) GetMempoolTransactions() ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -492,14 +492,6 @@ func (n *NulsRPC) SendRawTransaction(tx string) (string, error) {
|
|||
return broadcast.Data.Value, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) GetMempoolTransactionsForAddrDesc(addrDesc bchain.AddressDescriptor) ([]bchain.Outpoint, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n *NulsRPC) ResyncMempool(onNewTxAddr bchain.OnNewTxAddrFunc) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Call calls Backend RPC interface, using RPCMarshaler interface to marshall the request
|
||||
func (b *NulsRPC) Call(uri string, res interface{}) error {
|
||||
url := b.rpcURL + uri
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
"mempool_sub_workers": 8,
|
||||
"block_addresses_to_keep": 300,
|
||||
"xpub_magic": 76067358,
|
||||
"slip44": 133,
|
||||
"slip44": 8964,
|
||||
"additional_params": {}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -161,4 +161,4 @@
|
|||
"EstimateSmartFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
|
||||
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue