From d6375a19dd542d1b0c8e699d820bf503c1a898fa Mon Sep 17 00:00:00 2001 From: James Scaur Date: Wed, 21 Aug 2019 18:07:35 +0100 Subject: [PATCH] Add support for DIVI (#228) * Add Divi Project (DIVI) support * Remove PivX comments, add Divi RPC tests * Merge with latest Blockbook state * Fix permissions issue with automatic setup * Recreate integration tests with block 430894 * Remove GetBlockHeader test * Increase frequency of mempool sync --- bchain/coins/blockchain.go | 2 + bchain/coins/divi/diviparser.go | 229 +++++++++++ bchain/coins/divi/diviparser_test.go | 376 +++++++++++++++++++ bchain/coins/divi/divirpc.go | 60 +++ bchain/coins/divi/testdata/block_dump.407407 | 1 + bchain/coins/divi/testdata/block_dump.408074 | 1 + bchain/coins/divi/testdata/block_dump.409054 | 1 + configs/coins/divi.json | 65 ++++ docs/ports.md | 1 + tests/rpc/testdata/divi.json | 52 +++ tests/sync/testdata/divi.json | 112 ++++++ tests/tests.json | 5 + 12 files changed, 905 insertions(+) create mode 100755 bchain/coins/divi/diviparser.go create mode 100755 bchain/coins/divi/diviparser_test.go create mode 100755 bchain/coins/divi/divirpc.go create mode 100755 bchain/coins/divi/testdata/block_dump.407407 create mode 100755 bchain/coins/divi/testdata/block_dump.408074 create mode 100755 bchain/coins/divi/testdata/block_dump.409054 create mode 100644 configs/coins/divi.json create mode 100644 tests/rpc/testdata/divi.json create mode 100644 tests/sync/testdata/divi.json diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 4456ae6a..33ced306 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -9,6 +9,7 @@ import ( "blockbook/bchain/coins/dash" "blockbook/bchain/coins/dcr" "blockbook/bchain/coins/digibyte" + "blockbook/bchain/coins/divi" "blockbook/bchain/coins/dogecoin" "blockbook/bchain/coins/eth" "blockbook/bchain/coins/flo" @@ -97,6 +98,7 @@ func init() { BlockChainFactories["ZelCash"] = zec.NewZCashRPC BlockChainFactories["Ravencoin"] = ravencoin.NewRavencoinRPC BlockChainFactories["Ritocoin"] = ritocoin.NewRitocoinRPC + BlockChainFactories["Divi"] = divi.NewDiviRPC } // GetCoinNameFromConfig gets coin name and coin shortcut from config file diff --git a/bchain/coins/divi/diviparser.go b/bchain/coins/divi/diviparser.go new file mode 100755 index 00000000..1556c3bd --- /dev/null +++ b/bchain/coins/divi/diviparser.go @@ -0,0 +1,229 @@ +package divi + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + "blockbook/bchain/coins/utils" + "bytes" + "io" + + "encoding/hex" + "encoding/json" + + "math/big" + + "github.com/juju/errors" + "github.com/martinboehm/btcd/wire" + "github.com/martinboehm/btcutil/chaincfg" +) + +const ( + // MainnetMagic = "network messages so the messages can be identified to belong to a specific coin" + // Source https://github.com/DiviProject/Divi/blob/master0/divi/src/chainparams.cpp#L128-L136 + MainnetMagic wire.BitcoinNet = 0x8f8da0df +) + +var ( + // MainNetParams = ??? + MainNetParams chaincfg.Params +) + +func init() { + // DIVI mainnet Address encoding magics + MainNetParams = chaincfg.MainNetParams + MainNetParams.Net = MainnetMagic + MainNetParams.PubKeyHashAddrID = []byte{30} // starting with 'D' + MainNetParams.ScriptHashAddrID = []byte{13} + MainNetParams.PrivateKeyID = []byte{212} +} + +// DivicoinParser handle +type DivicoinParser struct { + *btc.BitcoinParser + baseparser *bchain.BaseParser + BitcoinOutputScriptToAddressesFunc btc.OutputScriptToAddressesFunc +} + +// NewDiviParser returns new DivicoinParser instance +func NewDiviParser(params *chaincfg.Params, c *btc.Configuration) *DivicoinParser { + p := &DivicoinParser{ + BitcoinParser: btc.NewBitcoinParser(params, c), + baseparser: &bchain.BaseParser{}, + } + p.BitcoinOutputScriptToAddressesFunc = p.OutputScriptToAddressesFunc + p.OutputScriptToAddressesFunc = p.outputScriptToAddresses + return p +} + +// GetChainParams contains network parameters for the main Divi network +func GetChainParams(chain string) *chaincfg.Params { + if !chaincfg.IsRegistered(&MainNetParams) { + err := chaincfg.Register(&MainNetParams) + /*if err == nil { + err = chaincfg.Register(&TestNetParams) + }*/ + if err != nil { + panic(err) + } + } /* + switch chain { + case "test": + return &TestNetParams + default: + */return &MainNetParams + //} +} + +// ParseBlock parses raw block to our Block struct +func (p *DivicoinParser) ParseBlock(b []byte) (*bchain.Block, error) { + r := bytes.NewReader(b) + w := wire.MsgBlock{} + h := wire.BlockHeader{} + err := h.Deserialize(r) + if err != nil { + return nil, errors.Annotatef(err, "Deserialize") + } + + if h.Version > 3 { + // Skip past AccumulatorCheckpoint which was added in pivx block version 4 + r.Seek(32, io.SeekCurrent) + } + + err = utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w) + if err != nil { + return nil, errors.Annotatef(err, "DecodeTransactions") + } + + txs := make([]bchain.Tx, len(w.Transactions)) + for ti, t := range w.Transactions { + txs[ti] = p.TxFromMsgTx(t, false) + } + + return &bchain.Block{ + BlockHeader: bchain.BlockHeader{ + Size: len(b), + Time: h.Timestamp.Unix(), + }, + Txs: txs, + }, nil +} + +// PackTx packs transaction to byte array using protobuf +func (p *DivicoinParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) { + return p.baseparser.PackTx(tx, height, blockTime) +} + +// UnpackTx unpacks transaction from protobuf byte array +func (p *DivicoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { + return p.baseparser.UnpackTx(buf) +} + +// ParseTx parses byte array containing transaction and returns Tx struct +func (p *DivicoinParser) ParseTx(b []byte) (*bchain.Tx, error) { + t := wire.MsgTx{} + r := bytes.NewReader(b) + if err := t.Deserialize(r); err != nil { + return nil, err + } + tx := p.TxFromMsgTx(&t, true) + tx.Hex = hex.EncodeToString(b) + return &tx, nil +} + +// TxFromMsgTx parses tx and adds handling for OP_ZEROCOINSPEND inputs +func (p *DivicoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.Tx { + vin := make([]bchain.Vin, len(t.TxIn)) + for i, in := range t.TxIn { + s := bchain.ScriptSig{ + Hex: hex.EncodeToString(in.SignatureScript), + // missing: Asm, + } + + txid := in.PreviousOutPoint.Hash.String() + + vin[i] = bchain.Vin{ + Txid: txid, + Vout: in.PreviousOutPoint.Index, + Sequence: in.Sequence, + ScriptSig: s, + } + } + vout := make([]bchain.Vout, len(t.TxOut)) + for i, out := range t.TxOut { + addrs := []string{} + if parseAddresses { + addrs, _, _ = p.OutputScriptToAddressesFunc(out.PkScript) + } + s := bchain.ScriptPubKey{ + Hex: hex.EncodeToString(out.PkScript), + Addresses: addrs, + // missing: Asm, + // missing: Type, + } + var vs big.Int + vs.SetInt64(out.Value) + vout[i] = bchain.Vout{ + ValueSat: vs, + N: uint32(i), + ScriptPubKey: s, + } + } + tx := bchain.Tx{ + Txid: t.TxHash().String(), + Version: t.Version, + LockTime: t.LockTime, + Vin: vin, + Vout: vout, + // skip: BlockHash, + // skip: Confirmations, + // skip: Time, + // skip: Blocktime, + } + return tx +} + +// ParseTxFromJSON parses JSON message containing transaction and returns Tx struct +func (p *DivicoinParser) ParseTxFromJSON(msg json.RawMessage) (*bchain.Tx, error) { + var tx bchain.Tx + err := json.Unmarshal(msg, &tx) + if err != nil { + return nil, err + } + + for i := range tx.Vout { + vout := &tx.Vout[i] + // convert vout.JsonValue to big.Int and clear it, it is only temporary value used for unmarshal + vout.ValueSat, err = p.AmountToBigInt(vout.JsonValue) + if err != nil { + return nil, err + } + vout.JsonValue = "" + + if vout.ScriptPubKey.Addresses == nil { + vout.ScriptPubKey.Addresses = []string{} + } + } + + return &tx, nil +} + +// outputScriptToAddresses converts ScriptPubKey to bitcoin addresses +func (p *DivicoinParser) outputScriptToAddresses(script []byte) ([]string, bool, error) { + rv, s, _ := p.BitcoinOutputScriptToAddressesFunc(script) + return rv, s, nil +} + +// GetAddrDescForUnknownInput = ??? +func (p *DivicoinParser) GetAddrDescForUnknownInput(tx *bchain.Tx, input int) bchain.AddressDescriptor { + if len(tx.Vin) > input { + scriptHex := tx.Vin[input].ScriptSig.Hex + + if scriptHex != "" { + script, _ := hex.DecodeString(scriptHex) + return script + } + } + + s := make([]byte, 10) + return s +} diff --git a/bchain/coins/divi/diviparser_test.go b/bchain/coins/divi/diviparser_test.go new file mode 100755 index 00000000..3250f226 --- /dev/null +++ b/bchain/coins/divi/diviparser_test.go @@ -0,0 +1,376 @@ +// +build unittest + +package divi + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + "bytes" + "encoding/hex" + "fmt" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "reflect" + "testing" + + "github.com/martinboehm/btcutil/chaincfg" +) + +func TestMain(m *testing.M) { + c := m.Run() + chaincfg.ResetParams() + os.Exit(c) +} + +// Test getting the address details from the address hash + +func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) { + type args struct { + address string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "P2PKH1", + args: args{address: "DDSsBchWiVfvPVn6Ldp1nL7k4L77cSDqM7"}, + want: "76a9145b1d583a4c270f2f14be77b298f0a9c6df97471388ac", + wantErr: false, + }, + } + parser := NewDiviParser(GetChainParams("main"), &btc.Configuration{}) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parser.GetAddrDescFromAddress(tt.args.address) + if (err != nil) != tt.wantErr { + t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr) + return + } + h := hex.EncodeToString(got) + if !reflect.DeepEqual(h, tt.want) { + t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want) + } + }) + } +} + +func Test_GetAddressesFromAddrDesc(t *testing.T) { + type args struct { + script string + } + tests := []struct { + name string + args args + want []string + want2 bool + wantErr bool + }{ + { + name: "Normal", + args: args{script: "76a914cb1196fb1b98d04b0cb8d2ffde3c2de3eb83d9fe88ac"}, + want: []string{"DPepnMkaNHKCa6cQi7oBThrdiFEwSSYFzv"}, + want2: true, + wantErr: false, + }, + } + + parser := NewDiviParser(GetChainParams("main"), &btc.Configuration{}) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b, _ := hex.DecodeString(tt.args.script) + got, got2, err := parser.GetAddressesFromAddrDesc(b) + if (err != nil) != tt.wantErr { + t.Errorf("GetAddressesFromAddrDesc() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got, tt.want) + } + if !reflect.DeepEqual(got2, tt.want2) { + t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got2, tt.want2) + } + }) + } +} + +// Test the packing and unpacking of raw transaction data + +var ( + // Mint transaction + testTx1 bchain.Tx + testTxPacked1 = "0a20f7a5324866ba18058ab032196f34458d19f7ec5a4ac284670c3ef07bfa724644124201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0603de3d060101ffffffff010000000000000000000000000018aefd9ce905200028defb1832160a0c303364653364303630313031180028ffffffff0f3a0210004000" + + // Normal transaction + testTx2 bchain.Tx + testTxPacked2 = "0a20eace41778a2940ff423b72a42033990eb5d6092810734a5806da6f3e5b34086412ea010100000001084b029489e1cddf726080c447c8a2b1d4bbe43024db31b8b19bc07585db9555010000006a473044022017422b9e3414d6233fa75f9eb7778469bebbb40686b0f7eb77d90a04c80149610220411f1063086fe205ea821ceb0de89e8158e202aba00f5ebb92b51f97381311fd012102ccb10a2f0603a0624b8708abefb5f4700631fc131c5de38b51e0359e2ffa7d1cffffffff03000000000000000000f260de1a580100001976a9145b1d583a4c270f2f14be77b298f0a9c6df97471388ac009ca6920c0000001976a914cb1196fb1b98d04b0cb8d2ffde3c2de3eb83d9fe88ac0000000018aefd9ce905200028defb183298010a0012205595db8575c09bb1b831db2430e4bbd4b1a2c847c4806072dfcde18994024b081801226a473044022017422b9e3414d6233fa75f9eb7778469bebbb40686b0f7eb77d90a04c80149610220411f1063086fe205ea821ceb0de89e8158e202aba00f5ebb92b51f97381311fd012102ccb10a2f0603a0624b8708abefb5f4700631fc131c5de38b51e0359e2ffa7d1c28ffffffff0f3a0210003a490a0601581ade60f210011a1976a9145b1d583a4c270f2f14be77b298f0a9c6df97471388ac222244445373426368576956667650566e364c6470316e4c376b344c3737635344714d373a480a050c92a69c0010021a1976a914cb1196fb1b98d04b0cb8d2ffde3c2de3eb83d9fe88ac2222445065706e4d6b614e484b436136635169376f425468726469464577535359467a764000" +) + +func init() { + testTx1 = bchain.Tx{ + Hex: "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0603de3d060101ffffffff0100000000000000000000000000", + Txid: "f7a5324866ba18058ab032196f34458d19f7ec5a4ac284670c3ef07bfa724644", + LockTime: 0, + Vin: []bchain.Vin{ + { + Coinbase: "03de3d060101", + Sequence: 4294967295, + }, + }, + Vout: []bchain.Vout{ + { + ValueSat: *big.NewInt(0), + N: 0, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "", + }, + }, + }, + Blocktime: 1562853038, + Time: 1562853038, + } + + testTx2 = bchain.Tx{ + Hex: "0100000001084b029489e1cddf726080c447c8a2b1d4bbe43024db31b8b19bc07585db9555010000006a473044022017422b9e3414d6233fa75f9eb7778469bebbb40686b0f7eb77d90a04c80149610220411f1063086fe205ea821ceb0de89e8158e202aba00f5ebb92b51f97381311fd012102ccb10a2f0603a0624b8708abefb5f4700631fc131c5de38b51e0359e2ffa7d1cffffffff03000000000000000000f260de1a580100001976a9145b1d583a4c270f2f14be77b298f0a9c6df97471388ac009ca6920c0000001976a914cb1196fb1b98d04b0cb8d2ffde3c2de3eb83d9fe88ac00000000", + Txid: "eace41778a2940ff423b72a42033990eb5d6092810734a5806da6f3e5b340864", + LockTime: 0, + Vin: []bchain.Vin{ + { + ScriptSig: bchain.ScriptSig{ + Hex: "473044022017422b9e3414d6233fa75f9eb7778469bebbb40686b0f7eb77d90a04c80149610220411f1063086fe205ea821ceb0de89e8158e202aba00f5ebb92b51f97381311fd012102ccb10a2f0603a0624b8708abefb5f4700631fc131c5de38b51e0359e2ffa7d1c", + }, + Txid: "5595db8575c09bb1b831db2430e4bbd4b1a2c847c4806072dfcde18994024b08", + Vout: 1, + Sequence: 4294967295, + }, + }, + Vout: []bchain.Vout{ + { + ValueSat: *big.NewInt(0), + N: 0, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "", + }, + }, + { + ValueSat: *big.NewInt(1477919531250), + N: 1, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "76a9145b1d583a4c270f2f14be77b298f0a9c6df97471388ac", + Addresses: []string{ + "DDSsBchWiVfvPVn6Ldp1nL7k4L77cSDqM7", + }, + }, + }, + { + ValueSat: *big.NewInt(54000000000), + N: 2, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "76a914cb1196fb1b98d04b0cb8d2ffde3c2de3eb83d9fe88ac", + Addresses: []string{ + "DPepnMkaNHKCa6cQi7oBThrdiFEwSSYFzv", + }, + }, + }, + }, + Blocktime: 1562853038, + Time: 1562853038, + } +} + +func Test_PackTx(t *testing.T) { + type args struct { + tx bchain.Tx + height uint32 + blockTime int64 + parser *DivicoinParser + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "divi-1", + args: args{ + tx: testTx1, + height: 409054, + blockTime: 1562853038, + parser: NewDiviParser(GetChainParams("main"), &btc.Configuration{}), + }, + want: testTxPacked1, + wantErr: false, + }, + { + name: "divi-2", + args: args{ + tx: testTx2, + height: 409054, + blockTime: 1562853038, + parser: NewDiviParser(GetChainParams("main"), &btc.Configuration{}), + }, + want: testTxPacked2, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.args.parser.PackTx(&tt.args.tx, tt.args.height, tt.args.blockTime) + if (err != nil) != tt.wantErr { + t.Errorf("packTx() error = %v, wantErr %v", err, tt.wantErr) + return + } + h := hex.EncodeToString(got) + if !reflect.DeepEqual(h, tt.want) { + t.Errorf("packTx() = %v, want %v", h, tt.want) + } + }) + } +} + +func Test_UnpackTx(t *testing.T) { + type args struct { + packedTx string + parser *DivicoinParser + } + tests := []struct { + name string + args args + want *bchain.Tx + want1 uint32 + wantErr bool + }{ + { + name: "divi-1", + args: args{ + packedTx: testTxPacked1, + parser: NewDiviParser(GetChainParams("main"), &btc.Configuration{}), + }, + want: &testTx1, + want1: 409054, + wantErr: false, + }, + { + name: "divi-2", + args: args{ + packedTx: testTxPacked2, + parser: NewDiviParser(GetChainParams("main"), &btc.Configuration{}), + }, + want: &testTx2, + want1: 409054, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b, _ := hex.DecodeString(tt.args.packedTx) + got, got1, err := tt.args.parser.UnpackTx(b) + if (err != nil) != tt.wantErr { + t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("unpackTx() got = %v, want %v", got, tt.want) + } + if got1 != tt.want1 { + t.Errorf("unpackTx() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} + +// Block test - looks for size, time, and transaction hashes + +type testBlock struct { + size int + time int64 + tx []string +} + +var testParseBlockTxs = map[int]testBlock{ + 407407: { + size: 479, + time: 1562753629, + tx: []string{ + "3f8f01aec6717ede0e167f267fe486f18ddd25a13afd910dc1d41537aa1c6658", + "b25224449d0f5266073876e924c4d6a4f127175aae151a66db6619e4ca41fe1d", + }, + }, + 409054: { + size: 479, + time: 1562853038, + tx: []string{ + "f7a5324866ba18058ab032196f34458d19f7ec5a4ac284670c3ef07bfa724644", + "eace41778a2940ff423b72a42033990eb5d6092810734a5806da6f3e5b340864", + }, + }, + 408074: { + size: 1303, + time: 1562794078, + tx: []string{ + "bf0004680570d49eefab2ab806bd41f99587b6f3e65d1e0fb1d8e8f766f211f3", + "8a334d86443d5e54d3d112b7ab4eff79ed0b879cbc62c580beee080b3c9e1142", + "1ba350ba68b8db6af589136a85246c961694434ec2ffd1ad9c86831965b96932", + "e05dcfece505455e8b4bcaeeb9ae1060fcf9c95ad1402c4fbd3b2c2bf1778683", + "d3980118dedde2666d5bcd03ebf2c2d91ad6056404503afe0c37ed6cdd549f62", + }, + }, +} + +func helperLoadBlock(t *testing.T, height int) []byte { + name := fmt.Sprintf("block_dump.%d", height) + path := filepath.Join("testdata", name) + + d, err := ioutil.ReadFile(path) + if err != nil { + t.Fatal(err) + } + + d = bytes.TrimSpace(d) + + b := make([]byte, hex.DecodedLen(len(d))) + _, err = hex.Decode(b, d) + if err != nil { + t.Fatal(err) + } + + return b +} + +func TestParseBlock(t *testing.T) { + p := NewDiviParser(GetChainParams("main"), &btc.Configuration{}) + + for height, tb := range testParseBlockTxs { + b := helperLoadBlock(t, height) + + blk, err := p.ParseBlock(b) + if err != nil { + t.Fatal(err) + } + + if blk.Size != tb.size { + t.Errorf("ParseBlock() block size: got %d, want %d", blk.Size, tb.size) + } + + if blk.Time != tb.time { + t.Errorf("ParseBlock() block time: got %d, want %d", blk.Time, tb.time) + } + + if len(blk.Txs) != len(tb.tx) { + t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(tb.tx)) + } + + for ti, tx := range tb.tx { + if blk.Txs[ti].Txid != tx { + t.Errorf("ParseBlock() transaction %d: got %s, want %s", ti, blk.Txs[ti].Txid, tx) + } + } + } +} diff --git a/bchain/coins/divi/divirpc.go b/bchain/coins/divi/divirpc.go new file mode 100755 index 00000000..a998d880 --- /dev/null +++ b/bchain/coins/divi/divirpc.go @@ -0,0 +1,60 @@ +package divi + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + "encoding/json" + + "github.com/golang/glog" +) + +// DivicoinRPC is an interface to JSON-RPC bitcoind service. +type DivicoinRPC struct { + *btc.BitcoinRPC +} + +// NewDiviRPC returns new DivicoinRPC instance. +func NewDiviRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) { + b, err := btc.NewBitcoinRPC(config, pushHandler) + if err != nil { + return nil, err + } + + s := &DivicoinRPC{ + b.(*btc.BitcoinRPC), + } + s.RPCMarshaler = btc.JSONMarshalerV1{} + s.ChainConfig.SupportsEstimateFee = true + s.ChainConfig.SupportsEstimateSmartFee = false + + return s, nil +} + +// Initialize initializes DivicoinRPC instance. +func (b *DivicoinRPC) Initialize() error { + ci, err := b.GetChainInfo() + if err != nil { + return err + } + chainName := ci.Chain + + glog.Info("Chain name ", chainName) + params := GetChainParams(chainName) + + // always create parser + b.Parser = NewDiviParser(params, b.ChainConfig) + + /* parameters for getInfo request + -- can be added later + if params.Net == MainnetMagic {*/ + b.Testnet = false + b.Network = "livenet" + /*} else { + b.Testnet = true + b.Network = "testnet" + }*/ + + glog.Info("rpc: block chain ", params.Name) + + return nil +} diff --git a/bchain/coins/divi/testdata/block_dump.407407 b/bchain/coins/divi/testdata/block_dump.407407 new file mode 100755 index 00000000..227b0be5 --- /dev/null +++ b/bchain/coins/divi/testdata/block_dump.407407 @@ -0,0 +1 @@ +04000000881f4e2a409a1b75c62ed6fdc6fa278b00f304bbe0729853bc336cbc30c4931da66efc4bd6260f95886061747185eace5294209dbe390fa3408534e42a0f5d525dba255dfe78101b0000000000000000000000000000000000000000000000000000000000000000000000000201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff06036f37060101ffffffff01000000000000000000000000000100000001652779264ccc941129adb833b8a42a6d278a98f295d25a0a53868ab1dc13bed4020000006a473044022046c6ab760d25c7d94f20aa2c81cc281a57bb3fa174023dcaac385727ab05f214022056841e947398a1d86bf7d0b99191f7d3421d0abf6ad1357955d38f5fe6c3a8c40121023c85ccefc390984ba8f71a0f0b999b7b6c6e9a30ccd63087d67d34d5c1fefc7dffffffff0300000000000000000080170a4e830000001976a914771f45c78657c739f0cc26dfc0b5a87ddd6e431c88ac009ca6920c0000001976a914e7c53cbb09860c3713b44958dd644e4d9154345688ac00000000412066000b291e3e3fd8f1fce0812ba4aaad2502a702fd3466a3d8a7dc1a78c816be01209f1179d41f7d38da229f1843a9c19cf67a9d610c2a767979ea31b264f3eb diff --git a/bchain/coins/divi/testdata/block_dump.408074 b/bchain/coins/divi/testdata/block_dump.408074 new file mode 100755 index 00000000..bb91b1fd --- /dev/null +++ b/bchain/coins/divi/testdata/block_dump.408074 @@ -0,0 +1 @@ +040000000ff149a32a715106e7023cd41193d7ea64ca0092edb4659f035a881fc452b845e9249f2c4463db2074e307ba62e93619fd3d17a3b6b7437a326a79869f35c00c5e58265db760101b0000000000000000000000000000000000000000000000000000000000000000000000000501000000010000000000000000000000000000000000000000000000000000000000000000ffffffff06030a3a060101ffffffff01000000000000000000000000000100000001fdb6b4d9d9aa8afbdaac2d9bc4c29371147a6a3672529ad7ae45a7944558ec93020000006a473044022061bc3ffe2b069a39acdba6a169ae4fbaa34b32360ff9a45fce9c92eb0434ca6602206fa6c22110f4a14c8cee58e458e3522f41993e461278ef8ccb94322595981ec1012103b2e3a485f7a7da6eb16ea025ef45efa613d18350b2538899fab0b5a2e86ffda3ffffffff0300000000000000000000a882a22d0000001976a914899ef2e7669830485476f9c910a8330959d245e188ac009ca6920c0000001976a914a9147e5ce1507c2f49d9ceb8bbd2d280fb410b8188ac000000000100000002a0a25308b7afb81327a4a8cb1d13aa54fa53edf292bdf7fe2739f297f4027bb4000000006b4830450221009396b072158c158e7e90cdd797a0a58caf15c7e1589030d542a32e3568324a22022063d90727f3a71a9c01f75ece872ee0240f46eeac14531a7f2bfdb3fe5d0e793f01210263e67be5854349c3c8e23fea5020aa2b7bfd46916ce4e805676e4cd3b375cda5ffffffff8c7745969fbcd3bfb698d51cc8b110fe540d6a0ed9816bd2c1e375f0e7c2ec94000000006a4730440220748c808b1107ecf01559159c76163a2206af771604643aefea438f3c99c699a0022006c067c603fb0b4bc7a5cd129750fc010613957ec0666c41185d035b6f5fcbb1012102a535454382f62235104836bbbe1c894f1d6705c828b8be11f623068159882eb0ffffffff022018e505000000001976a914bba57e3d30f892d804d588ee633dcbb6a96b0cbb88ac0010a5d4e80000001976a914dd40b08afb0dc8496892bfe5e3ae1aceda57135b88ac000000000100000001583f174f32c563eb47555fbacdb0937881b4a25f35fe5f13d8c49cbf92654fdd000000006b483045022100cbacf5f775ab4fabdfb147c095c0865dd56bc461115b03a7a29ab96037fc4b3f02206b824c334b950d856550fff524717e07095fa2d9bbe8d5d8096813721d98715c012103f4e09f09921ad224e9e590df477cea01fc8114e816a2112ed59518bf99cdaa3effffffff0200f2052a010000001976a9148226210ccf912fb3969c0996a5582dcfb99a2e1788ac60b66daee00000001976a914d649408521dfef8449203417d77e44eadfc329f588ac00000000010000000199bd849243dea120d7d7df9037caa11327ed9df671e90c06442d12859ef4c052000000006a4730440220101b4e2022b3ffa023b83197d8ceb45b3ef31ff2da5eba7a6f7f914448397c31022023cb7908eafab080da4ff37460feb3c72e1ac6ecdb052d2df0c69a3954ec44550121026fd3594eb77ded5de88f54a528950659822859472fcee072a59657032aaf6fe2ffffffff02f071dee22c0000001976a914a316f474589a2f2013bd60b7d0ffb735e33915d188ac00e1823e0c0100001976a914ee628c1d309de0e0d3033d047b19921563f97ee288ac00000000411f3b6a2aabf7c3ccdd2e4d30a581a53670b6bc623e324eb3416100dfe64b3c54a84567bace9246f935b310e026c137eb7ec9836dfb46854aa6695af50a1ac5dd64 diff --git a/bchain/coins/divi/testdata/block_dump.409054 b/bchain/coins/divi/testdata/block_dump.409054 new file mode 100755 index 00000000..f060ec93 --- /dev/null +++ b/bchain/coins/divi/testdata/block_dump.409054 @@ -0,0 +1 @@ +04000000c7fed5984ba7d11c7e4865c1d41ad1755a1082782d81a27fc08888bc749881e9e709a5bb1daf5a76582def89c13eba5e7c4cf8104407ecf81f74be6ba18e89c6ae3e275da61b0d1b0000000000000000000000000000000000000000000000000000000000000000000000000201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0603de3d060101ffffffff01000000000000000000000000000100000001084b029489e1cddf726080c447c8a2b1d4bbe43024db31b8b19bc07585db9555010000006a473044022017422b9e3414d6233fa75f9eb7778469bebbb40686b0f7eb77d90a04c80149610220411f1063086fe205ea821ceb0de89e8158e202aba00f5ebb92b51f97381311fd012102ccb10a2f0603a0624b8708abefb5f4700631fc131c5de38b51e0359e2ffa7d1cffffffff03000000000000000000f260de1a580100001976a9145b1d583a4c270f2f14be77b298f0a9c6df97471388ac009ca6920c0000001976a914cb1196fb1b98d04b0cb8d2ffde3c2de3eb83d9fe88ac00000000411f90bba5b16a087a5b7e0b8fc2aba041d81d74d270b502b24ea5801b5f4f975bba382260a6fd247facbb49acb8f304324deb3c1e00060d10a4958c047e4530c9fa diff --git a/configs/coins/divi.json b/configs/coins/divi.json new file mode 100644 index 00000000..d07c9d97 --- /dev/null +++ b/configs/coins/divi.json @@ -0,0 +1,65 @@ +{ + "coin": { + "name": "Divi", + "shortcut": "DIVI", + "label": "Divi Project", + "alias": "divi" + }, + "ports": { + "backend_rpc": 8089, + "backend_message_queue": 38389, + "blockbook_internal": 9089, + "blockbook_public": 9189 + }, + "ipc": { + "rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_user": "divirpc", + "rpc_pass": "divipass", + "rpc_timeout": 25, + "message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}" + }, + "backend": { + "package_name": "backend-divi", + "package_revision": "satoshilabs-1", + "system_user": "divi", + "version": "1.0.4", + "binary_url": "https://github.com/DiviProject/Divi/releases/download/v1.0.4-core/divi_ubuntu.zip", + "extract_command": "unzip -j -d backend", + "exclude_files": [ + "divi-cli", + "divi-tx" + ], + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/divid -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/*.log", + "postinst_script_template": "chmod a+x {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/divid", + "service_type": "forking", + "service_additional_params_template": "", + "protect_memory": false, + "mainnet": true, + "server_config_file": "bitcoin_like.conf", + "client_config_file": "bitcoin_like_client.conf", + "additional_params": { + "rpcallowip": "127.0.0.1" + } + }, + "blockbook": { + "package_name": "blockbook-divi", + "system_user": "blockbook-divi", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "https://chainz.cryptoid.info/divi/", + "additional_params": "-resyncindexperiod=30011 -resyncmempoolperiod=2011", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "slip44": 301, + "additional_params": {} + } + }, + "meta": { + "package_maintainer": "James Scaur", + "package_maintainer_email": "james@txbatch.com" + } +} diff --git a/docs/ports.md b/docs/ports.md index e76a233e..7a172857 100644 --- a/docs/ports.md +++ b/docs/ports.md @@ -36,6 +36,7 @@ | Flo | 9066 | 9166 | 8066 | 38366 | | Polis | 9067 | 9167 | 8067 | 38367 | | Qtum | 9088 | 9188 | 8088 | 38388 | +| Divi Project | 9089 | 9189 | 8089 | 38389 | | Bitcoin Testnet | 19030 | 19130 | 18030 | 48330 | | Bitcoin Cash Testnet | 19031 | 19131 | 18031 | 48331 | | Zcash Testnet | 19032 | 19132 | 18032 | 48332 | diff --git a/tests/rpc/testdata/divi.json b/tests/rpc/testdata/divi.json new file mode 100644 index 00000000..d62a1734 --- /dev/null +++ b/tests/rpc/testdata/divi.json @@ -0,0 +1,52 @@ +{ + "blockHeight": 430894, + "blockHash": "ca642b4cebd715d7448066e72d9c1c9f7cc17a7c7275ce5732357f9ede6199c7", + "blockTime": 1564178016, + "blockTxs": [ + "c206fc61c6957c54f365e7416d57a7cb314674b64aa84443ffa9b6d196aca9d9", + "877557968ae65d64e99d09170cd9548e8a309508596355cc264758ce8c483b4b" + ], + "txDetails": { + "877557968ae65d64e99d09170cd9548e8a309508596355cc264758ce8c483b4b": { + "hex": "010000000175d0b5235b4786be686a50f04382c3e401a1e8aa1ba17e7c959a58ddd39efeaa010000006b483045022100e552531d4be4290438edc7e8c5d1e2b5548c1a76cd396dfa9423b0e55abb0c6a02203efc3f76133de9161ba787c5fd9ef4cffbfd0745cfea8cf3a3f6dd3bce9293d0012103961c8f7574f64a61087adf3057f394be8bdc33b7a8abcbc60a71f7477dbd52a1ffffffff03000000000000000000009bb81da10100001976a9143ae7ad31dc26934c71e61bbb3248fb509a5f5d5988ac009ca6920c0000001976a9140d570e6e31e046f05b26f8bd8b14adbc2e76f99988ac00000000", + "txid": "877557968ae65d64e99d09170cd9548e8a309508596355cc264758ce8c483b4b", + "blocktime": 1564178016, + "time": 1564178016, + "locktime": 0, + "version": 1, + "vin": [ + { + "txid": "aafe9ed3dd589a957c7ea11baae8a101e4c38243f0506a68be86475b23b5d075", + "vout": 1, + "sequence": 4294967295, + "scriptSig": { + "hex": "483045022100e552531d4be4290438edc7e8c5d1e2b5548c1a76cd396dfa9423b0e55abb0c6a02203efc3f76133de9161ba787c5fd9ef4cffbfd0745cfea8cf3a3f6dd3bce9293d0012103961c8f7574f64a61087adf3057f394be8bdc33b7a8abcbc60a71f7477dbd52a1" + } + } + ], + "vout": [ + { + "value": 0, + "n": 0, + "scriptPubKey": { + "type": "nonstandard" + } + }, + { + "value": 17915, + "n": 1, + "scriptPubKey": { + "hex": "76a9143ae7ad31dc26934c71e61bbb3248fb509a5f5d5988ac" + } + }, + { + "value": 540, + "n": 2, + "scriptPubKey": { + "hex": "76a9140d570e6e31e046f05b26f8bd8b14adbc2e76f99988ac" + } + } + ] + } + } +} diff --git a/tests/sync/testdata/divi.json b/tests/sync/testdata/divi.json new file mode 100644 index 00000000..7d378578 --- /dev/null +++ b/tests/sync/testdata/divi.json @@ -0,0 +1,112 @@ +{ + "connectBlocks": { + "syncRanges": [ + { + "lower": 430894, + "upper": 430894 + } + ], + "blocks": { + "430894": { + "height": 430894, + "hash": "ca642b4cebd715d7448066e72d9c1c9f7cc17a7c7275ce5732357f9ede6199c7", + "noTxs": 2, + "txDetails": [ + { + "hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff06032e93060101ffffffff0100000000000000000000000000", + "txid": "c206fc61c6957c54f365e7416d57a7cb314674b64aa84443ffa9b6d196aca9d9", + "version": 1, + "locktime": 0, + "vin": [ + { + "coinbase": "032e93060101", + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.00000000, + "valueSat": 0, + "n": 0, + "scriptPubKey": { + "asm": "", + "hex": "", + "type": "nonstandard" + } + } + ], + "blockhash": "ca642b4cebd715d7448066e72d9c1c9f7cc17a7c7275ce5732357f9ede6199c7", + "height": 430894, + "confirmations": 1036, + "time": 1564178016, + "blocktime": 1564178016, + "size": 66 + }, + { + "hex": "010000000175d0b5235b4786be686a50f04382c3e401a1e8aa1ba17e7c959a58ddd39efeaa010000006b483045022100e552531d4be4290438edc7e8c5d1e2b5548c1a76cd396dfa9423b0e55abb0c6a02203efc3f76133de9161ba787c5fd9ef4cffbfd0745cfea8cf3a3f6dd3bce9293d0012103961c8f7574f64a61087adf3057f394be8bdc33b7a8abcbc60a71f7477dbd52a1ffffffff03000000000000000000009bb81da10100001976a9143ae7ad31dc26934c71e61bbb3248fb509a5f5d5988ac009ca6920c0000001976a9140d570e6e31e046f05b26f8bd8b14adbc2e76f99988ac00000000", + "txid": "877557968ae65d64e99d09170cd9548e8a309508596355cc264758ce8c483b4b", + "version": 1, + "locktime": 0, + "vin": [ + { + "txid": "aafe9ed3dd589a957c7ea11baae8a101e4c38243f0506a68be86475b23b5d075", + "vout": 1, + "scriptSig": { + "asm": "3045022100e552531d4be4290438edc7e8c5d1e2b5548c1a76cd396dfa9423b0e55abb0c6a02203efc3f76133de9161ba787c5fd9ef4cffbfd0745cfea8cf3a3f6dd3bce9293d001 03961c8f7574f64a61087adf3057f394be8bdc33b7a8abcbc60a71f7477dbd52a1", + "hex": "483045022100e552531d4be4290438edc7e8c5d1e2b5548c1a76cd396dfa9423b0e55abb0c6a02203efc3f76133de9161ba787c5fd9ef4cffbfd0745cfea8cf3a3f6dd3bce9293d0012103961c8f7574f64a61087adf3057f394be8bdc33b7a8abcbc60a71f7477dbd52a1" + }, + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.00000000, + "valueSat": 0, + "n": 0, + "scriptPubKey": { + "asm": "", + "hex": "", + "type": "nonstandard" + } + }, + { + "value": 17915.00000000, + "valueSat": 1791500000000, + "n": 1, + "scriptPubKey": { + "asm": "OP_DUP OP_HASH160 3ae7ad31dc26934c71e61bbb3248fb509a5f5d59 OP_EQUALVERIFY OP_CHECKSIG", + "hex": "76a9143ae7ad31dc26934c71e61bbb3248fb509a5f5d5988ac", + "reqSigs": 1, + "type": "pubkeyhash", + "addresses": [ + "DAWZGZ2NSQHgDY7HWZCkZEHC9ya5kiJVQJ" + ] + } + }, + { + "value": 540.00000000, + "valueSat": 54000000000, + "n": 2, + "scriptPubKey": { + "asm": "OP_DUP OP_HASH160 0d570e6e31e046f05b26f8bd8b14adbc2e76f999 OP_EQUALVERIFY OP_CHECKSIG", + "hex": "76a9140d570e6e31e046f05b26f8bd8b14adbc2e76f99988ac", + "reqSigs": 1, + "type": "pubkeyhash", + "addresses": [ + "D6MdcXyHpuMTDSF6zLPLT2UmXHB2ZY5x37" + ] + } + } + ], + "blockhash": "ca642b4cebd715d7448066e72d9c1c9f7cc17a7c7275ce5732357f9ede6199c7", + "height": 430894, + "confirmations": 1037, + "time": 1564178016, + "blocktime": 1564178016, + "size": 235 + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/tests.json b/tests/tests.json index d554d075..24b8b905 100644 --- a/tests/tests.json +++ b/tests/tests.json @@ -51,6 +51,11 @@ "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"] }, + "divi": { + "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", + "EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight"], + "sync": ["ConnectBlocksParallel", "ConnectBlocks"] + }, "dogecoin": { "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync"], "sync": ["ConnectBlocksParallel", "ConnectBlocks"]