Added Zcoin support (#106)

* Add zcoin configuration

* Update ports registry

* Change verification_type to gpg-sha256

* Fix incorrect zcoin configurations

* Change backend verification type to sha256 due to no public key for gpg-sha256

* Initializes Zcoin factory

* Add zcoin parser

* Finish BlockChain implementation for XZC

* Implement EstimateFee for XZC

* Add RPC integration tests for XZC

* Add unittest for parser && fix wrong network params

* Fix incorrect RPC marshaler for XZC

* Add sync integration test for zcoin

* Add zcoin block parser

* Add more testdata for sync integration test

* Remove EstimateSmartFee test for XZC due to it not supported

* Refactor and cleanup XZC parser

* Fix zerocoin spend vin error

* Fix display zerocoin mint and spend

* Support script parser for spend

* Fix build errors and bugs after rebase
pull/109/head
Putta Khunchalee 2019-01-18 02:31:15 +07:00 committed by Martin
parent b593643f63
commit 8e82b3da0f
15 changed files with 1656 additions and 9 deletions

View File

@ -19,6 +19,7 @@ import (
"blockbook/bchain/coins/namecoin"
"blockbook/bchain/coins/pivx"
"blockbook/bchain/coins/vertcoin"
"blockbook/bchain/coins/xzc"
"blockbook/bchain/coins/zec"
"blockbook/common"
"context"
@ -64,6 +65,7 @@ func init() {
BlockChainFactories["Groestlcoin"] = grs.NewGroestlcoinRPC
BlockChainFactories["Groestlcoin Testnet"] = grs.NewGroestlcoinRPC
BlockChainFactories["PIVX"] = pivx.NewPivXRPC
BlockChainFactories["Zcoin"] = xzc.NewZcoinRPC
BlockChainFactories["Fujicoin"] = fujicoin.NewFujicoinRPC
}

View File

@ -454,7 +454,7 @@ func (b *BitcoinRPC) GetChainInfo() (*bchain.ChainInfo, error) {
return rv, nil
}
func isErrBlockNotFound(err *bchain.RPCError) bool {
func IsErrBlockNotFound(err *bchain.RPCError) bool {
return err.Message == "Block not found" ||
err.Message == "Block height out of range"
}
@ -472,7 +472,7 @@ func (b *BitcoinRPC) GetBlockHash(height uint32) (string, error) {
return "", errors.Annotatef(err, "height %v", height)
}
if res.Error != nil {
if isErrBlockNotFound(res.Error) {
if IsErrBlockNotFound(res.Error) {
return "", bchain.ErrBlockNotFound
}
return "", errors.Annotatef(res.Error, "height %v", height)
@ -494,7 +494,7 @@ func (b *BitcoinRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) {
return nil, errors.Annotatef(err, "hash %v", hash)
}
if res.Error != nil {
if isErrBlockNotFound(res.Error) {
if IsErrBlockNotFound(res.Error) {
return nil, bchain.ErrBlockNotFound
}
return nil, errors.Annotatef(res.Error, "hash %v", hash)
@ -548,7 +548,7 @@ func (b *BitcoinRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
return nil, errors.Annotatef(err, "hash %v", hash)
}
if res.Error != nil {
if isErrBlockNotFound(res.Error) {
if IsErrBlockNotFound(res.Error) {
return nil, bchain.ErrBlockNotFound
}
return nil, errors.Annotatef(res.Error, "hash %v", hash)
@ -586,7 +586,7 @@ func (b *BitcoinRPC) GetBlockRaw(hash string) ([]byte, error) {
return nil, errors.Annotatef(err, "hash %v", hash)
}
if res.Error != nil {
if isErrBlockNotFound(res.Error) {
if IsErrBlockNotFound(res.Error) {
return nil, bchain.ErrBlockNotFound
}
return nil, errors.Annotatef(res.Error, "hash %v", hash)
@ -608,7 +608,7 @@ func (b *BitcoinRPC) GetBlockFull(hash string) (*bchain.Block, error) {
return nil, errors.Annotatef(err, "hash %v", hash)
}
if res.Error != nil {
if isErrBlockNotFound(res.Error) {
if IsErrBlockNotFound(res.Error) {
return nil, bchain.ErrBlockNotFound
}
return nil, errors.Annotatef(res.Error, "hash %v", hash)
@ -633,7 +633,7 @@ func (b *BitcoinRPC) GetMempool() ([]string, error) {
return res.Result, nil
}
func isMissingTx(err *bchain.RPCError) bool {
func IsMissingTx(err *bchain.RPCError) bool {
if err.Code == -5 { // "No such mempool or blockchain transaction"
return true
}
@ -654,7 +654,7 @@ func (b *BitcoinRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
return nil, errors.Annotatef(err, "txid %v", txid)
}
if res.Error != nil {
if isMissingTx(res.Error) {
if IsMissingTx(res.Error) {
return nil, bchain.ErrTxNotFound
}
return nil, errors.Annotatef(res.Error, "txid %v", txid)
@ -706,7 +706,7 @@ func (b *BitcoinRPC) getRawTransaction(txid string) (json.RawMessage, error) {
return nil, errors.Annotatef(err, "txid %v", txid)
}
if res.Error != nil {
if isMissingTx(res.Error) {
if IsMissingTx(res.Error) {
return nil, bchain.ErrTxNotFound
}
return nil, errors.Annotatef(res.Error, "txid %v", txid)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,262 @@
package xzc
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"bytes"
"encoding/binary"
"encoding/json"
"io"
"github.com/martinboehm/btcd/chaincfg/chainhash"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg"
)
const (
OpZeroCoinMint = 0xc1
OpZeroCoinSpend = 0xc2
MainnetMagic wire.BitcoinNet = 0xe3d9fef1
TestnetMagic wire.BitcoinNet = 0xcffcbeea
RegtestMagic wire.BitcoinNet = 0xfabfb5da
GenesisBlockTime = 1414776286
SwitchToMTPBlockHeader = 1544443200
MTPL = 64
SpendTxID = "0000000000000000000000000000000000000000000000000000000000000000"
)
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params
RegtestParams chaincfg.Params
)
func init() {
// mainnet
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
MainNetParams.AddressMagicLen = 1
MainNetParams.PubKeyHashAddrID = []byte{0x52}
MainNetParams.ScriptHashAddrID = []byte{0x07}
// testnet
TestNetParams = chaincfg.TestNet3Params
TestNetParams.Net = TestnetMagic
TestNetParams.AddressMagicLen = 1
TestNetParams.PubKeyHashAddrID = []byte{0x41}
TestNetParams.ScriptHashAddrID = []byte{0xb2}
// regtest
RegtestParams = chaincfg.RegressionNetParams
RegtestParams.Net = RegtestMagic
}
// ZcoinParser handle
type ZcoinParser struct {
*btc.BitcoinParser
}
// NewZcoinParser returns new ZcoinParser instance
func NewZcoinParser(params *chaincfg.Params, c *btc.Configuration) *ZcoinParser {
return &ZcoinParser{
BitcoinParser: btc.NewBitcoinParser(params, c),
}
}
// GetChainParams contains network parameters for the main Zcoin network,
// the regression test Zcoin network, the test Zcoin network and
// the simulation test Zcoin network, in this order
func GetChainParams(chain string) *chaincfg.Params {
if !chaincfg.IsRegistered(&MainNetParams) {
err := chaincfg.Register(&MainNetParams)
if err == nil {
err = chaincfg.Register(&TestNetParams)
}
if err == nil {
err = chaincfg.Register(&RegtestParams)
}
if err != nil {
panic(err)
}
}
switch chain {
case "test":
return &TestNetParams
case "regtest":
return &RegtestParams
default:
return &MainNetParams
}
}
// GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable
func (p *ZcoinParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) {
if len(addrDesc) > 0 && addrDesc[0] == OpZeroCoinMint {
return []string{"Zeromint"}, false, nil
}
if len(addrDesc) > 0 && addrDesc[0] == OpZeroCoinSpend {
return []string{"Zerospend"}, false, nil
}
return p.OutputScriptToAddressesFunc(addrDesc)
}
// PackTx packs transaction to byte array using protobuf
func (p *ZcoinParser) 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 *ZcoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
return p.BaseParser.UnpackTx(buf)
}
// ParseBlock parses raw block to our Block struct
func (p *ZcoinParser) ParseBlock(b []byte) (*bchain.Block, error) {
reader := bytes.NewReader(b)
// parse standard block header first
header, err := parseBlockHeader(reader)
if err != nil {
return nil, err
}
// then MTP header
if isMTP(header) {
mtpHeader := MTPBlockHeader{}
mtpHashData := MTPHashData{}
// header
err = binary.Read(reader, binary.LittleEndian, &mtpHeader)
if err != nil {
return nil, err
}
// hash data
err = binary.Read(reader, binary.LittleEndian, &mtpHashData)
if err != nil {
return nil, err
}
// proof
for i := 0; i < MTPL*3; i++ {
var numberProofBlocks uint8
err = binary.Read(reader, binary.LittleEndian, &numberProofBlocks)
if err != nil {
return nil, err
}
for j := uint8(0); j < numberProofBlocks; j++ {
var mtpData [16]uint8
err = binary.Read(reader, binary.LittleEndian, mtpData[:])
if err != nil {
return nil, err
}
}
}
}
// parse txs
ntx, err := wire.ReadVarInt(reader, 0)
if err != nil {
return nil, err
}
txs := make([]bchain.Tx, ntx)
for i := uint64(0); i < ntx; i++ {
tx := wire.MsgTx{}
err := tx.BtcDecode(reader, 0, wire.WitnessEncoding)
if err != nil {
return nil, err
}
btx := p.TxFromMsgTx(&tx, false)
p.parseZcoinTx(&btx)
txs[i] = btx
}
return &bchain.Block{
BlockHeader: bchain.BlockHeader{
Size: len(b),
Time: header.Timestamp.Unix(),
},
Txs: txs,
}, nil
}
// ParseTxFromJson parses JSON message containing transaction and returns Tx struct
func (p *ZcoinParser) 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 = ""
}
p.parseZcoinTx(&tx)
return &tx, nil
}
func (p *ZcoinParser) parseZcoinTx(tx *bchain.Tx) error {
for i := range tx.Vin {
vin := &tx.Vin[i]
// FIXME: right now we treat zerocoin spend vin as coinbase
// change this after blockbook support special type of vin
if vin.Txid == SpendTxID {
vin.Coinbase = vin.Txid
vin.Txid = ""
vin.Sequence = 0
vin.Vout = 0
}
}
return nil
}
func parseBlockHeader(r io.Reader) (*wire.BlockHeader, error) {
h := &wire.BlockHeader{}
err := h.Deserialize(r)
return h, err
}
func isMTP(h *wire.BlockHeader) bool {
epoch := h.Timestamp.Unix()
// the genesis block never be MTP block
return epoch > GenesisBlockTime && epoch >= SwitchToMTPBlockHeader
}
type MTPHashData struct {
HashRootMTP [16]uint8
BlockMTP [128][128]uint64
}
type MTPBlockHeader struct {
VersionMTP int32
MTPHashValue chainhash.Hash
Reserved1 chainhash.Hash
Reserved2 chainhash.Hash
}

View File

@ -0,0 +1,620 @@
// +build unittest
package xzc
import (
"bytes"
"encoding/hex"
"encoding/json"
"io/ioutil"
"math/big"
"os"
"reflect"
"strings"
"testing"
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcutil/chaincfg"
)
var (
testTx1, testTx2, testTx3, testTx4 bchain.Tx
testTxPacked1, testTxPacked2, testTxPacked3, testTxPacked4 string
rawBlock1, rawBlock2 string
jsonTx json.RawMessage
)
func readHexs(path string) []string {
raw, err := ioutil.ReadFile(path)
if err != nil {
panic(err)
}
rawStr := string(raw)
raws := strings.Split(rawStr, "\n")
return raws
}
func init() {
rawBlocks := readHexs("./testdata/rawblock.hex")
rawBlock1 = rawBlocks[0]
rawBlock2 = rawBlocks[1]
hextxs := readHexs("./testdata/txs.hex")
rawTestTx1 := hextxs[0]
rawTestTx2 := hextxs[1]
rawTestTx3 := hextxs[2]
rawTestTx4 := hextxs[3]
rawSpendHex := readHexs("./testdata/rawspend.hex")[0]
rawSpendTx, err := ioutil.ReadFile("./testdata/spendtx.json")
if err != nil {
panic(err)
}
jsonTx = json.RawMessage(rawSpendTx)
testTxPackeds := readHexs("./testdata/packedtxs.hex")
testTxPacked1 = testTxPackeds[0]
testTxPacked2 = testTxPackeds[1]
testTxPacked3 = testTxPackeds[2]
testTx1 = bchain.Tx{
Hex: rawTestTx1,
Blocktime: 1533980594,
Time: 1533980594,
Txid: "9d9e759dd970d86df9e105a7d4f671543bc16a03b6c5d2b48895f2a00aa7dd23",
LockTime: 0,
Vin: []bchain.Vin{
{
ScriptSig: bchain.ScriptSig{
Hex: "47304402205b7d9c9aae790b69017651e10134735928df3b4a4a2feacc9568eb4fa133ed5902203f21a399385ce29dd79831ea34aa535612aa4314c5bd0b002bbbc9bcd2de1436012102b8d462740c99032a00083ac7028879acec244849e54ad0a04ea87f632f54b1d2",
},
Txid: "463a2d66b04636a014da35724909425f3403d9d786dd4f79780de50d47b18716",
Vout: 1,
Sequence: 4294967294,
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(18188266638),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "c10280004c80f767f3ee79953c67a7ed386dcccf1243619eb4bbbe414a3982dd94a83c1b69ac52d6ab3b653a3e05c4e4516c8dfe1e58ada40461bc5835a4a0d0387a51c29ac11b72ae25bbcdef745f50ad08f08b3e9bc2c31a35444398a490e65ac090e9f341f1abdebe47e57e8237ac25d098e951b4164a35caea29f30acb50b12e4425df28",
},
},
{
ValueSat: *big.NewInt(18188266638),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914c963f917c7f23cb4243e079db33107571b87690588ac",
Addresses: []string{
"aK5KKi8qqDbspcXFfDjx8UBGMouhYbYZVp",
},
},
},
},
}
testTx2 = bchain.Tx{
Hex: rawTestTx2,
Blocktime: 1481277009,
Time: 1481277009,
Txid: "3d721fdce2855e2b4a54b74a26edd58a7262e1f195b5acaaae7832be6e0b3d32",
LockTime: 0,
Vin: []bchain.Vin{
{
ScriptSig: bchain.ScriptSig{
Hex: rawSpendHex,
},
Txid: "0000000000000000000000000000000000000000000000000000000000000000",
Vout: 4294967295,
Sequence: 2,
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(5000000000),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914b9e262e30df03e88ccea312652bc83ca7290c8fc88ac",
Addresses: []string{
"aHfKwzFZMiSxDuNL4jts819nh57t2yJG1h",
},
},
},
},
}
testTx3 = bchain.Tx{
Hex: rawTestTx3,
Blocktime: 1547091829,
Time: 1547091829,
Txid: "96ae951083651f141d1fb2719c76d47e5a3ad421b81905f679c0edb60f2de0ff",
LockTime: 126200,
Vin: []bchain.Vin{
{
ScriptSig: bchain.ScriptSig{
Hex: "483045022100bdc6b51c114617e29e28390dc9b3ad95b833ca3d1f0429ba667c58a667f9124702204ca2ed362dd9ef723ddbdcf4185b47c28b127a36f46bc4717662be863309b3e601210387e7ff08b953e3736955408fc6ebcd8aa84a04cc4b45758ea29cc2cfe1820535",
},
Txid: "448ccfd9c3f375be8701b86aff355a230dbe240334233f2ed476fcae6abd295d",
Vout: 1,
Sequence: 4294967294,
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(42000000000),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a91429bef7962c5c65a2f0f4f7d9ec791866c54f851688ac",
Addresses: []string{
"a4XCDQ7AnRH9opZ4h6LcG3g7ocSV2SbBmS",
},
},
},
{
ValueSat: *big.NewInt(107300000000),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914e2cee7b71c3a4637dbdfe613f19f4b4f2d070d7f88ac",
Addresses: []string{
"aMPiKHB3E1AGPi8kKLknx6j1L4JnKCGkLw",
},
},
},
},
}
testTx4 = bchain.Tx{
Hex: rawTestTx4,
Blocktime: 1533977563,
Time: 1533977563,
Txid: "914ccbdb72f593e5def15978cf5891e1384a1b85e89374fc1c440c074c6dd286",
LockTime: 0,
Vin: []bchain.Vin{
{
Coinbase: "03a1860104dba36e5b082a00077c00000000052f6d70682f",
Sequence: 0,
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(2800200000),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a91436e086acf6561a68ba64196e7b92b606d0b8516688ac",
Addresses: []string{
"a5idCcHN8WYxvFCeBXSXvMPrZHuBkZmqEJ",
},
},
},
{
ValueSat: *big.NewInt(1500000000),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914381a5dd1a279e8e63e67cde39ecfa61a99dd2ba288ac",
Addresses: []string{
"a5q7Ad4okSFFVh5adyqx5DT21RTxJykpUM",
},
},
},
{
ValueSat: *big.NewInt(100000000),
N: 2,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9147d9ed014fc4e603fca7c2e3f9097fb7d0fb487fc88ac",
Addresses: []string{
"aCAgTPgtYcA4EysU4UKC86EQd5cTtHtCcr",
},
},
},
{
ValueSat: *big.NewInt(100000000),
N: 3,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914bc7e5a5234db3ab82d74c396ad2b2af419b7517488ac",
Addresses: []string{
"aHu897ivzmeFuLNB6956X6gyGeVNHUBRgD",
},
},
},
{
ValueSat: *big.NewInt(100000000),
N: 4,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914ff71b0c9c2a90c6164a50a2fb523eb54a8a6b55088ac",
Addresses: []string{
"a1HwTdCmQV3NspP2QqCGpehoFpi8NY4Zg3",
},
},
},
{
ValueSat: *big.NewInt(300000000),
N: 5,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9140654dd9b856f2ece1d56cb4ee5043cd9398d962c88ac",
Addresses: []string{
"a1HwTdCmQV3NspP2QqCGpehoFpi8NY4Zg3",
},
},
},
{
ValueSat: *big.NewInt(100000000),
N: 6,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9140b4bfb256ef4bfa360e3b9e66e53a0bd84d196bc88ac",
Addresses: []string{
"a1kCCGddf5pMXSipLVD9hBG2MGGVNaJ15U",
},
},
},
},
}
}
func TestMain(m *testing.M) {
c := m.Run()
chaincfg.ResetParams()
os.Exit(c)
}
func TestGetAddrDesc(t *testing.T) {
type args struct {
tx bchain.Tx
parser *ZcoinParser
}
tests := []struct {
name string
args args
}{
{
name: "xzc-1",
args: args{
tx: testTx1,
parser: NewZcoinParser(GetChainParams("main"), &btc.Configuration{}),
},
},
// FIXME: work around handle zerocoin spend as coinbase
// {
// name: "xzc-2",
// args: args{
// tx: testTx2,
// parser: NewZcoinParser(GetChainParams("main"), &btc.Configuration{}),
// },
// },
{
name: "xzc-3",
args: args{
tx: testTx3,
parser: NewZcoinParser(GetChainParams("main"), &btc.Configuration{}),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for n, vout := range tt.args.tx.Vout {
got1, err := tt.args.parser.GetAddrDescFromVout(&vout)
if err != nil {
t.Errorf("getAddrDescFromVout() error = %v, vout = %d", err, n)
return
}
// normal vout
if len(vout.ScriptPubKey.Addresses) >= 1 {
got2, err := tt.args.parser.GetAddrDescFromAddress(vout.ScriptPubKey.Addresses[0])
if err != nil {
t.Errorf("getAddrDescFromAddress() error = %v, vout = %d", err, n)
return
}
if !bytes.Equal(got1, got2) {
t.Errorf("Address descriptors mismatch: got1 = %v, got2 = %v", got1, got2)
}
}
}
})
}
}
func TestGetAddrDescFromVoutForMint(t *testing.T) {
type args struct {
vout bchain.Vout
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "OP_RETURN",
args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "6a072020f1686f6a20"}}},
want: "6a072020f1686f6a20",
wantErr: false,
},
{
name: "OP_ZEROCOINMINT",
args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "c10280004c80f767f3ee79953c67a7ed386dcccf1243619eb4bbbe414a3982dd94a83c1b69ac52d6ab3b653a3e05c4e4516c8dfe1e58ada40461bc5835a4a0d0387a51c29ac11b72ae25bbcdef745f50ad08f08b3e9bc2c31a35444398a490e65ac090e9f341f1abdebe47e57e8237ac25d098e951b4164a35caea29f30acb50b12e4425df28"}}},
want: "c10280004c80f767f3ee79953c67a7ed386dcccf1243619eb4bbbe414a3982dd94a83c1b69ac52d6ab3b653a3e05c4e4516c8dfe1e58ada40461bc5835a4a0d0387a51c29ac11b72ae25bbcdef745f50ad08f08b3e9bc2c31a35444398a490e65ac090e9f341f1abdebe47e57e8237ac25d098e951b4164a35caea29f30acb50b12e4425df28",
wantErr: false,
},
}
parser := NewZcoinParser(GetChainParams("main"), &btc.Configuration{})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.GetAddrDescFromVout(&tt.args.vout)
if (err != nil) != tt.wantErr {
t.Errorf("GetAddrDescFromVout() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("GetAddrDescFromVout() = %v, want %v", h, tt.want)
}
})
}
}
func TestGetAddressesFromAddrDescForMint(t *testing.T) {
type args struct {
script string
}
tests := []struct {
name string
args args
want []string
want2 bool
wantErr bool
}{
{
name: "OP_RETURN hex",
args: args{script: "6a072020f1686f6a20"},
want: []string{"OP_RETURN 2020f1686f6a20"},
want2: false,
wantErr: false,
},
{
name: "OP_ZEROCOINMINT size hex",
args: args{script: "c10280004c80f767f3ee79953c67a7ed386dcccf1243619eb4bbbe414a3982dd94a83c1b69ac52d6ab3b653a3e05c4e4516c8dfe1e58ada40461bc5835a4a0d0387a51c29ac11b72ae25bbcdef745f50ad08f08b3e9bc2c31a35444398a490e65ac090e9f341f1abdebe47e57e8237ac25d098e951b4164a35caea29f30acb50b12e4425df28"},
want: []string{"Zeromint"},
want2: false,
wantErr: false,
},
}
parser := NewZcoinParser(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)
}
})
}
}
func TestPackTx(t *testing.T) {
type args struct {
tx bchain.Tx
height uint32
blockTime int64
parser *ZcoinParser
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "xzc-1",
args: args{
tx: testTx1,
height: 100002,
blockTime: 1533980594,
parser: NewZcoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: testTxPacked1,
wantErr: false,
},
// FIXME: work around handle zerocoin spend as coinbase
// {
// name: "xzc-2",
// args: args{
// tx: testTx2,
// height: 11002,
// blockTime: 1481277009,
// parser: NewZcoinParser(GetChainParams("main"), &btc.Configuration{}),
// },
// want: testTxPacked2,
// wantErr: true,
// },
{
name: "xzc-3",
args: args{
tx: testTx3,
height: 126202,
blockTime: 1547091829,
parser: NewZcoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: testTxPacked3,
wantErr: false,
},
{
name: "xzc-coinbase",
args: args{
tx: testTx4,
height: 100001,
blockTime: 1533977563,
parser: NewZcoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: testTxPacked4,
wantErr: true,
},
}
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
}
if !tt.wantErr {
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("packTx() = %v, want %v", h, tt.want)
}
}
})
}
}
func TestUnpackTx(t *testing.T) {
type args struct {
packedTx string
parser *ZcoinParser
}
tests := []struct {
name string
args args
want *bchain.Tx
want1 uint32
wantErr bool
}{
{
name: "xzc-1",
args: args{
packedTx: testTxPacked1,
parser: NewZcoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &testTx1,
want1: 100002,
wantErr: false,
},
// FIXME: work around handle zerocoin spend as coinbase
// {
// name: "xzc-2",
// args: args{
// packedTx: testTxPacked2,
// parser: NewZcoinParser(GetChainParams("main"), &btc.Configuration{}),
// },
// want: &testTx2,
// want1: 11002,
// wantErr: true,
// },
{
name: "xzc-3",
args: args{
packedTx: testTxPacked3,
parser: NewZcoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &testTx3,
want1: 126202,
wantErr: false,
},
// {
// name: "xzc-4",
// args: args{
// packedTx: testTxPacked4,
// parser: NewZcoinParser(GetChainParams("main"), &btc.Configuration{}),
// },
// want: &testTx4,
// want1: 100001,
// 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 !tt.wantErr {
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)
}
}
})
}
}
func TestParseBlock(t *testing.T) {
type args struct {
rawBlock string
parser *ZcoinParser
}
tests := []struct {
name string
args args
want *bchain.Block
wantTxs int
wantErr bool
}{
{
name: "normal-block",
args: args{
rawBlock: rawBlock1,
parser: NewZcoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &bchain.Block{
BlockHeader: bchain.BlockHeader{
Size: 200286,
Time: 1547120622,
},
},
wantTxs: 3,
wantErr: false,
},
{
name: "spend-block",
args: args{
rawBlock: rawBlock2,
parser: NewZcoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &bchain.Block{
BlockHeader: bchain.BlockHeader{
Size: 25298,
Time: 1482107572,
},
},
wantTxs: 4,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b, _ := hex.DecodeString(tt.args.rawBlock)
got, err := tt.args.parser.ParseBlock(b)
if (err != nil) != tt.wantErr {
t.Errorf("parseBlock() error = %+v", err)
}
if got != nil {
if !reflect.DeepEqual(got.BlockHeader, tt.want.BlockHeader) {
t.Errorf("parseBlock() got = %v, want %v", got.BlockHeader, tt.want.BlockHeader)
}
if len(got.Txs) != tt.wantTxs {
t.Errorf("parseBlock() txs length got = %d, want %d", len(got.Txs), tt.wantTxs)
}
}
})
}
}

View File

@ -0,0 +1,242 @@
package xzc
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex"
"encoding/json"
"github.com/golang/glog"
"github.com/juju/errors"
)
type ZcoinRPC struct {
*btc.BitcoinRPC
}
func NewZcoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
// init base implementation
bc, err := btc.NewBitcoinRPC(config, pushHandler)
if err != nil {
return nil, err
}
// init zcoin implementation
zc := &ZcoinRPC{
BitcoinRPC: bc.(*btc.BitcoinRPC),
}
zc.ChainConfig.Parse = true
zc.ChainConfig.SupportsEstimateFee = true
zc.ChainConfig.SupportsEstimateSmartFee = false
zc.ParseBlocks = true
zc.RPCMarshaler = btc.JSONMarshalerV1{}
return zc, nil
}
func (zc *ZcoinRPC) Initialize() error {
chainName, err := zc.GetChainInfoAndInitializeMempool(zc)
if err != nil {
return err
}
params := GetChainParams(chainName)
// always create parser
zc.Parser = NewZcoinParser(params, zc.ChainConfig)
// parameters for getInfo request
if params.Net == MainnetMagic {
zc.Testnet = false
zc.Network = "livenet"
} else {
zc.Testnet = true
zc.Network = "testnet"
}
glog.Info("rpc: block chain ", params.Name)
return nil
}
func (zc *ZcoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
var err error
if hash == "" {
hash, err = zc.GetBlockHash(height)
if err != nil {
return nil, err
}
}
// optimization
if height > 0 {
return zc.GetBlockWithoutHeader(hash, height)
}
header, err := zc.GetBlockHeader(hash)
if err != nil {
return nil, err
}
data, err := zc.GetBlockRaw(hash)
if err != nil {
return nil, err
}
block, err := zc.Parser.ParseBlock(data)
if err != nil {
return nil, errors.Annotatef(err, "hash %v", hash)
}
block.BlockHeader = *header
return block, nil
}
func (zc *ZcoinRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
glog.V(1).Info("rpc: getblock (verbosity=true) ", hash)
res := btc.ResGetBlockInfo{}
req := cmdGetBlock{Method: "getblock"}
req.Params.BlockHash = hash
req.Params.Verbosity = true
err := zc.Call(&req, &res)
if err != nil {
return nil, errors.Annotatef(err, "hash %v", hash)
}
if res.Error != nil {
if btc.IsErrBlockNotFound(res.Error) {
return nil, bchain.ErrBlockNotFound
}
return nil, errors.Annotatef(res.Error, "hash %v", hash)
}
return &res.Result, nil
}
func (zc *ZcoinRPC) GetBlockWithoutHeader(hash string, height uint32) (*bchain.Block, error) {
data, err := zc.GetBlockRaw(hash)
if err != nil {
return nil, err
}
block, err := zc.Parser.ParseBlock(data)
if err != nil {
return nil, errors.Annotatef(err, "%v %v", height, hash)
}
block.BlockHeader.Hash = hash
block.BlockHeader.Height = height
return block, nil
}
func (zc *ZcoinRPC) GetBlockRaw(hash string) ([]byte, error) {
glog.V(1).Info("rpc: getblock (verbosity=false) ", hash)
res := btc.ResGetBlockRaw{}
req := cmdGetBlock{Method: "getblock"}
req.Params.BlockHash = hash
req.Params.Verbosity = false
err := zc.Call(&req, &res)
if err != nil {
return nil, errors.Annotatef(err, "hash %v", hash)
}
if res.Error != nil {
if btc.IsErrBlockNotFound(res.Error) {
return nil, bchain.ErrBlockNotFound
}
return nil, errors.Annotatef(res.Error, "hash %v", hash)
}
return hex.DecodeString(res.Result)
}
func (zc *ZcoinRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
glog.V(1).Info("rpc: getrawtransaction nonverbose ", txid)
res := btc.ResGetRawTransactionNonverbose{}
req := cmdGetRawTransaction{Method: "getrawtransaction"}
req.Params.Txid = txid
req.Params.Verbose = 0
err := zc.Call(&req, &res)
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}
if res.Error != nil {
if btc.IsMissingTx(res.Error) {
return nil, bchain.ErrTxNotFound
}
return nil, errors.Annotatef(res.Error, "txid %v", txid)
}
data, err := hex.DecodeString(res.Result)
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}
tx, err := zc.Parser.ParseTx(data)
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}
return tx, nil
}
func (zc *ZcoinRPC) GetTransaction(txid string) (*bchain.Tx, error) {
r, err := zc.getRawTransaction(txid)
if err != nil {
return nil, err
}
tx, err := zc.Parser.ParseTxFromJson(r)
tx.CoinSpecificData = r
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}
return tx, nil
}
func (zc *ZcoinRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, error) {
if csd, ok := tx.CoinSpecificData.(json.RawMessage); ok {
return csd, nil
}
return zc.getRawTransaction(tx.Txid)
}
func (zc *ZcoinRPC) getRawTransaction(txid string) (json.RawMessage, error) {
glog.V(1).Info("rpc: getrawtransaction ", txid)
res := btc.ResGetRawTransaction{}
req := cmdGetRawTransaction{Method: "getrawtransaction"}
req.Params.Txid = txid
req.Params.Verbose = 1
err := zc.Call(&req, &res)
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}
if res.Error != nil {
if btc.IsMissingTx(res.Error) {
return nil, bchain.ErrTxNotFound
}
return nil, errors.Annotatef(res.Error, "txid %v", txid)
}
return res.Result, nil
}
type cmdGetBlock struct {
Method string `json:"method"`
Params struct {
BlockHash string `json:"blockhash"`
Verbosity bool `json:"verbosity"`
} `json:"params"`
}
type cmdGetRawTransaction struct {
Method string `json:"method"`
Params struct {
Txid string `json:"txid"`
Verbose int `json:"verbose"`
} `json:"params"`
}

