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
This commit is contained in:
James Scaur 2019-08-21 18:07:35 +01:00 committed by Martin
parent 44a0e5823f
commit d6375a19dd
12 changed files with 905 additions and 0 deletions

View File

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

229
bchain/coins/divi/diviparser.go Executable file
View File

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

View File

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

60
bchain/coins/divi/divirpc.go Executable file
View File

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

View File

@ -0,0 +1 @@
04000000881f4e2a409a1b75c62ed6fdc6fa278b00f304bbe0729853bc336cbc30c4931da66efc4bd6260f95886061747185eace5294209dbe390fa3408534e42a0f5d525dba255dfe78101b0000000000000000000000000000000000000000000000000000000000000000000000000201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff06036f37060101ffffffff01000000000000000000000000000100000001652779264ccc941129adb833b8a42a6d278a98f295d25a0a53868ab1dc13bed4020000006a473044022046c6ab760d25c7d94f20aa2c81cc281a57bb3fa174023dcaac385727ab05f214022056841e947398a1d86bf7d0b99191f7d3421d0abf6ad1357955d38f5fe6c3a8c40121023c85ccefc390984ba8f71a0f0b999b7b6c6e9a30ccd63087d67d34d5c1fefc7dffffffff0300000000000000000080170a4e830000001976a914771f45c78657c739f0cc26dfc0b5a87ddd6e431c88ac009ca6920c0000001976a914e7c53cbb09860c3713b44958dd644e4d9154345688ac00000000412066000b291e3e3fd8f1fce0812ba4aaad2502a702fd3466a3d8a7dc1a78c816be01209f1179d41f7d38da229f1843a9c19cf67a9d610c2a767979ea31b264f3eb

View File

@ -0,0 +1 @@
040000000ff149a32a715106e7023cd41193d7ea64ca0092edb4659f035a881fc452b845e9249f2c4463db2074e307ba62e93619fd3d17a3b6b7437a326a79869f35c00c5e58265db760101b0000000000000000000000000000000000000000000000000000000000000000000000000501000000010000000000000000000000000000000000000000000000000000000000000000ffffffff06030a3a060101ffffffff01000000000000000000000000000100000001fdb6b4d9d9aa8afbdaac2d9bc4c29371147a6a3672529ad7ae45a7944558ec93020000006a473044022061bc3ffe2b069a39acdba6a169ae4fbaa34b32360ff9a45fce9c92eb0434ca6602206fa6c22110f4a14c8cee58e458e3522f41993e461278ef8ccb94322595981ec1012103b2e3a485f7a7da6eb16ea025ef45efa613d18350b2538899fab0b5a2e86ffda3ffffffff0300000000000000000000a882a22d0000001976a914899ef2e7669830485476f9c910a8330959d245e188ac009ca6920c0000001976a914a9147e5ce1507c2f49d9ceb8bbd2d280fb410b8188ac000000000100000002a0a25308b7afb81327a4a8cb1d13aa54fa53edf292bdf7fe2739f297f4027bb4000000006b4830450221009396b072158c158e7e90cdd797a0a58caf15c7e1589030d542a32e3568324a22022063d90727f3a71a9c01f75ece872ee0240f46eeac14531a7f2bfdb3fe5d0e793f01210263e67be5854349c3c8e23fea5020aa2b7bfd46916ce4e805676e4cd3b375cda5ffffffff8c7745969fbcd3bfb698d51cc8b110fe540d6a0ed9816bd2c1e375f0e7c2ec94000000006a4730440220748c808b1107ecf01559159c76163a2206af771604643aefea438f3c99c699a0022006c067c603fb0b4bc7a5cd129750fc010613957ec0666c41185d035b6f5fcbb1012102a535454382f62235104836bbbe1c894f1d6705c828b8be11f623068159882eb0ffffffff022018e505000000001976a914bba57e3d30f892d804d588ee633dcbb6a96b0cbb88ac0010a5d4e80000001976a914dd40b08afb0dc8496892bfe5e3ae1aceda57135b88ac000000000100000001583f174f32c563eb47555fbacdb0937881b4a25f35fe5f13d8c49cbf92654fdd000000006b483045022100cbacf5f775ab4fabdfb147c095c0865dd56bc461115b03a7a29ab96037fc4b3f02206b824c334b950d856550fff524717e07095fa2d9bbe8d5d8096813721d98715c012103f4e09f09921ad224e9e590df477cea01fc8114e816a2112ed59518bf99cdaa3effffffff0200f2052a010000001976a9148226210ccf912fb3969c0996a5582dcfb99a2e1788ac60b66daee00000001976a914d649408521dfef8449203417d77e44eadfc329f588ac00000000010000000199bd849243dea120d7d7df9037caa11327ed9df671e90c06442d12859ef4c052000000006a4730440220101b4e2022b3ffa023b83197d8ceb45b3ef31ff2da5eba7a6f7f914448397c31022023cb7908eafab080da4ff37460feb3c72e1ac6ecdb052d2df0c69a3954ec44550121026fd3594eb77ded5de88f54a528950659822859472fcee072a59657032aaf6fe2ffffffff02f071dee22c0000001976a914a316f474589a2f2013bd60b7d0ffb735e33915d188ac00e1823e0c0100001976a914ee628c1d309de0e0d3033d047b19921563f97ee288ac00000000411f3b6a2aabf7c3ccdd2e4d30a581a53670b6bc623e324eb3416100dfe64b3c54a84567bace9246f935b310e026c137eb7ec9836dfb46854aa6695af50a1ac5dd64

View File

@ -0,0 +1 @@
04000000c7fed5984ba7d11c7e4865c1d41ad1755a1082782d81a27fc08888bc749881e9e709a5bb1daf5a76582def89c13eba5e7c4cf8104407ecf81f74be6ba18e89c6ae3e275da61b0d1b0000000000000000000000000000000000000000000000000000000000000000000000000201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0603de3d060101ffffffff01000000000000000000000000000100000001084b029489e1cddf726080c447c8a2b1d4bbe43024db31b8b19bc07585db9555010000006a473044022017422b9e3414d6233fa75f9eb7778469bebbb40686b0f7eb77d90a04c80149610220411f1063086fe205ea821ceb0de89e8158e202aba00f5ebb92b51f97381311fd012102ccb10a2f0603a0624b8708abefb5f4700631fc131c5de38b51e0359e2ffa7d1cffffffff03000000000000000000f260de1a580100001976a9145b1d583a4c270f2f14be77b298f0a9c6df97471388ac009ca6920c0000001976a914cb1196fb1b98d04b0cb8d2ffde3c2de3eb83d9fe88ac00000000411f90bba5b16a087a5b7e0b8fc2aba041d81d74d270b502b24ea5801b5f4f975bba382260a6fd247facbb49acb8f304324deb3c1e00060d10a4958c047e4530c9fa

65
configs/coins/divi.json Normal file
View File

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

View File

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

52
tests/rpc/testdata/divi.json vendored Normal file
View File

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

112
tests/sync/testdata/divi.json vendored Normal file
View File

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

View File

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