From a4e3db6fbbabba0ae5890205e413d8b50c8da74d Mon Sep 17 00:00:00 2001 From: kiss1987f4 <156755027@qq.com> Date: Tue, 28 May 2019 07:02:38 +0800 Subject: [PATCH] Some updates to NULS (#183) --- bchain/coins/nuls/nulsparser.go | 55 ++++++++- bchain/coins/nuls/nulsparser_test.go | 170 +++++++++++++++++++++++++++ bchain/coins/nuls/nulsrpc.go | 10 +- configs/coins/nuls.json | 2 +- tests/tests.json | 2 +- 5 files changed, 225 insertions(+), 14 deletions(-) diff --git a/bchain/coins/nuls/nulsparser.go b/bchain/coins/nuls/nulsparser.go index bf5d8c9b..5f9e0905 100644 --- a/bchain/coins/nuls/nulsparser.go +++ b/bchain/coins/nuls/nulsparser.go @@ -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 +} diff --git a/bchain/coins/nuls/nulsparser_test.go b/bchain/coins/nuls/nulsparser_test.go index 614000cd..058e2db9 100644 --- a/bchain/coins/nuls/nulsparser_test.go +++ b/bchain/coins/nuls/nulsparser_test.go @@ -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 + } + } + }) + } + +} diff --git a/bchain/coins/nuls/nulsrpc.go b/bchain/coins/nuls/nulsrpc.go index d800b94d..ffca179d 100644 --- a/bchain/coins/nuls/nulsrpc.go +++ b/bchain/coins/nuls/nulsrpc.go @@ -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 diff --git a/configs/coins/nuls.json b/configs/coins/nuls.json index 29ce6308..9cd2de33 100644 --- a/configs/coins/nuls.json +++ b/configs/coins/nuls.json @@ -56,7 +56,7 @@ "mempool_sub_workers": 8, "block_addresses_to_keep": 300, "xpub_magic": 76067358, - "slip44": 133, + "slip44": 8964, "additional_params": {} } }, diff --git a/tests/tests.json b/tests/tests.json index a49a29b8..18bc848a 100644 --- a/tests/tests.json +++ b/tests/tests.json @@ -161,4 +161,4 @@ "EstimateSmartFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"], "sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"] } -} +} \ No newline at end of file