View File

@ -0,0 +1,76 @@
{
"coin": {
"name": "Zcoin",
"shortcut": "XZC",
"label": "Zcoin",
"alias": "zcoin"
},
"ports": {
"backend_rpc": 8050,
"backend_message_queue": 38350,
"blockbook_internal": 9050,
"blockbook_public": 9150
},
"ipc": {
"rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}",
"rpc_user": "rpc",
"rpc_pass": "rpc",
"rpc_timeout": 25,
"message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}"
},
"backend": {
"package_name": "backend-zcoin",
"package_revision": "satoshilabs-1",
"system_user": "zcoin",
"version": "0.13.7.6",
"binary_url": "https://github.com/zcoinofficial/zcoin/releases/download/v0.13.7.6/zcoin-0.13.7.6-linux64.tar.gz",
"verification_type": "sha256",
"verification_source": "d1567283a4c530ef5a9297857d47c17541be182f5feabb3e2ce607c0d1a67511",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/test_bitcoin",
"bin/tor",
"bin/tor-gencert",
"bin/tor-resolve",
"bin/torify",
"bin/zcoin-qt",
"etc/tor/torrc.sample",
"include/bitcoinconsensus.h",
"lib/libbitcoinconsensus.so",
"lib/libbitcoinconsensus.so.0",
"lib/libbitcoinconsensus.so.0.0.0",
"share/tor/geoip",
"share/tor/geoip6"
],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/zcoind -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": "",
"service_type": "forking",
"service_additional_params_template": "",
"protect_memory": true,
"mainnet": true,
"server_config_file": "bitcoin_like.conf",
"client_config_file": "bitcoin_like_client.conf",
"additional_params": {
"deprecatedrpc": "estimatefee"
}
},
"blockbook": {
"package_name": "blockbook-zcoin",
"system_user": "blockbook-zcoin",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"mempool_workers": 8,
"mempool_sub_workers": 2,
"block_addresses_to_keep": 300,
"additional_params": {}
}
},
"meta": {
"package_maintainer": "Putta Khunchalee",
"package_maintainer_email": "putta@zcoin.io"
}
}

View File

@ -22,6 +22,7 @@
| Liquid | 9047 | 9147 | 8047 | 38347 |
| Fujicoin | 9048 | 9148 | 8048 | 38348 |
| PIVX | 9049 | 9149 | 8049 | 38349 |
| Zcoin | 9050 | 9150 | 8050 | 38350 |
| Bitcoin Testnet | 19030 | 19130 | 18030 | 48330 |
| Bitcoin Cash Testnet | 19031 | 19131 | 18031 | 48331 |
| Zcash Testnet | 19032 | 19132 | 18032 | 48332 |

104
tests/rpc/testdata/zcoin.json vendored 100644
View File

@ -0,0 +1,104 @@
{
"blockHeight": 126311,
"blockHash": "4857051323dd6a10bc1c92facb00e7c8258adc9b8b680d0bcdd19034a777d72d",
"blockTime": 1547120622,
"blockTxs": [
"5ee3ba91195adc9a22e61a570b168052764584305000450d04355ebcb9cc4be8",
"2f831477d523ea717caf9a9520dd848b8fe932540207037fabf100a56d466e91",
"347d7c8c1d77f2f835641be27d97900f88be71646bdaa14ffb11b37aaac72f79"
],
"txDetails": {
"2f831477d523ea717caf9a9520dd848b8fe932540207037fabf100a56d466e91": {
"hex": "010000000106be6afc693eb158fa010f751c347982851108ea316d0685109caaa8ec4580c2000000006a473044022048dcdd1121148ecf495df74f563384d91db0635dae3c608666511ef8f24adc19022004af5384be1bce1531d0bf8498a8bfb150f797e63e348ca3865f0322203384aa012102d79d7e7f40e091517465c50ed3f5345c7b9a4611a98103ee1ca1f8e66686ad9fffffffff02e04d6cb3160000001976a914b923050a6542d70d6eb6546ba3275a2b6ea0eda588ac06d8c849000000001976a914c353c188620cb33a72d218c0d67fb3f92ad1b6b088ac00000000",
"txid": "2f831477d523ea717caf9a9520dd848b8fe932540207037fabf100a56d466e91",
"blocktime": 1547120622,
"time": 1547120622,
"locktime": 0,
"version": 1,
"vin": [
{
"txid": "c28045eca8aa9c1085066d31ea0811858279341c750f01fa58b13e69fc6abe06",
"vout": 0,
"sequence": 4294967295,
"scriptSig": {
"hex": "473044022048dcdd1121148ecf495df74f563384d91db0635dae3c608666511ef8f24adc19022004af5384be1bce1531d0bf8498a8bfb150f797e63e348ca3865f0322203384aa012102d79d7e7f40e091517465c50ed3f5345c7b9a4611a98103ee1ca1f8e66686ad9f"
}
}
],
"vout": [
{
"value": 974.99500000,
"n": 0,
"scriptPubKey": {
"hex": "76a914b923050a6542d70d6eb6546ba3275a2b6ea0eda588ac"
}
},
{
"value": 12.37899270,
"n": 1,
"scriptPubKey": {
"hex": "76a914c353c188620cb33a72d218c0d67fb3f92ad1b6b088ac"
}
}
]
},
"347d7c8c1d77f2f835641be27d97900f88be71646bdaa14ffb11b37aaac72f79": {
"hex": "0100000004bc566f299c27644851e70f8646f2ed642985897f488090f5bd5b152d891388a9000000006b483045022100cd5ca45783f836db65a47ba568f97f890155c27ccf0769c1edf231bf6bf49f9b022021d8493fae950bced2c417ce6c4a62aaad495c4d8675e4a4a3e9d86ab44f4b29012102d91223be36388a51cef618ec34c2a1d95f85b6d85d0e07e9e963d84893b5cc4dfefffffff117437f28af1665a002c8351e903de10bd40ab73d54932bbaae28caa6f49a42640000006b483045022100962a7c4b0a96b25fa418f92a69e9f37c5517740d50e996a92724f064bfadcf75022073f1c0475215bef70de392273360655a0b0d4f1f8afd3917614a5dfd1ddd1ca1012103bbfb869bf408cfdc237c63c1dcec226faf56cc141c42ef6bb8ef4d156df669eafeffffff0dd680cce849762fa7f3f117a1b0e3e7fb6493f2600152143502aa59ad7ca4ed4d0000006b483045022100a6b7befee6468f01e2b3813f05b81241dd5a4c0b04d97a783940c75d97b85ba502206c0dd2fa5cc5c153d359c55f24407c97aec32bf42b2723fc40374188443bd4b2012103bbfb869bf408cfdc237c63c1dcec226faf56cc141c42ef6bb8ef4d156df669eafeffffffd7e86128b28dcf2046c33f098cfcc2b7f08ea781cc119c0944b2fb23e378135d150000006a47304402204116539acc084920a86bf837ade9d938b517dfa0d346beb969cc97505e6f21520220069a9a33f798a6c1912e29568be2cef12d4db0ea1db751054db5e2311b761854012103bbfb869bf408cfdc237c63c1dcec226faf56cc141c42ef6bb8ef4d156df669eafeffffff0229b90000000000001976a914da8378d88cd781e0a6f9fdfa6668dd47a718cf6188ac80a35d06000000001976a914707077511db03836716dc01ce47442596264fd8a88acccea0100",
"txid": "347d7c8c1d77f2f835641be27d97900f88be71646bdaa14ffb11b37aaac72f79",
"blocktime": 1547120622,
"time": 1547120622,
"locktime": 125644,
"version": 1,
"vin": [
{
"txid": "a98813892d155bbdf59080487f89852964edf246860fe7514864279c296f56bc",
"vout": 0,
"sequence": 4294967294,
"scriptSig": {
"hex": "483045022100cd5ca45783f836db65a47ba568f97f890155c27ccf0769c1edf231bf6bf49f9b022021d8493fae950bced2c417ce6c4a62aaad495c4d8675e4a4a3e9d86ab44f4b29012102d91223be36388a51cef618ec34c2a1d95f85b6d85d0e07e9e963d84893b5cc4d"
}
},
{
"txid": "429af4a6ca28aeba2b93543db70ad40be13d901e35c802a06516af287f4317f1",
"vout": 100,
"sequence": 4294967294,
"scriptSig": {
"hex": "483045022100962a7c4b0a96b25fa418f92a69e9f37c5517740d50e996a92724f064bfadcf75022073f1c0475215bef70de392273360655a0b0d4f1f8afd3917614a5dfd1ddd1ca1012103bbfb869bf408cfdc237c63c1dcec226faf56cc141c42ef6bb8ef4d156df669ea"
}
},
{
"txid": "eda47cad59aa023514520160f29364fbe7e3b0a117f1f3a72f7649e8cc80d60d",
"vout": 77,
"sequence": 4294967294,
"scriptSig": {
"hex": "483045022100a6b7befee6468f01e2b3813f05b81241dd5a4c0b04d97a783940c75d97b85ba502206c0dd2fa5cc5c153d359c55f24407c97aec32bf42b2723fc40374188443bd4b2012103bbfb869bf408cfdc237c63c1dcec226faf56cc141c42ef6bb8ef4d156df669ea"
}
},
{
"txid": "5d1378e323fbb244099c11cc81a78ef0b7c2fc8c093fc34620cf8db22861e8d7",
"vout": 21,
"sequence": 4294967294,
"scriptSig": {
"hex": "47304402204116539acc084920a86bf837ade9d938b517dfa0d346beb969cc97505e6f21520220069a9a33f798a6c1912e29568be2cef12d4db0ea1db751054db5e2311b761854012103bbfb869bf408cfdc237c63c1dcec226faf56cc141c42ef6bb8ef4d156df669ea"
}
}
],
"vout": [
{
"value": 0.00047401,
"n": 0,
"scriptPubKey": {
"hex": "76a914da8378d88cd781e0a6f9fdfa6668dd47a718cf6188ac"
}
},
{
"value": 1.06800000,
"n": 1,
"scriptPubKey": {
"hex": "76a914707077511db03836716dc01ce47442596264fd8a88ac"
}
}
]
}
}
}

