Some updates to NULS (#183)
parent
3e67307a5d
commit
a4e3db6fbb
|
@ -6,12 +6,13 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
vlq "github.com/bsm/go-vlq"
|
vlq "github.com/bsm/go-vlq"
|
||||||
"github.com/martinboehm/btcutil/base58"
|
"github.com/martinboehm/btcutil/base58"
|
||||||
|
|
||||||
"github.com/martinboehm/btcd/wire"
|
"github.com/martinboehm/btcd/wire"
|
||||||
"github.com/martinboehm/btcutil/chaincfg"
|
"github.com/martinboehm/btcutil/chaincfg"
|
||||||
|
"github.com/martinboehm/btcutil/hdkeychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// magic numbers
|
// magic numbers
|
||||||
|
@ -19,6 +20,8 @@ const (
|
||||||
MainnetMagic wire.BitcoinNet = 0xbd6b0cbf
|
MainnetMagic wire.BitcoinNet = 0xbd6b0cbf
|
||||||
TestnetMagic wire.BitcoinNet = 0xffcae2ce
|
TestnetMagic wire.BitcoinNet = 0xffcae2ce
|
||||||
RegtestMagic wire.BitcoinNet = 0xdcb7c1fc
|
RegtestMagic wire.BitcoinNet = 0xdcb7c1fc
|
||||||
|
|
||||||
|
AddressHashLength = 24
|
||||||
)
|
)
|
||||||
|
|
||||||
// chain parameters
|
// chain parameters
|
||||||
|
@ -33,8 +36,11 @@ func init() {
|
||||||
MainNetParams.Net = MainnetMagic
|
MainNetParams.Net = MainnetMagic
|
||||||
|
|
||||||
// Address encoding magics
|
// Address encoding magics
|
||||||
MainNetParams.PubKeyHashAddrID = []byte{38} // base58 prefix: G
|
MainNetParams.AddressMagicLen = 3
|
||||||
MainNetParams.ScriptHashAddrID = []byte{10} // base58 prefix: W
|
|
||||||
|
// 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 = chaincfg.TestNet3Params
|
||||||
TestNetParams.Net = TestnetMagic
|
TestNetParams.Net = TestnetMagic
|
||||||
|
@ -153,3 +159,46 @@ func (p *NulsParser) ParseTx(b []byte) (*bchain.Tx, error) {
|
||||||
}
|
}
|
||||||
return &tx, err
|
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"
|
"blockbook/bchain/coins/btc"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/martinboehm/btcutil/hdkeychain"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"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
|
return blockInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NulsRPC) GetMempool() ([]string, error) {
|
func (n *NulsRPC) GetMempoolTransactions() ([]string, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,14 +492,6 @@ func (n *NulsRPC) SendRawTransaction(tx string) (string, error) {
|
||||||
return broadcast.Data.Value, nil
|
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
|
// Call calls Backend RPC interface, using RPCMarshaler interface to marshall the request
|
||||||
func (b *NulsRPC) Call(uri string, res interface{}) error {
|
func (b *NulsRPC) Call(uri string, res interface{}) error {
|
||||||
url := b.rpcURL + uri
|
url := b.rpcURL + uri
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
"mempool_sub_workers": 8,
|
"mempool_sub_workers": 8,
|
||||||
"block_addresses_to_keep": 300,
|
"block_addresses_to_keep": 300,
|
||||||
"xpub_magic": 76067358,
|
"xpub_magic": 76067358,
|
||||||
"slip44": 133,
|
"slip44": 8964,
|
||||||
"additional_params": {}
|
"additional_params": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -161,4 +161,4 @@
|
||||||
"EstimateSmartFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
|
"EstimateSmartFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
|
||||||
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
|
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue