Some updates to NULS (#183)

pull/189/head
kiss1987f4 2019-05-28 07:02:38 +08:00 committed by Martin
parent 3e67307a5d
commit a4e3db6fbb
5 changed files with 225 additions and 14 deletions

View File

@ -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
}

View File

@ -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
}
}
})
}
}

View File

@ -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

View File

@ -56,7 +56,7 @@
"mempool_sub_workers": 8,
"block_addresses_to_keep": 300,
"xpub_magic": 76067358,
"slip44": 133,
"slip44": 8964,
"additional_params": {}
}
},

View File

@ -161,4 +161,4 @@
"EstimateSmartFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
}
}
}