285
tests/sync/testdata/zcoin.json vendored 100644
View File

@ -0,0 +1,285 @@
{
"connectBlocks": {
"syncRanges": [
{ "lower": 10012, "upper": 10020 },
{ "lower": 121100, "upper": 121150 }
],
"blocks": {
"10012": {
"height": 10012,
"hash": "270364b0b441f4b5b4c98d073462c0f00e1200fec00410e27f4361e4695a8530",
"noTxs": 2,
"txDetails": [
{
"hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff26021c27062f503253482f043c424258081000cd76000000000d2f6e6f64655374726174756d2f0000000006406a7aee000000001976a9146934fe23ac758cbc21953fadfab01dd3671c01b688ac00c2eb0b000000001976a9147d9ed014fc4e603fca7c2e3f9097fb7d0fb487fc88ac00c2eb0b000000001976a914dcf01f01f5655c10d4fa8149d71cfee36313c02e88ac00c2eb0b000000001976a914ff71b0c9c2a90c6164a50a2fb523eb54a8a6b55088ac00c2eb0b000000001976a9140654dd9b856f2ece1d56cb4ee5043cd9398d962c88ac00c2eb0b000000001976a9140b4bfb256ef4bfa360e3b9e66e53a0bd84d196bc88ac00000000",
"txid": "ec09f614f427ac2553846d4e91bdbac34748502374bb981fc324f27f8c8683f6",
"blocktime": 1480737340,
"time": 1480737340,
"version": 1,
"vin": [
{
"coinbase": "021c27062f503253482f043c424258081000cd76000000000d2f6e6f64655374726174756d2f",
"sequence": 0
}
],
"vout": [
{
"value": 40.01,
"n": 0,
"scriptPubKey": {
"hex": "76a9146934fe23ac758cbc21953fadfab01dd3671c01b688ac"
}
},
{
"value": 2.0,
"n": 1,
"scriptPubKey": {
"hex": "76a9147d9ed014fc4e603fca7c2e3f9097fb7d0fb487fc88ac"
}
},
{
"value": 2.0,
"n": 2,
"scriptPubKey": {
"hex": "76a914dcf01f01f5655c10d4fa8149d71cfee36313c02e88ac"
}
},
{
"value": 2.0,
"n": 3,
"scriptPubKey": {
"hex": "76a914ff71b0c9c2a90c6164a50a2fb523eb54a8a6b55088ac"
}
},
{
"value": 2.0,
"n": 4,
"scriptPubKey": {
"hex": "76a9140654dd9b856f2ece1d56cb4ee5043cd9398d962c88ac"
}
},
{
"value": 2.0,
"n": 5,
"scriptPubKey": {
"hex": "76a9140b4bfb256ef4bfa360e3b9e66e53a0bd84d196bc88ac"
}
}
]
},
{
"hex": "0100000001a82d9bb9c02ad9eddfc9d760073b4fc2632b6c939a43d104a8ad08cebee12490000000006b483045022100ecadec884ee8e570bb77704672e223d5cfbaba0aa0957659b8efeabb71392587022076a17e47ac618bcf76c47f6a7fac739dad05f1efda3c61ec5d7cae7021088f370121021a1a3c0f862f7b757930d9654dce951175188eb49f659d9adce95113de5d05f2ffffffff02bc16367e000000001976a9149e694e14a690f487d2a42603dfa1e39e58adcb4688ac01a89721000000001976a914184f6e4e10fb4f1800d32537617528674b6afe0c88ac00000000",
"txid": "c04be7747ef077477aedbec55f1cf6fd51ec2efd58ff978f08884eab3c6fdfbf",
"time": 1480737340,
"blocktime": 1480737340,
"version": 1,
"vin": [
{
"txid": "9024e1bece08ada804d1439a936c2b63c24f3b0760d7c9dfedd92ac0b99b2da8",
"vout": 0,
"sequence": 4294967295,
"scriptSig": {
"hex": "483045022100ecadec884ee8e570bb77704672e223d5cfbaba0aa0957659b8efeabb71392587022076a17e47ac618bcf76c47f6a7fac739dad05f1efda3c61ec5d7cae7021088f370121021a1a3c0f862f7b757930d9654dce951175188eb49f659d9adce95113de5d05f2"
}
}
],
"vout": [
{
"value": 21.1747398,
"n": 0,
"scriptPubKey": {
"hex": "76a9149e694e14a690f487d2a42603dfa1e39e58adcb4688ac"
}
},
{
"value": 5.63587073,
"n": 1,
"scriptPubKey": {
"hex": "76a914184f6e4e10fb4f1800d32537617528674b6afe0c88ac"
}
}
]
}
]
},
"10013": {
"height": 10013,
"hash": "8be1836104f7adefc0ac8067f56abd1d30725dc05ccb19236be6f538637ec213",
"noTxs": 4,
"txDetails": [
{
"hex": "0100000001e9f4e2d5180ae0aa5b1ef1fdc496118d6226b688c7986dd9b4215fb4ad59b729010000006a47304402206261d51bf92238e28432307e92fc58af641a15db3a72cfc3a21d5a2ddfdc4d2e022060cd1044cc0a1a2932eaf642f9a4d08b62abb82bde6e7332f65aa343aa204434012102038af35cb691fb64a5af8ae4f3739a3a834363436bb7a4a5d98d9d9eaf46704effffffff02e3c72c00000000001976a914d475cafdcf52e81ee0ed36fa5b1aacebc9cc436f88ac6b638506000000001976a914c8f06a3a16c1210ae30c95111b60c481e20dcc0088ac00000000",
"txid": "01676407d835b8cb9a411136134efcfed3708ba123cb911532b63d9e464eae2e",
"version": 1,
"time": 1480737375,
"blocktime": 1480737375,
"vin": [
{
"txid": "29b759adb45f21b4d96d98c788b626628d1196c4fdf11e5baae00a18d5e2f4e9",
"vout": 1,
"scriptSig": {
"hex": "47304402206261d51bf92238e28432307e92fc58af641a15db3a72cfc3a21d5a2ddfdc4d2e022060cd1044cc0a1a2932eaf642f9a4d08b62abb82bde6e7332f65aa343aa204434012102038af35cb691fb64a5af8ae4f3739a3a834363436bb7a4a5d98d9d9eaf46704e"
},
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.02934755,
"n": 0,
"scriptPubKey": {
"hex": "76a914d475cafdcf52e81ee0ed36fa5b1aacebc9cc436f88ac"
}
},
{
"value": 1.09405035,
"n": 1,
"scriptPubKey": {
"hex": "76a914c8f06a3a16c1210ae30c95111b60c481e20dcc0088ac"
}
}
]
}
]
},
"121100": {
"height": 121100,
"hash": "02bc0d5ae888024d95f1887712def2639c341c8fbb85c25cc3684e8b2b60081f",
"noTxs": 30,
"txDetails": [
{
"hex": "01000000017ce8aed3eec1d7891141857700287f431090c1f9466e65b4d0f6a751feafa23c000000006b483045022100b7934000466f52a2cbdb63ea099ad30685c82778890f9b06facc09e6e9b3cb5502204a44c5b9c48c2ba4e61b7af467b384fbea8e1efb63296326ca222c97642d61b901210257436124422382febf4003f5bff403aeccab2b6ad901ea1c4d4e08ebb26eadabfeffffff0200105e5f000000001976a914732cd1f18bc5455be4c5f04b120dc4f06ebb2fbc88ac8cd81e03000000001976a914cfcce1c68a8974b1410e971708839fb95c0afcd788ac0bd90100",
"txid": "b71c6ed0b38c9a52fa02c25b79ba2b9f7da599fddae2fdf36dc2fa65bb127699",
"version": 1,
"vin": [
{
"txid": "3ca2affe51a7f6d0b4656e46f9c19010437f28007785411189d7c1eed3aee87c",
"vout": 0,
"scriptSig": {
"hex": "483045022100b7934000466f52a2cbdb63ea099ad30685c82778890f9b06facc09e6e9b3cb5502204a44c5b9c48c2ba4e61b7af467b384fbea8e1efb63296326ca222c97642d61b901210257436124422382febf4003f5bff403aeccab2b6ad901ea1c4d4e08ebb26eadab"
},
"sequence": 4294967294
}
],
"vout": [
{
"value": 16.0,
"n": 0,
"scriptPubKey": {
"hex": "76a914732cd1f18bc5455be4c5f04b120dc4f06ebb2fbc88ac"
}
},
{
"value": 0.52353164,
"n": 1,
"scriptPubKey": {
"hex": "76a914cfcce1c68a8974b1410e971708839fb95c0afcd788ac"
}
}
],
"time": 1545641453,
"blocktime": 1545641453
}
]
}
}
},
"handleFork": {
"syncRanges": [
{ "lower": 10012, "upper": 10017 },
{ "lower": 121100, "upper": 121104 }
],
"fakeBlocks": {
"10012": {
"height": 10012,
"hash": "8a647ed7cbc02600775dd6a7fff874a394a9abdfe5d86d9c46aa3d9a4307e010"
},
"10013": {
"height": 10013,
"hash": "005b810a796c05359264f847f968132981b608138e42655ffd9261e2a2f8e6c8"
},
"10014": {
"height": 10014,
"hash": "b11ee0e2620bc1a37da725f3cd4a1db9faec2b88ae3e77c9c4d66bbf5106bc44"
},
"10015": {
"height": 10015,
"hash": "712819a7a0c669c2367660cc246c725de58b68b7f77b803b1964a7c4343a115f"
},
"10016": {
"height": 10016,
"hash": "69b3ba86607980063eb9a04b9701c8d94206148bae1e3a4324e0fb36ca2e5d11"
},
"10017": {
"height": 10017,
"hash": "e60984c79e2723a19ebbc37f8d0c10e1e6294f1088c9bed3e532254f68c1a199"
},
"121100": {
"height": 121100,
"hash": "7eda2cf4cefb5945dbe60fd7e62296ff04fc8a2007f735d574edbbd6e30fb829"
},
"121101": {
"height": 121101,
"hash": "66d76166f8655a0394e26643c06bea61e0b1d0bc73acca3a53e48d80c7dab4af"
},
"121102": {
"height": 121102,
"hash": "65e94ebede51ea311784676b0673546e398b882b333be86ed16a100774bed32a"
},
"121103": {
"height": 121103,
"hash": "2d00b3d85882295717141e9c900d43d3e7b0a7e3ecdeaa05d32ed56d20577a97"
},
"121104": {
"height": 121104,
"hash": "199220df07b03ddacc3ba0e18a2562c83cb57dff0d12d8953b8233d5a47d7bf3"
}
},
"realBlocks": {
"10012": {
"height": 10012,
"hash": "270364b0b441f4b5b4c98d073462c0f00e1200fec00410e27f4361e4695a8530"
},
"10013": {
"height": 10013,
"hash": "8be1836104f7adefc0ac8067f56abd1d30725dc05ccb19236be6f538637ec213"
},
"10014": {
"height": 10014,
"hash": "def841c7406526043da07f202d67a0b1fe18d10650cbb3661a56722ad814815f"
},
"10015": {
"height": 10015,
"hash": "4dfab32ae8abb08ecd02b83305606d5d154e57018ff4d12cc744f9cd0b2d47b7"
},
"10016": {
"height": 10016,
"hash": "e4d49fd46e9d5e7488678356c91e7f8b4d2c69a0dd39e52e107a2987d41e230c"
},
"10017": {
"height": 10017,
"hash": "713837605fd64f8458cace5b961b28d0f1104665a054a6dde4b3572d520270f5"
},
"121100": {
"height": 121100,
"hash": "02bc0d5ae888024d95f1887712def2639c341c8fbb85c25cc3684e8b2b60081f"
},
"121101": {
"height": 121101,
"hash": "aa30be2d62075ee4d147149ef48ad42cd4f6d0fef90c0ec67c117f1b7d7e192f"
},
"121102": {
"height": 121102,
"hash": "77f49fc80ad9bb5398832332f98f7535e53971fab2dc81f7f3e1fc1dfac8ac7f"
},
"121103": {
"height": 121103,
"hash": "ed5947e57d68528929d32846a6b7942a5b9db4c98b9eacbd72e8662121bb7971"
},
"121104": {
"height": 121104,
"hash": "bea29f4df145ea43fd4450aad21ad79388f1fff36b9ab62a1b394440bf47eaf8"
}
}
}
}

View File

@ -95,5 +95,10 @@
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
},
"zcoin": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
}
}