Add decred support (#216)
This commit is contained in:
parent
72143f027c
commit
91691ed7e7
68
Gopkg.lock
generated
68
Gopkg.lock
generated
@ -26,6 +26,13 @@
|
||||
pruneopts = ""
|
||||
revision = "8e7d5b18fe7ad671e07097d5445dbc70422663b2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/agl/ed25519"
|
||||
packages = [".","edwards25519"]
|
||||
revision = "5312a61534124124185d41f09206b9fef1d88403"
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/beorn7/perks"
|
||||
@ -50,12 +57,36 @@
|
||||
packages = ["."]
|
||||
revision = "84c8d2346e9fc8c7b947e243b9c24e6df9fd206a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/dchest/blake256"
|
||||
packages = ["."]
|
||||
revision = "dee3fe6eb0e98dc774a94fc231f85baf7c29d360"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/deckarep/golang-set"
|
||||
packages = ["."]
|
||||
revision = "1d4478f51bed434f1dadf96dcd9b43aabac66795"
|
||||
version = "v1.7"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/decred/base58"
|
||||
packages = ["."]
|
||||
revision = "dbeddd8aab76c31eb2ea98351a63fa2c6bf46888"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/decred/dcrd"
|
||||
packages = ["chaincfg","chaincfg/chainec","chaincfg/chainhash","dcrec","dcrec/edwards","dcrec/secp256k1","dcrec/secp256k1/schnorr","dcrjson","dcrutil","txscript","wire"]
|
||||
revision = "0fe564967f03160b9dbe0a147420d8aa13371d12"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/decred/slog"
|
||||
packages = ["."]
|
||||
revision = "fbd821ef791ba2b8ae945f5d44f4e49396d230c5"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/ethereum/go-ethereum"
|
||||
packages = [".","common","common/hexutil","common/math","core/types","crypto","crypto/secp256k1","crypto/sha3","ethclient","ethdb","log","metrics","p2p/netutil","params","rlp","rpc","trie"]
|
||||
@ -227,6 +258,41 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "3a7b863bec3806ba9460f3961e2d6e8ff4b77e5f66d196b1f79bd37fa6bf8d82"
|
||||
input-imports = [
|
||||
"github.com/bsm/go-vlq",
|
||||
"github.com/deckarep/golang-set",
|
||||
"github.com/decred/dcrd/chaincfg",
|
||||
"github.com/decred/dcrd/dcrjson",
|
||||
"github.com/decred/dcrd/txscript",
|
||||
"github.com/ethereum/go-ethereum",
|
||||
"github.com/ethereum/go-ethereum/common",
|
||||
"github.com/ethereum/go-ethereum/common/hexutil",
|
||||
"github.com/ethereum/go-ethereum/core/types",
|
||||
"github.com/ethereum/go-ethereum/ethclient",
|
||||
"github.com/ethereum/go-ethereum/rpc",
|
||||
"github.com/gobuffalo/packr",
|
||||
"github.com/gogo/protobuf/proto",
|
||||
"github.com/golang/glog",
|
||||
"github.com/golang/protobuf/proto",
|
||||
"github.com/gorilla/websocket",
|
||||
"github.com/juju/errors",
|
||||
"github.com/martinboehm/bchutil",
|
||||
"github.com/martinboehm/btcd/blockchain",
|
||||
"github.com/martinboehm/btcd/chaincfg/chainhash",
|
||||
"github.com/martinboehm/btcd/txscript",
|
||||
"github.com/martinboehm/btcd/wire",
|
||||
"github.com/martinboehm/btcutil",
|
||||
"github.com/martinboehm/btcutil/base58",
|
||||
"github.com/martinboehm/btcutil/chaincfg",
|
||||
"github.com/martinboehm/btcutil/hdkeychain",
|
||||
"github.com/martinboehm/btcutil/txscript",
|
||||
"github.com/martinboehm/golang-socketio",
|
||||
"github.com/martinboehm/golang-socketio/transport",
|
||||
"github.com/pebbe/zmq4",
|
||||
"github.com/prometheus/client_golang/prometheus",
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp",
|
||||
"github.com/schancel/cashaddr-converter/address",
|
||||
"github.com/tecbot/gorocksdb",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"blockbook/bchain/coins/btc"
|
||||
"blockbook/bchain/coins/btg"
|
||||
"blockbook/bchain/coins/dash"
|
||||
"blockbook/bchain/coins/dcr"
|
||||
"blockbook/bchain/coins/digibyte"
|
||||
"blockbook/bchain/coins/dogecoin"
|
||||
"blockbook/bchain/coins/eth"
|
||||
@ -62,6 +63,8 @@ func init() {
|
||||
BlockChainFactories["Bgold"] = btg.NewBGoldRPC
|
||||
BlockChainFactories["Dash"] = dash.NewDashRPC
|
||||
BlockChainFactories["Dash Testnet"] = dash.NewDashRPC
|
||||
BlockChainFactories["Decred"] = dcr.NewDecredRPC
|
||||
BlockChainFactories["Decred Testnet"] = dcr.NewDecredRPC
|
||||
BlockChainFactories["GameCredits"] = gamecredits.NewGameCreditsRPC
|
||||
BlockChainFactories["Koto"] = koto.NewKotoRPC
|
||||
BlockChainFactories["Koto Testnet"] = koto.NewKotoRPC
|
||||
|
||||
231
bchain/coins/dcr/decredparser.go
Normal file
231
bchain/coins/dcr/decredparser.go
Normal file
@ -0,0 +1,231 @@
|
||||
package dcr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
"blockbook/bchain"
|
||||
"blockbook/bchain/coins/btc"
|
||||
"blockbook/bchain/coins/utils"
|
||||
|
||||
cfg "github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/martinboehm/btcd/wire"
|
||||
"github.com/martinboehm/btcutil/chaincfg"
|
||||
)
|
||||
|
||||
const (
|
||||
// MainnetMagic is mainnet network constant
|
||||
MainnetMagic wire.BitcoinNet = 0xd9b400f9
|
||||
// TestnetMagic is testnet network constant
|
||||
TestnetMagic wire.BitcoinNet = 0xb194aa75
|
||||
)
|
||||
|
||||
var (
|
||||
// MainNetParams are parser parameters for mainnet
|
||||
MainNetParams chaincfg.Params
|
||||
// TestNet3Params are parser parameters for testnet
|
||||
TestNet3Params chaincfg.Params
|
||||
)
|
||||
|
||||
func init() {
|
||||
MainNetParams = chaincfg.MainNetParams
|
||||
MainNetParams.Net = MainnetMagic
|
||||
MainNetParams.PubKeyHashAddrID = []byte{0x07, 0x3f}
|
||||
MainNetParams.ScriptHashAddrID = []byte{0x07, 0x1a}
|
||||
|
||||
TestNet3Params = chaincfg.TestNet3Params
|
||||
TestNet3Params.Net = TestnetMagic
|
||||
TestNet3Params.PubKeyHashAddrID = []byte{0x0f, 0x21}
|
||||
TestNet3Params.ScriptHashAddrID = []byte{0x0e, 0xfc}
|
||||
}
|
||||
|
||||
// DecredParser handle
|
||||
type DecredParser struct {
|
||||
*btc.BitcoinParser
|
||||
baseParser *bchain.BaseParser
|
||||
}
|
||||
|
||||
// NewDecredParser returns new DecredParser instance
|
||||
func NewDecredParser(params *chaincfg.Params, c *btc.Configuration) *DecredParser {
|
||||
return &DecredParser{
|
||||
BitcoinParser: btc.NewBitcoinParser(params, c),
|
||||
baseParser: &bchain.BaseParser{},
|
||||
}
|
||||
}
|
||||
|
||||
// GetChainParams contains network parameters for the main Decred network,
|
||||
// and the test Decred network.
|
||||
func GetChainParams(chain string) *chaincfg.Params {
|
||||
var param *chaincfg.Params
|
||||
|
||||
switch chain {
|
||||
case "testnet3":
|
||||
param = &TestNet3Params
|
||||
default:
|
||||
param = &MainNetParams
|
||||
}
|
||||
|
||||
if !chaincfg.IsRegistered(param) {
|
||||
if err := chaincfg.Register(param); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return param
|
||||
}
|
||||
|
||||
// ParseBlock parses raw block to our Block struct.
|
||||
func (p *DecredParser) ParseBlock(b []byte) (*bchain.Block, error) {
|
||||
r := bytes.NewReader(b)
|
||||
h := wire.BlockHeader{}
|
||||
if err := h.Deserialize(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if (h.Version & utils.VersionAuxpow) != 0 {
|
||||
if err := utils.SkipAuxpow(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var w wire.MsgBlock
|
||||
if err := utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (p *DecredParser) ParseTxFromJson(jsonTx json.RawMessage) (*bchain.Tx, error) {
|
||||
var getTxResult GetTransactionResult
|
||||
if err := json.Unmarshal([]byte(jsonTx), &getTxResult.Result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vins := make([]bchain.Vin, len(getTxResult.Result.Vin))
|
||||
for index, input := range getTxResult.Result.Vin {
|
||||
hexData := bchain.ScriptSig{}
|
||||
if input.ScriptSig != nil {
|
||||
hexData.Hex = input.ScriptSig.Hex
|
||||
}
|
||||
|
||||
vins[index] = bchain.Vin{
|
||||
Coinbase: input.Coinbase,
|
||||
Txid: input.Txid,
|
||||
Vout: input.Vout,
|
||||
ScriptSig: hexData,
|
||||
Sequence: input.Sequence,
|
||||
// Addresses: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
vouts := make([]bchain.Vout, len(getTxResult.Result.Vout))
|
||||
for index, output := range getTxResult.Result.Vout {
|
||||
addr := output.ScriptPubKey.Addresses
|
||||
// If nulldata type found make asm field the address data.
|
||||
if output.ScriptPubKey.Type == "nulldata" {
|
||||
addr = []string{output.ScriptPubKey.Asm}
|
||||
}
|
||||
|
||||
vouts[index] = bchain.Vout{
|
||||
ValueSat: *big.NewInt(int64(math.Round(output.Value * 1e8))),
|
||||
N: output.N,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: output.ScriptPubKey.Hex,
|
||||
Addresses: addr,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
tx := &bchain.Tx{
|
||||
Hex: getTxResult.Result.Hex,
|
||||
Txid: getTxResult.Result.Txid,
|
||||
Version: getTxResult.Result.Version,
|
||||
LockTime: getTxResult.Result.LockTime,
|
||||
Vin: vins,
|
||||
Vout: vouts,
|
||||
Confirmations: uint32(getTxResult.Result.Confirmations),
|
||||
Time: getTxResult.Result.Time,
|
||||
Blocktime: getTxResult.Result.Blocktime,
|
||||
}
|
||||
|
||||
tx.CoinSpecificData = getTxResult.Result.TxExtraInfo
|
||||
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
// GetAddrDescForUnknownInput returns nil AddressDescriptor
|
||||
func (p *DecredParser) GetAddrDescForUnknownInput(tx *bchain.Tx, input int) bchain.AddressDescriptor {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *DecredParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) {
|
||||
addressByte := []byte(address)
|
||||
return bchain.AddressDescriptor(addressByte), nil
|
||||
}
|
||||
|
||||
func (p *DecredParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) {
|
||||
script, err := hex.DecodeString(output.ScriptPubKey.Hex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var params cfg.Params
|
||||
if p.Params.Name == "mainnet" {
|
||||
params = cfg.MainNetParams
|
||||
} else {
|
||||
params = cfg.TestNet3Params
|
||||
}
|
||||
|
||||
scriptClass, addresses, _, err := txscript.ExtractPkScriptAddrs(txscript.DefaultScriptVersion, script, ¶ms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if scriptClass.String() == "nulldata" {
|
||||
if parsedOPReturn := p.BitcoinParser.TryParseOPReturn(script); parsedOPReturn != "" {
|
||||
return []byte(parsedOPReturn), nil
|
||||
}
|
||||
}
|
||||
|
||||
var addressByte []byte
|
||||
for i := range addresses {
|
||||
addressByte = append(addressByte, addresses[i].String()...)
|
||||
}
|
||||
|
||||
return bchain.AddressDescriptor(addressByte), nil
|
||||
}
|
||||
|
||||
func (p *DecredParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) {
|
||||
var addrs []string
|
||||
|
||||
if addrDesc != nil {
|
||||
addrs = append(addrs, string(addrDesc))
|
||||
}
|
||||
|
||||
return addrs, true, nil
|
||||
}
|
||||
|
||||
// PackTx packs transaction to byte array using protobuf
|
||||
func (p *DecredParser) 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 *DecredParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
|
||||
return p.baseParser.UnpackTx(buf)
|
||||
}
|
||||
238
bchain/coins/dcr/decredparser_test.go
Normal file
238
bchain/coins/dcr/decredparser_test.go
Normal file
@ -0,0 +1,238 @@
|
||||
// +build unittest
|
||||
|
||||
package dcr
|
||||
|
||||
import (
|
||||
"blockbook/bchain"
|
||||
"blockbook/bchain/coins/btc"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
parser *DecredParser
|
||||
|
||||
testTx1 = bchain.Tx{
|
||||
Hex: "01000000012372568fe80d2f9b2ab17226158dd5732d9926dc705371eaf40ab748c9e3d9720200000001ffffffff02644b252d0000000000001976a914a862f83733cc368f386a651e03d844a5bd6116d588acacdf63090000000000001976a91491dc5d18370939b3414603a0729bcb3a38e4ef7688ac000000000000000001e48d893600000000bb3d0000020000006a4730440220378e1442cc17fa7e49184518713eedd30e13e42147e077859557da6ffbbd40c702205f85563c28b6287f9c9110e6864dd18acfd92d85509ea846913c28b6e8a7f940012102bbbd7aadef33f2d2bdd9b0c5ba278815f5d66a6a01d2c019fb73f697662038b5",
|
||||
Blocktime: 1535632670,
|
||||
Time: 1535632670,
|
||||
Txid: "132acb5b474b45b830f7961c91c87e53cce3a37a6c6f0b0933ccdf0395c81a6a",
|
||||
LockTime: 0,
|
||||
Version: 1,
|
||||
Vin: []bchain.Vin{
|
||||
{
|
||||
Txid: "72d9e3c948b70af4ea715370dc26992d73d58d152672b12a9b2f0de88f567223",
|
||||
Vout: 2,
|
||||
Sequence: 4294967295,
|
||||
},
|
||||
},
|
||||
Vout: []bchain.Vout{
|
||||
{
|
||||
ValueSat: *big.NewInt(757418852),
|
||||
N: 0,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: "76a914a862f83733cc368f386a651e03d844a5bd6116d588ac",
|
||||
Addresses: []string{
|
||||
"TsgNUZKEnUhFASLESj7fVRTkgue3QR9TAeZ",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ValueSat: *big.NewInt(157540268),
|
||||
N: 1,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: "76a91491dc5d18370939b3414603a0729bcb3a38e4ef7688ac",
|
||||
Addresses: []string{
|
||||
"TseKNSWYbAzaGogpnNn25teTz53PTk3sgPu",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testTx2 = bchain.Tx{
|
||||
Hex: "0100000001c56d80756eaa7fc6e3542b29f596c60a9bcc959cf04d5f6e6b12749e241ece290200000001ffffffff02cf20b42d0000000000001976a9140799daa3cd36b44def220886802eb99e10c4a7c488ac0c25c7070000000000001976a9140b102deb3314213164cb6322211225365658407e88ac000000000000000001afa87b3500000000e33d0000000000006a47304402201ff342e5aa55b6030171f85729221ca0b81938826cc09449b77752e6e3b615be0220281e160b618e57326b95a0e0c3ac7a513bd041aba63cbace2f71919e111cfdba01210290a8de6665c8caac2bb8ca1aabd3dc09a334f997f97bd894772b1e51cab003d9",
|
||||
Blocktime: 1535638326,
|
||||
Time: 1535638326,
|
||||
Txid: "caf34c934d4c36b410c0265222b069f52e2df459ebb09d6797a635ceee0edd60",
|
||||
LockTime: 0,
|
||||
Version: 1,
|
||||
Vin: []bchain.Vin{
|
||||
{
|
||||
Txid: "29ce1e249e74126b6e5f4df09c95cc9b0ac696f5292b54e3c67faa6e75806dc5",
|
||||
Vout: 2,
|
||||
Sequence: 4294967295,
|
||||
},
|
||||
},
|
||||
Vout: []bchain.Vout{
|
||||
{
|
||||
ValueSat: *big.NewInt(766779599),
|
||||
N: 0,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: "76a9140799daa3cd36b44def220886802eb99e10c4a7c488ac",
|
||||
Addresses: []string{
|
||||
"TsRiKWsS9ucaqYDw9qhg6NukTthS5LwTRnv",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ValueSat: *big.NewInt(13049166),
|
||||
N: 1,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: "76a9140b102deb3314213164cb6322211225365658407e88ac",
|
||||
Addresses: []string{
|
||||
"TsS2dHqESY1vffjddpo1VMTbwLnDspfEj5W",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
parser = NewDecredParser(GetChainParams("testnet3"), &btc.Configuration{})
|
||||
exitCode := m.Run()
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
func TestGetAddrDescFromAddress(t *testing.T) {
|
||||
type args struct {
|
||||
address string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "P2PKH",
|
||||
args: args{address: "TcrypGAcGCRVXrES7hWqVZb5oLJKCZEtoL1"},
|
||||
want: "5463727970474163474352565872455337685771565a62356f4c4a4b435a45746f4c31",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "P2PKH",
|
||||
args: args{address: "TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd"},
|
||||
want: "547366444c72526b6b3963695575776670326238506177776e756b59443779416a4764",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "P2PKH",
|
||||
args: args{address: "TsTevp3WYTiV3X1qjvZqa7nutuTqt5VNeoU"},
|
||||
want: "547354657670335759546956335831716a765a7161376e75747554717435564e656f55",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
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.Fatalf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
h := hex.EncodeToString(got)
|
||||
if !reflect.DeepEqual(h, tt.want) {
|
||||
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAddrDescFromVout(t *testing.T) {
|
||||
type args struct {
|
||||
vout bchain.Vout
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "P2PK",
|
||||
args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "76a914936f3a56a2dd0fb3bfde6bc820d4643e1701542a88ac"}}},
|
||||
want: "54736554683431516f356b594c3337614c474d535167346e67636f71396a7a44583659",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "P2PK",
|
||||
args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "76a9144b31f712b03837b1303cddcb1ae9abd98da44f1088ac"}}},
|
||||
want: "547358736a3161747744736455746e354455576b666f6d5a586e4a6151467862395139",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "P2PK",
|
||||
args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "76a9140d85a1d3f77383eb3dacfd83c46e2c7915aba91d88ac"}}},
|
||||
want: "54735346644c79657942776e68486978737367784b34546f4664763876525931793871",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
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.Fatalf("GetAddrDescFromVout() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
h := hex.EncodeToString(got)
|
||||
if !reflect.DeepEqual(h, tt.want) {
|
||||
t.Errorf("GetAddrDescFromVout() = %v, want %v", h, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAddressesFromAddrDesc(t *testing.T) {
|
||||
type args struct {
|
||||
script string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
want2 bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "P2PKH",
|
||||
args: args{script: "5463727970474163474352565872455337685771565a62356f4c4a4b435a45746f4c31"},
|
||||
want: []string{"TcrypGAcGCRVXrES7hWqVZb5oLJKCZEtoL1"},
|
||||
want2: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "P2PKH",
|
||||
args: args{script: "547366444c72526b6b3963695575776670326238506177776e756b59443779416a4764"},
|
||||
want: []string{"TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd"},
|
||||
want2: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "P2PKH",
|
||||
args: args{script: "547354657670335759546956335831716a765a7161376e75747554717435564e656f55"},
|
||||
want: []string{"TsTevp3WYTiV3X1qjvZqa7nutuTqt5VNeoU"},
|
||||
want2: true,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
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.Fatalf("GetAddressesFromAddrDesc() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
882
bchain/coins/dcr/decredrpc.go
Normal file
882
bchain/coins/dcr/decredrpc.go
Normal file
@ -0,0 +1,882 @@
|
||||
package dcr
|
||||
|
||||
import (
|
||||
"blockbook/bchain"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"blockbook/bchain/coins/btc"
|
||||
|
||||
"github.com/decred/dcrd/dcrjson"
|
||||
"github.com/golang/glog"
|
||||
"github.com/juju/errors"
|
||||
)
|
||||
|
||||
// voteBitYes defines the vote bit set when a given block validates the previous
|
||||
// block
|
||||
const voteBitYes = 0x0001
|
||||
|
||||
type DecredRPC struct {
|
||||
*btc.BitcoinRPC
|
||||
mtx sync.Mutex
|
||||
client http.Client
|
||||
rpcURL string
|
||||
rpcUser string
|
||||
bestBlock uint32
|
||||
rpcPassword string
|
||||
}
|
||||
|
||||
// NewDecredRPC returns new DecredRPC instance.
|
||||
func NewDecredRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
|
||||
b, err := btc.NewBitcoinRPC(config, pushHandler)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var c btc.Configuration
|
||||
if err = json.Unmarshal(config, &c); err != nil {
|
||||
return nil, errors.Annotate(err, "Invalid configuration file")
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
Dial: (&net.Dialer{KeepAlive: 600 * time.Second}).Dial,
|
||||
MaxIdleConns: 100,
|
||||
MaxIdleConnsPerHost: 100, // necessary to not to deplete ports
|
||||
}
|
||||
|
||||
d := &DecredRPC{
|
||||
BitcoinRPC: b.(*btc.BitcoinRPC),
|
||||
client: http.Client{Timeout: time.Duration(c.RPCTimeout) * time.Second, Transport: transport},
|
||||
rpcURL: c.RPCURL,
|
||||
rpcUser: c.RPCUser,
|
||||
rpcPassword: c.RPCPass,
|
||||
}
|
||||
|
||||
d.BitcoinRPC.RPCMarshaler = btc.JSONMarshalerV1{}
|
||||
d.BitcoinRPC.ChainConfig.SupportsEstimateSmartFee = false
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// Initialize initializes DecredRPC instance.
|
||||
func (d *DecredRPC) Initialize() error {
|
||||
chainInfo, err := d.GetChainInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chainName := chainInfo.Chain
|
||||
glog.Info("Chain name ", chainName)
|
||||
|
||||
params := GetChainParams(chainName)
|
||||
|
||||
// always create parser
|
||||
d.BitcoinRPC.Parser = NewDecredParser(params, d.BitcoinRPC.ChainConfig)
|
||||
|
||||
// parameters for getInfo request
|
||||
if params.Net == MainnetMagic {
|
||||
d.BitcoinRPC.Testnet = false
|
||||
d.BitcoinRPC.Network = "livenet"
|
||||
} else {
|
||||
d.BitcoinRPC.Testnet = true
|
||||
d.BitcoinRPC.Network = "testnet"
|
||||
}
|
||||
|
||||
glog.Info("rpc: block chain ", params.Name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type GenericCmd struct {
|
||||
ID int `json:"id"`
|
||||
Method string `json:"method"`
|
||||
Params []interface{} `json:"params,omitempty"`
|
||||
}
|
||||
|
||||
type GetBlockChainInfoResult struct {
|
||||
Error Error `json:"error"`
|
||||
Result struct {
|
||||
Chain string `json:"chain"`
|
||||
Blocks int64 `json:"blocks"`
|
||||
Headers int64 `json:"headers"`
|
||||
SyncHeight int64 `json:"syncheight"`
|
||||
BestBlockHash string `json:"bestblockhash"`
|
||||
Difficulty uint32 `json:"difficulty"`
|
||||
VerificationProgress float64 `json:"verificationprogress"`
|
||||
ChainWork string `json:"chainwork"`
|
||||
InitialBlockDownload bool `json:"initialblockdownload"`
|
||||
MaxBlockSize int64 `json:"maxblocksize"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
type GetNetworkInfoResult struct {
|
||||
Error Error `json:"error"`
|
||||
Result struct {
|
||||
Version int32 `json:"version"`
|
||||
ProtocolVersion int32 `json:"protocolversion"`
|
||||
TimeOffset int64 `json:"timeoffset"`
|
||||
Connections int32 `json:"connections"`
|
||||
RelayFee float64 `json:"relayfee"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
type GetInfoChainResult struct {
|
||||
Error Error `json:"error"`
|
||||
Result struct {
|
||||
Version int32 `json:"version"`
|
||||
ProtocolVersion int32 `json:"protocolversion"`
|
||||
Blocks int64 `json:"blocks"`
|
||||
TimeOffset int64 `json:"timeoffset"`
|
||||
Connections int32 `json:"connections"`
|
||||
Proxy string `json:"proxy"`
|
||||
Difficulty float64 `json:"difficulty"`
|
||||
TestNet bool `json:"testnet"`
|
||||
RelayFee float64 `json:"relayfee"`
|
||||
Errors string `json:"errors"`
|
||||
}
|
||||
}
|
||||
|
||||
type GetBestBlockResult struct {
|
||||
Error Error `json:"error"`
|
||||
Result struct {
|
||||
Hash string `json:"hash"`
|
||||
Height uint32 `json:"height"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
type GetBlockHashResult struct {
|
||||
Error Error `json:"error"`
|
||||
Result string `json:"result"`
|
||||
}
|
||||
|
||||
type GetBlockResult struct {
|
||||
Error Error `json:"error"`
|
||||
Result struct {
|
||||
Hash string `json:"hash"`
|
||||
Confirmations int64 `json:"confirmations"`
|
||||
Size int32 `json:"size"`
|
||||
Height uint32 `json:"height"`
|
||||
Version json.Number `json:"version"`
|
||||
MerkleRoot string `json:"merkleroot"`
|
||||
StakeRoot string `json:"stakeroot"`
|
||||
RawTx []RawTx `json:"rawtx"`
|
||||
Tx []string `json:"tx,omitempty"`
|
||||
STx []string `json:"stx,omitempty"`
|
||||
Time int64 `json:"time"`
|
||||
Nonce json.Number `json:"nonce"`
|
||||
VoteBits uint16 `json:"votebits"`
|
||||
FinalState string `json:"finalstate"`
|
||||
Voters uint16 `json:"voters"`
|
||||
FreshStake uint8 `json:"freshstake"`
|
||||
Revocations uint8 `json:"revocations"`
|
||||
PoolSize uint32 `json:"poolsize"`
|
||||
Bits string `json:"bits"`
|
||||
SBits float64 `json:"sbits"`
|
||||
ExtraData string `json:"extradata"`
|
||||
StakeVersion uint32 `json:"stakeversion"`
|
||||
Difficulty float64 `json:"difficulty"`
|
||||
ChainWork string `json:"chainwork"`
|
||||
PreviousHash string `json:"previousblockhash"`
|
||||
NextHash string `json:"nextblockhash,omitempty"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
type GetBlockHeaderResult struct {
|
||||
Error Error `json:"error"`
|
||||
Result struct {
|
||||
Hash string `json:"hash"`
|
||||
Confirmations int64 `json:"confirmations"`
|
||||
Version json.Number `json:"version"`
|
||||
MerkleRoot string `json:"merkleroot"`
|
||||
StakeRoot string `json:"stakeroot"`
|
||||
VoteBits uint16 `json:"votebits"`
|
||||
FinalState string `json:"finalstate"`
|
||||
Voters uint16 `json:"voters"`
|
||||
FreshStake uint8 `json:"freshstake"`
|
||||
Revocations uint8 `json:"revocations"`
|
||||
PoolSize uint32 `json:"poolsize"`
|
||||
Bits string `json:"bits"`
|
||||
SBits float64 `json:"sbits"`
|
||||
Height uint32 `json:"height"`
|
||||
Size uint32 `json:"size"`
|
||||
Time int64 `json:"time"`
|
||||
Nonce uint32 `json:"nonce"`
|
||||
ExtraData string `json:"extradata"`
|
||||
StakeVersion uint32 `json:"stakeversion"`
|
||||
Difficulty float64 `json:"difficulty"`
|
||||
ChainWork string `json:"chainwork"`
|
||||
PreviousHash string `json:"previousblockhash,omitempty"`
|
||||
NextHash string `json:"nextblockhash,omitempty"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
type ScriptSig struct {
|
||||
Asm string `json:"asm"`
|
||||
Hex string `json:"hex"`
|
||||
}
|
||||
|
||||
type Vin struct {
|
||||
Coinbase string `json:"coinbase"`
|
||||
Stakebase string `json:"stakebase"`
|
||||
Txid string `json:"txid"`
|
||||
Vout uint32 `json:"vout"`
|
||||
Tree int8 `json:"tree"`
|
||||
Sequence uint32 `json:"sequence"`
|
||||
AmountIn float64 `json:"amountin"`
|
||||
BlockHeight uint32 `json:"blockheight"`
|
||||
BlockIndex uint32 `json:"blockindex"`
|
||||
ScriptSig *ScriptSig `json:"scriptsig"`
|
||||
}
|
||||
|
||||
type ScriptPubKeyResult struct {
|
||||
Asm string `json:"asm"`
|
||||
Hex string `json:"hex,omitempty"`
|
||||
ReqSigs int32 `json:"reqSigs,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Addresses []string `json:"addresses,omitempty"`
|
||||
CommitAmt *float64 `json:"commitamt,omitempty"`
|
||||
}
|
||||
|
||||
type Vout struct {
|
||||
Value float64 `json:"value"`
|
||||
N uint32 `json:"n"`
|
||||
Version uint16 `json:"version"`
|
||||
ScriptPubKey ScriptPubKeyResult `json:"scriptPubKey"`
|
||||
}
|
||||
|
||||
type RawTx struct {
|
||||
Hex string `json:"hex"`
|
||||
Txid string `json:"txid"`
|
||||
Version int32 `json:"version"`
|
||||
LockTime uint32 `json:"locktime"`
|
||||
Vin []Vin `json:"vin"`
|
||||
Vout []Vout `json:"vout"`
|
||||
Expiry uint32 `json:"expiry"`
|
||||
BlockIndex uint32 `json:"blockindex,omitempty"`
|
||||
Confirmations int64 `json:"confirmations,omitempty"`
|
||||
Time int64 `json:"time,omitempty"`
|
||||
Blocktime int64 `json:"blocktime,omitempty"`
|
||||
TxExtraInfo
|
||||
}
|
||||
|
||||
type GetTransactionResult struct {
|
||||
Error Error `json:"error"`
|
||||
Result struct {
|
||||
RawTx
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
type MempoolTxsResult struct {
|
||||
Error Error `json:"error"`
|
||||
Result []string `json:"result"`
|
||||
}
|
||||
|
||||
type EstimateSmartFeeResult struct {
|
||||
Error Error `json:"error"`
|
||||
Result struct {
|
||||
FeeRate float64 `json:"feerate"`
|
||||
Errors []string `json:"errors"`
|
||||
Blocks int64 `json:"blocks"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
type EstimateFeeResult struct {
|
||||
Error Error `json:"error"`
|
||||
Result json.Number `json:"result"`
|
||||
}
|
||||
|
||||
type SendRawTransactionResult struct {
|
||||
Error Error `json:"error"`
|
||||
Result string `json:"result"`
|
||||
}
|
||||
|
||||
type DecodeRawTransactionResult struct {
|
||||
Error Error `json:"error"`
|
||||
Result struct {
|
||||
Txid string `json:"txid"`
|
||||
Version int32 `json:"version"`
|
||||
Locktime uint32 `json:"locktime"`
|
||||
Expiry uint32 `json:"expiry"`
|
||||
Vin []Vin `json:"vin"`
|
||||
Vout []Vout `json:"vout"`
|
||||
TxExtraInfo
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
type TxExtraInfo struct {
|
||||
BlockHeight uint32 `json:"blockheight,omitempty"`
|
||||
BlockHash string `json:"blockhash,omitempty"`
|
||||
}
|
||||
|
||||
func (d *DecredRPC) GetChainInfo() (*bchain.ChainInfo, error) {
|
||||
blockchainInfoRequest := GenericCmd{
|
||||
ID: 1,
|
||||
Method: "getblockchaininfo",
|
||||
}
|
||||
|
||||
var blockchainInfoResult GetBlockChainInfoResult
|
||||
if err := d.Call(blockchainInfoRequest, &blockchainInfoResult); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if blockchainInfoResult.Error.Message != "" {
|
||||
return nil, mapToStandardErr("Error fetching blockchain info: %s", blockchainInfoResult.Error)
|
||||
}
|
||||
|
||||
infoChainRequest := GenericCmd{
|
||||
ID: 2,
|
||||
Method: "getinfo",
|
||||
}
|
||||
|
||||
var infoChainResult GetInfoChainResult
|
||||
if err := d.Call(infoChainRequest, &infoChainResult); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if infoChainResult.Error.Message != "" {
|
||||
return nil, mapToStandardErr("Error fetching network info: %s", infoChainResult.Error)
|
||||
}
|
||||
|
||||
chainInfo := &bchain.ChainInfo{
|
||||
Chain: blockchainInfoResult.Result.Chain,
|
||||
Blocks: int(blockchainInfoResult.Result.Blocks),
|
||||
Headers: int(blockchainInfoResult.Result.Headers),
|
||||
Bestblockhash: blockchainInfoResult.Result.BestBlockHash,
|
||||
Difficulty: strconv.Itoa(int(blockchainInfoResult.Result.Difficulty)),
|
||||
SizeOnDisk: blockchainInfoResult.Result.SyncHeight,
|
||||
Version: strconv.Itoa(int(infoChainResult.Result.Version)),
|
||||
Subversion: "",
|
||||
ProtocolVersion: strconv.Itoa(int(infoChainResult.Result.ProtocolVersion)),
|
||||
Timeoffset: float64(infoChainResult.Result.TimeOffset),
|
||||
Warnings: "",
|
||||
}
|
||||
return chainInfo, nil
|
||||
}
|
||||
|
||||
// getChainBestBlock returns the best block according to dcrd chain. This block
|
||||
// has no atleast one confirming block.
|
||||
func (d *DecredRPC) getChainBestBlock() (*GetBestBlockResult, error) {
|
||||
bestBlockRequest := GenericCmd{
|
||||
ID: 1,
|
||||
Method: "getbestblock",
|
||||
}
|
||||
|
||||
var bestBlockResult GetBestBlockResult
|
||||
if err := d.Call(bestBlockRequest, &bestBlockResult); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bestBlockResult.Error.Message != "" {
|
||||
return nil, mapToStandardErr("Error fetching best block: %s", bestBlockResult.Error)
|
||||
}
|
||||
|
||||
return &bestBlockResult, nil
|
||||
}
|
||||
|
||||
// getBestBlock returns details for the block mined immediately before the
|
||||
// official dcrd chain's bestblock i.e. it has a minimum of 1 confirmation.
|
||||
// The chain's best block is not returned as its block validity is not guarranteed.
|
||||
func (d *DecredRPC) getBestBlock() (*GetBestBlockResult, error) {
|
||||
bestBlockResult, err := d.getChainBestBlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// remove the block with less than 1 confirming block
|
||||
bestBlockResult.Result.Height--
|
||||
validBlockHash, err := d.getBlockHashByHeight(bestBlockResult.Result.Height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bestBlockResult.Result.Hash = validBlockHash.Result
|
||||
|
||||
return bestBlockResult, nil
|
||||
}
|
||||
|
||||
// GetBestBlockHash returns the block hash of the most recent block to be mined
|
||||
// and has a minimum of 1 confirming block.
|
||||
func (d *DecredRPC) GetBestBlockHash() (string, error) {
|
||||
bestBlock, err := d.getBestBlock()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return bestBlock.Result.Hash, nil
|
||||
}
|
||||
|
||||
// GetBestBlockHeight returns the block height of the most recent block to be mined
|
||||
// and has a minimum of 1 confirming block.
|
||||
func (d *DecredRPC) GetBestBlockHeight() (uint32, error) {
|
||||
bestBlock, err := d.getBestBlock()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint32(bestBlock.Result.Height), err
|
||||
}
|
||||
|
||||
// GetBlockHash returns the block hash of the block at the provided height.
|
||||
func (d *DecredRPC) GetBlockHash(height uint32) (string, error) {
|
||||
blockHashResult, err := d.getBlockHashByHeight(height)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return blockHashResult.Result, nil
|
||||
}
|
||||
|
||||
func (d *DecredRPC) getBlockHashByHeight(height uint32) (*GetBlockHashResult, error) {
|
||||
blockHashRequest := GenericCmd{
|
||||
ID: 1,
|
||||
Method: "getblockhash",
|
||||
Params: []interface{}{height},
|
||||
}
|
||||
|
||||
var blockHashResult GetBlockHashResult
|
||||
if err := d.Call(blockHashRequest, &blockHashResult); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if blockHashResult.Error.Message != "" {
|
||||
return nil, mapToStandardErr("Error fetching block hash: %s", blockHashResult.Error)
|
||||
}
|
||||
|
||||
return &blockHashResult, nil
|
||||
}
|
||||
|
||||
// GetBlockHeader returns the block header of the block the provided block hash.
|
||||
func (d *DecredRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) {
|
||||
blockHeaderRequest := GenericCmd{
|
||||
ID: 1,
|
||||
Method: "getblockheader",
|
||||
Params: []interface{}{hash},
|
||||
}
|
||||
|
||||
var blockHeader GetBlockHeaderResult
|
||||
if err := d.Call(blockHeaderRequest, &blockHeader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if blockHeader.Error.Message != "" {
|
||||
return nil, mapToStandardErr("Error fetching block info: %s", blockHeader.Error)
|
||||
}
|
||||
|
||||
header := &bchain.BlockHeader{
|
||||
Hash: blockHeader.Result.Hash,
|
||||
Prev: blockHeader.Result.PreviousHash,
|
||||
Next: blockHeader.Result.NextHash,
|
||||
Height: blockHeader.Result.Height,
|
||||
Confirmations: int(blockHeader.Result.Confirmations),
|
||||
Size: int(blockHeader.Result.Size),
|
||||
Time: blockHeader.Result.Time,
|
||||
}
|
||||
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func (d *DecredRPC) GetBlockHeaderByHeight(height uint32) (*bchain.BlockHeader, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetBlock returns the block retreived using the provided block hash by default
|
||||
// or using the block height if an empty hash string was provided. If the
|
||||
// requested block has less than 2 confirmation bchain.ErrBlockNotFound error
|
||||
// is returned. This rule is in places to guarrantee that only validated block
|
||||
// details (txs) are saved to the db. Access to the bestBlock height is threadsafe.
|
||||
func (d *DecredRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
|
||||
// Confirm if the block at provided height has at least 2 confirming blocks.
|
||||
d.mtx.Lock()
|
||||
var bestBlockHeight = d.bestBlock
|
||||
if height > bestBlockHeight {
|
||||
bestBlock, err := d.getBestBlock()
|
||||
if err != nil || height > bestBlock.Result.Height {
|
||||
// If an error occured or the current height doesn't have a minimum
|
||||
// of two confirming blocks (greater than best block), quit.
|
||||
d.mtx.Unlock()
|
||||
return nil, bchain.ErrBlockNotFound
|
||||
}
|
||||
|
||||
d.bestBlock = bestBlock.Result.Height
|
||||
bestBlockHeight = bestBlock.Result.Height
|
||||
}
|
||||
d.mtx.Unlock() // Releases the lock soonest possible
|
||||
|
||||
if hash == "" {
|
||||
getHashResult, err := d.getBlockHashByHeight(height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hash = getHashResult.Result
|
||||
}
|
||||
|
||||
block, err := d.getBlock(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
header := bchain.BlockHeader{
|
||||
Hash: block.Result.Hash,
|
||||
Prev: block.Result.PreviousHash,
|
||||
Next: block.Result.NextHash,
|
||||
Height: block.Result.Height,
|
||||
Confirmations: int(block.Result.Confirmations),
|
||||
Size: int(block.Result.Size),
|
||||
Time: block.Result.Time,
|
||||
}
|
||||
|
||||
bchainBlock := &bchain.Block{BlockHeader: header}
|
||||
|
||||
// Check the current block validity by fetch the next block
|
||||
nextBlockHashResult, err := d.getBlockHashByHeight(height + 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nextBlock, err := d.getBlock(nextBlockHashResult.Result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the Votesbits set equals to voteBitYes append the regular transactions.
|
||||
if nextBlock.Result.VoteBits == voteBitYes {
|
||||
for _, txID := range block.Result.Tx {
|
||||
if block.Result.Height == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
tx, err := d.GetTransaction(txID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bchainBlock.Txs = append(bchainBlock.Txs, *tx)
|
||||
}
|
||||
}
|
||||
|
||||
return bchainBlock, nil
|
||||
}
|
||||
|
||||
func (d *DecredRPC) getBlock(hash string) (*GetBlockResult, error) {
|
||||
blockRequest := GenericCmd{
|
||||
ID: 1,
|
||||
Method: "getblock",
|
||||
Params: []interface{}{hash},
|
||||
}
|
||||
|
||||
var block GetBlockResult
|
||||
if err := d.Call(blockRequest, &block); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if block.Error.Message != "" {
|
||||
return nil, mapToStandardErr("Error fetching block info: %s", block.Error)
|
||||
}
|
||||
|
||||
return &block, nil
|
||||
}
|
||||
|
||||
func (d *DecredRPC) decodeRawTransaction(txHex string) (*bchain.Tx, error) {
|
||||
decodeRawTxRequest := GenericCmd{
|
||||
ID: 1,
|
||||
Method: "decoderawtransaction",
|
||||
Params: []interface{}{txHex},
|
||||
}
|
||||
|
||||
var decodeRawTxResult DecodeRawTransactionResult
|
||||
if err := d.Call(decodeRawTxRequest, &decodeRawTxResult); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if decodeRawTxResult.Error.Message != "" {
|
||||
return nil, mapToStandardErr("Error decoding raw tx: %s", decodeRawTxResult.Error)
|
||||
}
|
||||
|
||||
tx := &bchain.Tx{
|
||||
Hex: txHex,
|
||||
Txid: decodeRawTxResult.Result.Txid,
|
||||
Version: decodeRawTxResult.Result.Version,
|
||||
LockTime: decodeRawTxResult.Result.Locktime,
|
||||
}
|
||||
|
||||
// Add block height and block hash info
|
||||
tx.CoinSpecificData = decodeRawTxResult.Result.TxExtraInfo
|
||||
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
func (d *DecredRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
|
||||
block, err := d.getBlock(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
header := bchain.BlockHeader{
|
||||
Hash: block.Result.Hash,
|
||||
Prev: block.Result.PreviousHash,
|
||||
Next: block.Result.NextHash,
|
||||
Height: block.Result.Height,
|
||||
Confirmations: int(block.Result.Confirmations),
|
||||
Size: int(block.Result.Size),
|
||||
Time: int64(block.Result.Time),
|
||||
}
|
||||
|
||||
bInfo := &bchain.BlockInfo{
|
||||
BlockHeader: header,
|
||||
MerkleRoot: block.Result.MerkleRoot,
|
||||
Version: block.Result.Version,
|
||||
Nonce: block.Result.Nonce,
|
||||
Bits: block.Result.Bits,
|
||||
Difficulty: json.Number(strconv.FormatFloat(block.Result.Difficulty, 'e', -1, 64)),
|
||||
Txids: block.Result.Tx,
|
||||
}
|
||||
|
||||
return bInfo, nil
|
||||
}
|
||||
|
||||
// GetTransaction returns a transaction by the transaction ID
|
||||
func (d *DecredRPC) GetTransaction(txid string) (*bchain.Tx, error) {
|
||||
r, err := d.getRawTransaction(txid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tx, err := d.Parser.ParseTxFromJson(r)
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "txid %v", txid)
|
||||
}
|
||||
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
// getRawTransaction returns json as returned by backend, with all coin specific data
|
||||
func (d *DecredRPC) getRawTransaction(txid string) (json.RawMessage, error) {
|
||||
if txid == "" {
|
||||
return nil, bchain.ErrTxidMissing
|
||||
}
|
||||
|
||||
verbose := 1
|
||||
getTxRequest := GenericCmd{
|
||||
ID: 1,
|
||||
Method: "getrawtransaction",
|
||||
Params: []interface{}{txid, &verbose},
|
||||
}
|
||||
|
||||
var getTxResult GetTransactionResult
|
||||
if err := d.Call(getTxRequest, &getTxResult); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if getTxResult.Error.Message != "" {
|
||||
return nil, mapToStandardErr("Error fetching transaction: %s", getTxResult.Error)
|
||||
}
|
||||
|
||||
bytes, err := json.Marshal(getTxResult.Result)
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "txid %v", txid)
|
||||
}
|
||||
|
||||
return json.RawMessage(bytes), nil
|
||||
}
|
||||
|
||||
// GetTransactionForMempool returns the full tx information identified by the
|
||||
// provided txid.
|
||||
func (d *DecredRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
|
||||
return d.GetTransaction(txid)
|
||||
}
|
||||
|
||||
// GetMempoolTransactions returns a slice of regular transactions currently in
|
||||
// the mempool. The block whose validation is still undecided will have its txs,
|
||||
// listed like they are still in the mempool till the block is confirmed.
|
||||
func (d *DecredRPC) GetMempoolTransactions() ([]string, error) {
|
||||
verbose := false
|
||||
txType := "regular"
|
||||
mempoolRequest := GenericCmd{
|
||||
ID: 1,
|
||||
Method: "getrawmempool",
|
||||
Params: []interface{}{&verbose, &txType},
|
||||
}
|
||||
|
||||
var mempool MempoolTxsResult
|
||||
if err := d.Call(mempoolRequest, &mempool); err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
if mempool.Error.Message != "" {
|
||||
return nil, mapToStandardErr("Error fetching mempool data: %s", mempool.Error)
|
||||
}
|
||||
|
||||
unvalidatedBlockResult, err := d.getChainBestBlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
unvalidatedBlock, err := d.getBlock(unvalidatedBlockResult.Result.Hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mempool.Result = append(mempool.Result, unvalidatedBlock.Result.Tx...)
|
||||
|
||||
return mempool.Result, nil
|
||||
}
|
||||
|
||||
// GetTransactionSpecific returns the json raw message for the tx identified by
|
||||
// the provided txid.
|
||||
func (d *DecredRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, error) {
|
||||
return d.getRawTransaction(tx.Txid)
|
||||
}
|
||||
|
||||
// EstimateSmartFee returns fee estimation
|
||||
func (d *DecredRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
|
||||
estimateSmartFeeRequest := GenericCmd{
|
||||
ID: 1,
|
||||
Method: "estimatesmartfee",
|
||||
Params: []interface{}{blocks},
|
||||
}
|
||||
|
||||
var smartFeeEstimate EstimateSmartFeeResult
|
||||
if err := d.Call(estimateSmartFeeRequest, &smartFeeEstimate); err != nil {
|
||||
return *big.NewInt(0), err
|
||||
}
|
||||
|
||||
if smartFeeEstimate.Error.Message != "" {
|
||||
return *big.NewInt(0), mapToStandardErr("Error fetching smart fee estimate: %s", smartFeeEstimate.Error)
|
||||
}
|
||||
|
||||
return *big.NewInt(int64(smartFeeEstimate.Result.FeeRate)), nil
|
||||
}
|
||||
|
||||
// EstimateFee returns fee estimation.
|
||||
func (d *DecredRPC) EstimateFee(blocks int) (big.Int, error) {
|
||||
estimateFeeRequest := GenericCmd{
|
||||
ID: 1,
|
||||
Method: "estimatefee",
|
||||
Params: []interface{}{blocks},
|
||||
}
|
||||
|
||||
var feeEstimate EstimateFeeResult
|
||||
if err := d.Call(estimateFeeRequest, &feeEstimate); err != nil {
|
||||
return *big.NewInt(0), err
|
||||
}
|
||||
|
||||
if feeEstimate.Error.Message != "" {
|
||||
return *big.NewInt(0), mapToStandardErr("Error fetching fee estimate: %s", feeEstimate.Error)
|
||||
}
|
||||
|
||||
r, err := d.Parser.AmountToBigInt(feeEstimate.Result)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (d *DecredRPC) SendRawTransaction(tx string) (string, error) {
|
||||
sendRawTxRequest := &GenericCmd{
|
||||
ID: 1,
|
||||
Method: "sendrawtransaction",
|
||||
Params: []interface{}{tx},
|
||||
}
|
||||
|
||||
var sendRawTxResult SendRawTransactionResult
|
||||
err := d.Call(sendRawTxRequest, &sendRawTxResult)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if sendRawTxResult.Error.Message != "" {
|
||||
return "", mapToStandardErr("error sending raw transaction: %s", sendRawTxResult.Error)
|
||||
}
|
||||
|
||||
return sendRawTxResult.Result, nil
|
||||
}
|
||||
|
||||
// Call calls Backend RPC interface, using RPCMarshaler interface to marshall the request
|
||||
func (d *DecredRPC) Call(req interface{}, res interface{}) error {
|
||||
httpData, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
httpReq, err := http.NewRequest("POST", d.rpcURL, bytes.NewBuffer(httpData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpReq.SetBasicAuth(d.rpcUser, d.rpcPassword)
|
||||
httpRes, err := d.client.Do(httpReq)
|
||||
// in some cases the httpRes can contain data even if it returns error
|
||||
// see http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/
|
||||
if httpRes != nil {
|
||||
defer httpRes.Body.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if server returns HTTP error code it might not return json with response
|
||||
// handle both cases
|
||||
if httpRes.StatusCode != 200 {
|
||||
if err = safeDecodeResponse(httpRes.Body, &res); err != nil {
|
||||
return errors.Errorf("%v %v", httpRes.Status, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return safeDecodeResponse(httpRes.Body, &res)
|
||||
}
|
||||
|
||||
func safeDecodeResponse(body io.ReadCloser, res *interface{}) (err error) {
|
||||
var data []byte
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
glog.Error("unmarshal json recovered from panic: ", r, "; data: ", string(data))
|
||||
debug.PrintStack()
|
||||
if len(data) > 0 && len(data) < 2048 {
|
||||
err = errors.Errorf("Error: %v", string(data))
|
||||
} else {
|
||||
err = errors.New("Internal error")
|
||||
}
|
||||
}
|
||||
}()
|
||||
data, err = ioutil.ReadAll(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
error := json.Unmarshal(data, res)
|
||||
return error
|
||||
}
|
||||
|
||||
// mapToStandardErr map the dcrd API Message errors to the standard error messages
|
||||
// supported by trezor. Dcrd errors to be mapped are listed here:
|
||||
// https://github.com/decred/dcrd/blob/2f5e47371263b996bb99e8dc3484f659309bd83a/dcrjson/jsonerr.go
|
||||
func mapToStandardErr(customPrefix string, err Error) error {
|
||||
switch {
|
||||
case strings.Contains(err.Message, dcrjson.ErrBlockNotFound.Message) || // Block not found
|
||||
strings.Contains(err.Message, dcrjson.ErrOutOfRange.Message) || // Block number out of range
|
||||
strings.Contains(err.Message, dcrjson.ErrBestBlockHash.Message): // Error getting best block hash
|
||||
return bchain.ErrBlockNotFound
|
||||
case strings.Contains(err.Message, dcrjson.ErrNoTxInfo.Message): // No information available about transaction
|
||||
return bchain.ErrTxNotFound
|
||||
case strings.Contains(err.Message, dcrjson.ErrInvalidTxVout.Message): // Output index number (vout) does not exist for transaction
|
||||
return bchain.ErrTxidMissing
|
||||
default:
|
||||
return fmt.Errorf(customPrefix, err.Message)
|
||||
}
|
||||
}
|
||||
68
configs/coins/decred.json
Normal file
68
configs/coins/decred.json
Normal file
@ -0,0 +1,68 @@
|
||||
{
|
||||
"coin": {
|
||||
"name": "Decred",
|
||||
"shortcut": "DCR",
|
||||
"label": "Decred",
|
||||
"alias": "decred"
|
||||
},
|
||||
"ports": {
|
||||
"backend_rpc": 8061,
|
||||
"backend_message_queue": 38361,
|
||||
"blockbook_internal": 9061,
|
||||
"blockbook_public": 9161
|
||||
},
|
||||
"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-decred",
|
||||
"package_revision": "decred-1",
|
||||
"system_user": "decred",
|
||||
"version": "1.4.0",
|
||||
"binary_url": "https://github.com/decred/decred-binaries/releases/download/v1.4.0/decred-linux-amd64-v1.4.0.tar.gz",
|
||||
"verification_type": "sha256",
|
||||
"verification_source": "36375985df1ba9a45bc11b4f6cdaed4f14ff6e5e9c46e17ef6e4f70a3349aba2",
|
||||
"extract_command": "tar -C backend --strip 1 -xf",
|
||||
"exclude_files": [],
|
||||
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/dcrd --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --rpcuser={{.IPC.RPCUser}} --rpcpass={{.IPC.RPCPass}} -C={{.Env.BackendDataPath}}/{{.Coin.Alias}}/dcrd.conf --nofilelogging --appdata={{.Env.BackendDataPath}}/{{.Coin.Alias}} --notls --txindex --addrindex --rpclisten=[127.0.0.1]:8061",
|
||||
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/*.log",
|
||||
"postinst_script_template": "",
|
||||
"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": {
|
||||
"rpcmaxclients": 16,
|
||||
"upnp": 0,
|
||||
"whitelist": "127.0.0.1"
|
||||
}
|
||||
},
|
||||
"blockbook": {
|
||||
"package_name": "blockbook-decred",
|
||||
"system_user": "blockbook-decred",
|
||||
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
|
||||
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
|
||||
"explorer_url": "https://mainnet.dcrdata.org/",
|
||||
"additional_params": "",
|
||||
"block_chain": {
|
||||
"parse": true,
|
||||
"subversion":"/Decred dcrd:1.4.0",
|
||||
"mempool_workers": 8,
|
||||
"mempool_sub_workers": 2,
|
||||
"block_addresses_to_keep": 30,
|
||||
"xpub_magic": 49990397,
|
||||
"slip44": 3,
|
||||
"additional_params": {}
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"package_maintainer": "Ugwueze Michael",
|
||||
"package_maintainer_email": "code.maestro64@gmail.com"
|
||||
}
|
||||
}
|
||||
68
configs/coins/decred_testnet.json
Normal file
68
configs/coins/decred_testnet.json
Normal file
@ -0,0 +1,68 @@
|
||||
{
|
||||
"coin": {
|
||||
"name": "Decred Testnet",
|
||||
"shortcut": "tDCR",
|
||||
"label": "Decred Testnet",
|
||||
"alias": "decred_testnet"
|
||||
},
|
||||
"ports": {
|
||||
"backend_rpc": 18061,
|
||||
"backend_message_queue": 48361,
|
||||
"blockbook_internal": 19061,
|
||||
"blockbook_public": 19161
|
||||
},
|
||||
"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-decred-testnet",
|
||||
"package_revision": "decred-testnet-1",
|
||||
"system_user": "decred",
|
||||
"version": "1.4.0",
|
||||
"binary_url": "https://github.com/decred/decred-binaries/releases/download/v1.4.0/decred-linux-amd64-v1.4.0.tar.gz",
|
||||
"verification_type": "sha256",
|
||||
"verification_source": "36375985df1ba9a45bc11b4f6cdaed4f14ff6e5e9c46e17ef6e4f70a3349aba2",
|
||||
"extract_command": "tar -C backend --strip 1 -xf",
|
||||
"exclude_files": [],
|
||||
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/dcrd --datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --rpcuser={{.IPC.RPCUser}} --rpcpass={{.IPC.RPCPass}} -C={{.Env.BackendDataPath}}/{{.Coin.Alias}}/dcrd.conf --nofilelogging --appdata={{.Env.BackendDataPath}}/{{.Coin.Alias}} --notls --txindex --addrindex --testnet --rpclisten=[127.0.0.1]18061",
|
||||
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/*.log",
|
||||
"postinst_script_template": "",
|
||||
"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": {
|
||||
"rpcmaxclients": 16,
|
||||
"upnp": 0,
|
||||
"whitelist": "127.0.0.1"
|
||||
}
|
||||
},
|
||||
"blockbook": {
|
||||
"package_name": "blockbook-decred-testnet",
|
||||
"system_user": "blockbook-decred-testnet",
|
||||
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
|
||||
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
|
||||
"explorer_url": "https://testnet.dcrdata.org/",
|
||||
"additional_params": "",
|
||||
"block_chain": {
|
||||
"parse": true,
|
||||
"subversion":"/Decred dcrd:1.4.0",
|
||||
"mempool_workers": 8,
|
||||
"mempool_sub_workers": 2,
|
||||
"block_addresses_to_keep": 30,
|
||||
"xpub_magic": 49990397,
|
||||
"slip44": 3,
|
||||
"additional_params": {}
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"package_maintainer": "Ugwueze Michael",
|
||||
"package_maintainer_email": "code.maestro64@gmail.com"
|
||||
}
|
||||
}
|
||||
98
tests/rpc/testdata/decred.json
vendored
Normal file
98
tests/rpc/testdata/decred.json
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
{
|
||||
"blockHeight": 30000,
|
||||
"blockHash": "000000000000039135acfe97d4c769156856318997ff9afa6635873344b4e26f",
|
||||
"blockTime": 1463843967,
|
||||
"blockTxs": [
|
||||
"4cfab09f97f04e065f9a9552dcdda9ce1b2aabec436330adea330f0b5f7a359d",
|
||||
"fb422109aa8db4dafdd16a0e9c38d49a4ea448a3e739513d5aaf2cc056e366a6",
|
||||
"a768ec0314cde1e497893d6825f4780f5342e24a841c50d48189e540d3d08a2a",
|
||||
"7058766ffef2e9cee61ee4b7604a39bc91c3000cb951c4f93f3307f6e0bf4def",
|
||||
"ea66736c9488fee34d85042a6cfdb6c53a0b6da4a3b69f9014d9037ffc672173",
|
||||
"798b8a038b96719dddb544a82a9e626ea548353064e17541b3a4f4bf91663501",
|
||||
"8794b8c3b72fccddeb5acfb61fe8671de697a3e43d9111c59fc80b0890ff9ba9",
|
||||
"b285a6e33b3d1bdf329fa0d169a5ddb21c1e9517df021ecf5235c0ebfd21c6fc"
|
||||
],
|
||||
"blockSize": 5050,
|
||||
"txDetails": {
|
||||
"7058766ffef2e9cee61ee4b7604a39bc91c3000cb951c4f93f3307f6e0bf4def": {
|
||||
"hex": "0100000001193c189c71dff482b70ccb10ec9cf0ea3421a7fc51e4c7b0cf59c98a293a2f960200000000ffffffff027c87f00b0000000000001976a91418f10131a859912119c4a8510199f87f0a4cec2488ac9889495f0000000000001976a914631fb783b1e06c3f6e71777e16da6de13450465e88ac0000000000000000015ced3d6b0000000030740000000000006a47304402204e6afc21f6d065b9c082dad81a5f29136320e2b54c6cdf6b8722e4507e1a8d8902203933c5e592df3b0bbb0568f121f48ef6cbfae9cf479a57229742b5780dedc57a012103b89bb443b6ab17724458285b302291b082c59e5a022f273af0f61d47a414a537",
|
||||
"txid": "7058766ffef2e9cee61ee4b7604a39bc91c3000cb951c4f93f3307f6e0bf4def",
|
||||
"blocktime": 1463843967,
|
||||
"time": 1463843967,
|
||||
"locktime": 0,
|
||||
"version": 1,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "962f3a298ac959cfb0c7e451fca72134eaf09cec10cb0cb782f4df719c183c19",
|
||||
"vout": 2,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "47304402204e6afc21f6d065b9c082dad81a5f29136320e2b54c6cdf6b8722e4507e1a8d8902203933c5e592df3b0bbb0568f121f48ef6cbfae9cf479a57229742b5780dedc57a012103b89bb443b6ab17724458285b302291b082c59e5a022f273af0f61d47a414a537"
|
||||
}
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 2.003127,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a91418f10131a859912119c4a8510199f87f0a4cec2488ac",
|
||||
"addresses": [
|
||||
"DsTEnRLDEjQNeQ4A47fdS2pqtaFrGNzkqNa"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 15.98654872,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914631fb783b1e06c3f6e71777e16da6de13450465e88ac",
|
||||
"addresses": [
|
||||
"Dsa12P9VnCd55hTnUXpvGgFKSeGkFkzRvYb"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"798b8a038b96719dddb544a82a9e626ea548353064e17541b3a4f4bf91663501": {
|
||||
"hex": "0100000001188e1dd0e4708b0921afdd75d013b7cd63f7952e558ceb1040f45681717a27100000000000ffffffff029eb7de050000000000001976a91437b2be41e6b04cc29e4421b2bc2f7ab31a8a926d88aca7eade600000000000001976a914c450e55bb7f766ce35f1c16c3ddbc8eefcc7719688ac0000000000000000018d7ec166000000002d750000020000006b483045022100dd83f7cf6832e78bcbaac959ee38833cf2da2bac537223d8d80c61ea1578716f02202aa925285793c372dc8dfe5ef4febdae94c853b8230443fbb61ef59805b229530121031d5fdebe075585f15d8dad8eecb62bdf5e5d8c353097ee08ed7c1a9bc0de0a77",
|
||||
"txid": "798b8a038b96719dddb544a82a9e626ea548353064e17541b3a4f4bf91663501",
|
||||
"blocktime": 1463843967,
|
||||
"time": 1463843967,
|
||||
"locktime": 0,
|
||||
"version": 1,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "10277a718156f44010eb8c552e95f763cdb713d075ddaf21098b70e4d01d8e18",
|
||||
"vout": 0,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "483045022100dd83f7cf6832e78bcbaac959ee38833cf2da2bac537223d8d80c61ea1578716f02202aa925285793c372dc8dfe5ef4febdae94c853b8230443fbb61ef59805b229530121031d5fdebe075585f15d8dad8eecb62bdf5e5d8c353097ee08ed7c1a9bc0de0a77"
|
||||
}
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.98482078,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a91437b2be41e6b04cc29e4421b2bc2f7ab31a8a926d88ac",
|
||||
"addresses": [
|
||||
"DsW3QnGxXzyvZAvFbLeuDE53aWD55deJiBk"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 16.25221799,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914c450e55bb7f766ce35f1c16c3ddbc8eefcc7719688ac",
|
||||
"addresses": [
|
||||
"Dsirvqn2J4AzZSns3DpoB3iCzV8iWmspa9m"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
115
tests/rpc/testdata/decred_testnet.json
vendored
Normal file
115
tests/rpc/testdata/decred_testnet.json
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
{
|
||||
"blockHeight": 18720,
|
||||
"blockHash": "00000000056aa5d3268088dd958da4aeb73586815a2974ef40a907ecbbf8893c",
|
||||
"blockTime": 1535980876,
|
||||
"blockTxs": [
|
||||
"47ceedca164da150ce1c677f6d6f1eae41b792d9c16a956204cc0dcbf8caa905",
|
||||
"0c7a348bbcc0da20ac5f304627e8154b90ed2b56ec828a5513bce0dd521713cc"
|
||||
],
|
||||
"blockSize": 2531,
|
||||
"txDetails": {
|
||||
"326e54f7909ef8b3d696367039c5eec67b505ef82c3edd2368dd39b6aa272dee": {
|
||||
"hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff03fbed9f0d00000000000017a914d585cd7426d25b4ea5faf1e6987aacfeda3db94287000000000000000000000e6a0c2049000039c63f0b763dfcffa145c0510000000000001976a9149bb0ab1ab455c220c575304aa6bb31dad3eff19888ac000000000000000001e0815f5f0000000000000000ffffffff0800002f646372642f",
|
||||
"blocktime": 1535980876,
|
||||
"time": 1535980876,
|
||||
"locktime": 0,
|
||||
"version": 1,
|
||||
"vin": [
|
||||
{
|
||||
"blockindex": 4294967295,
|
||||
"coinbase": "00002f646372642f",
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 2.28584955,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "a914d585cd7426d25b4ea5faf1e6987aacfeda3db94287",
|
||||
"addresses": [
|
||||
"TcrypGAcGCRVXrES7hWqVZb5oLJKCZEtoL1"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "6a0c2049000039c63f0b763dfcff",
|
||||
"addresses": [
|
||||
"OP_RETURN 2049000039c63f0b763dfcff"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 13.71555233,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a9149bb0ab1ab455c220c575304aa6bb31dad3eff19888ac",
|
||||
"addresses": [
|
||||
"TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"5cf42c027886df5281df2d15fa876281a362ebb742efe963682258ef201d58d3": {
|
||||
"hex": "01000000022071ad6dd7c1f463d1cdd7bf15879397be44da6d98bf03c89d571eedf77ffc530200000001ffffffff39880e89917a590c0b010d87064812019e619048d2c4e8dbe8115048c067ecd50200000001ffffffff03cd1ecb340000000000001976a914ae99f28cb5e00dd88b1669c19995f5c84586c33388accd1ecb340000000000001976a914ae99f28cb5e00dd88b1669c19995f5c84586c33388ac1a0801070000000000001976a914fe3d3bc633ef2321d479332412b71ef6bc60e86a88ac000000000000000002362e54360000000010490000010000006b483045022100dbb703dea8c4fc87357be19a9af114242bc80c4eafa387d654da1a427c56cd6102207cb00d4fedbfaa4f2aa7b4530683e286ad13fe127dd9be5edee4c1cb594d9da401210372321040dccd62d5171718e49713a804ceb566fa65ea762a86e7b89e8448d0ed3ac9433a0000000010490000030000006a4730440220729ca901c17ef7008d32df19bfdedf91821d10f0a7511be4d78bdf1027f5598c022077c4a7df6388d1107927f9f95b6aca4b3fd02ae1ff2c422fe47ad79b51c42b64012103adde046e4f13579bba4104ec9b5064158df0ea6648876a8667d88c6abffc0e22",
|
||||
"blocktime": 1535980876,
|
||||
"time": 1535980876,
|
||||
"locktime": 0,
|
||||
"version": 1,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "53fc7ff7ed1e579dc803bf986dda44be97938715bfd7cdd163f4c1d76dad7120",
|
||||
"vout": 2,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "483045022100dbb703dea8c4fc87357be19a9af114242bc80c4eafa387d654da1a427c56cd6102207cb00d4fedbfaa4f2aa7b4530683e286ad13fe127dd9be5edee4c1cb594d9da401210372321040dccd62d5171718e49713a804ceb566fa65ea762a86e7b89e8448d0ed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"txid": "d5ec67c0485011e8dbe8c4d24890619e01124806870d010b0c597a91890e8839",
|
||||
"vout": 2,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "4730440220729ca901c17ef7008d32df19bfdedf91821d10f0a7511be4d78bdf1027f5598c022077c4a7df6388d1107927f9f95b6aca4b3fd02ae1ff2c422fe47ad79b51c42b64012103adde046e4f13579bba4104ec9b5064158df0ea6648876a8667d88c6abffc0e22"
|
||||
}
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 8.85726925,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914ae99f28cb5e00dd88b1669c19995f5c84586c33388ac",
|
||||
"addresses": [
|
||||
"TsgwLUPtU1PF6nFe6RohUdhwUURJX7bv5R1"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 8.85726925,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914ae99f28cb5e00dd88b1669c19995f5c84586c33388ac",
|
||||
"addresses": [
|
||||
"TsgwLUPtU1PF6nFe6RohUdhwUURJX7bv5R1"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 1.17508122,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914fe3d3bc633ef2321d479332412b71ef6bc60e86a88ac",
|
||||
"addresses": [
|
||||
"TspCRTrypUAyeE3yNqg6Wp1a2w9bmko3oED"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
217
tests/sync/testdata/decred.json
vendored
Normal file
217
tests/sync/testdata/decred.json
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
{
|
||||
"connectBlocks": {
|
||||
"syncRanges": [
|
||||
{"lower": 100000, "upper": 100020}
|
||||
],
|
||||
"blocks": {
|
||||
"100005": {
|
||||
"height": 100005,
|
||||
"hash": "0000000000000521f66d113ea7ca9a5a03e0c1ade0c440bbd32c459b24d5d47e",
|
||||
"noTxs": 3,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff03e184db0f00000000000017a914f5916158e3e2c4551c1796708db8367207ed13bb8700000000000000000000266a24a586010000000000000000000000000000000000000000000000000084efffe9a8e93376eb569b5f0000000000001976a914b3c2069c496bc13228c154b03993809f278233d188ac0000000000000000012ba2006f0000000000000000ffffffff0800002f646372642f",
|
||||
"txid": "111e559788e2213ad354d0e293d79ce6ebda98340016fba5314ea62cfa157562",
|
||||
"version": 1,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "",
|
||||
"vout": 0,
|
||||
"coinbase": "00002f646372642f",
|
||||
"sequence": 4294967294
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 2.66044641,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "a914f5916158e3e2c4551c1796708db8367207ed13bb87",
|
||||
"addresses": [
|
||||
"Dcur2mcGjmENx4DhNqDctW5wJCVyT3Qeqkx"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "6a24a586010000000000000000000000000000000000000000000000000084efffe9a8e93376"
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 16.04015851,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914b3c2069c496bc13228c154b03993809f278233d188ac",
|
||||
"addresses": [
|
||||
"DshMNsvETDWpVoCe1re9NTAChiJagzsFV7J"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"time": 1484891644,
|
||||
"blocktime": 1484891644
|
||||
},
|
||||
{
|
||||
"hex": "010000000771c0d98b5e604efb69b5dcccf6e934dd7fb40baa6572bbeb9e4ff75e5899aa1c0100000000ffffffff72d9c5d4a2798360f102badc1a0283934eb1ba04c69b7b152088d3c0f72069bd0100000000ffffffff73c8832d83ad240cbc582c0084279cdc9bfba725ae85d847024d25f3bfd26ce10100000000ffffffff7874bdd4ddd605ec3cfec5e5efbb73d482d2fc03227c6f5d210d1f41d706dad91600000000ffffffff7874bdd4ddd605ec3cfec5e5efbb73d482d2fc03227c6f5d210d1f41d706dad92f00000000ffffffff790d74bd9ff9178fc3ba60b8faaf05b8f78b604d4a90696791169c6682275c2f0100000000ffffffff7a64c28e2b9dfb8248744b234af4a151f7d223924d806869ac682d1861c99f860000000000ffffffff0291a9bad30900000000001976a914ae06b5f3af5d9e81f1a1a63dd0a64f19dafd7bc588ac32a285e90200000000001976a9148f2273ad9bb74f78fc3e24a9b29ed4756958001f88ac0000000000000000076c980a060000000039860100080000006b483045022100bbad991e492adc128329c01fc8a69cad490311192997d89fbb673a1dc3acd9e002205bc58d4db2c75cc9306a404f1878aa4e0bc0ef4774555fab802c84a5e9c5dd1a012103e4aa9749379b85116621c6308bcfc48d7d293f6866187fc26f6534987497912bd456de050000000010830100020000006a47304402204eeae188a6b7d983f3a316df1cb71c1884007b3557e554adfc53a7143a81c0cc02204c200cdaeb97ac0e268f5445163c320d0af67a7efc55bce14e47bf3e741395e20121035a0f902dc68ffe0e8e0559d3833d84dad9466f0ac467ca85ac0dac2abbbd6784da41de020000000022830100070000006b48304502210099eefa905ef76102a5aa15f256c75b60a5ac688b148ed2b2f2baa87531dee255022027f6bf71df34d63921ecb817adbac0b369f7350847550344504fdb51d3ddbec50121036d8a38a3e7219b42008bcc88a1d6de11479deaf077327b864b5181122c049884a6f71c0100000000c9840100030000006b48304502210097c20b800de44d256b9078e26408605295f511e92a01222e36fdc129eeb264d2022003e976c07cd3d2e2a955a28bd4510b7d6dd883c7699c76af43d09687f4500ab4012102d4828ba360e1409872b269c9a66a5bbdc04e2478cf38faaab370c953b683e8dbaf4d0e0100000000c9840100030000006b483045022100de0d039786d03fd1fbf2ba4383ed17b4120ab9ef5e5f197b413808bf74aede1402206bf601514f8a27777fad96f51ed3da65f129df03abd42fedf69af0a415b82f2b012102261c5be43f15ff9c6af795759047ccaec9f4805d5a31e3240a97313d7de858c057dfe2050000000002850100090000006b483045022100917523b954ea3e27c18c63d791070cbf37efa0066741719af153d80336b2b08102206fed9413ab193cfee63177a4b4ae44383a444669165438ec77f4193f9abcbe5f012103de17ca81a7ce2655abc63f2a5d0f85ab41c0cadd6e564eadb409fbe614bc679ee5047ea60c0000008d850100030000006b483045022100bc8d6aa9da128d9cb98bdb949fa25bb3a9d4e126b03d0ad66caa39c00ec1abff02200b90a3717c4f179ddbdeffe203a2c1ca58d3f4c51317955a47eef7ea0eea447f012102d73d8c7df63c5dcab39d0b22ce2e4380611be0e71c4a324f369c91e3818cdb9a",
|
||||
"txid": "114694eebaddaf48adaffb097fb4ed338d0fe5650a337047b6c34c0c2ed48981",
|
||||
"version": 2,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "1caa99585ef74f9eebbb7265aa0bb47fdd34e9f6ccdcb569fb4e605e8bd9c071",
|
||||
"vout": 1,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "483045022100bbad991e492adc128329c01fc8a69cad490311192997d89fbb673a1dc3acd9e002205bc58d4db2c75cc9306a404f1878aa4e0bc0ef4774555fab802c84a5e9c5dd1a012103e4aa9749379b85116621c6308bcfc48d7d293f6866187fc26f6534987497912b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"txid": "bd6920f7c0d38820157b9bc604bab14e9383021adcba02f1608379a2d4c5d972",
|
||||
"vout": 1,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "47304402204eeae188a6b7d983f3a316df1cb71c1884007b3557e554adfc53a7143a81c0cc02204c200cdaeb97ac0e268f5445163c320d0af67a7efc55bce14e47bf3e741395e20121035a0f902dc68ffe0e8e0559d3833d84dad9466f0ac467ca85ac0dac2abbbd6784"
|
||||
}
|
||||
},
|
||||
{
|
||||
"txid": "e16cd2bff3254d0247d885ae25a7fb9bdc9c2784002c58bc0c24ad832d83c873",
|
||||
"vout": 1,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "48304502210099eefa905ef76102a5aa15f256c75b60a5ac688b148ed2b2f2baa87531dee255022027f6bf71df34d63921ecb817adbac0b369f7350847550344504fdb51d3ddbec50121036d8a38a3e7219b42008bcc88a1d6de11479deaf077327b864b5181122c049884"
|
||||
}
|
||||
},
|
||||
{
|
||||
"txid": "d9da06d7411f0d215d6f7c2203fcd282d473bbefe5c5fe3cec05d6ddd4bd7478",
|
||||
"vout": 22,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "48304502210097c20b800de44d256b9078e26408605295f511e92a01222e36fdc129eeb264d2022003e976c07cd3d2e2a955a28bd4510b7d6dd883c7699c76af43d09687f4500ab4012102d4828ba360e1409872b269c9a66a5bbdc04e2478cf38faaab370c953b683e8db"
|
||||
}
|
||||
},
|
||||
{
|
||||
"txid": "d9da06d7411f0d215d6f7c2203fcd282d473bbefe5c5fe3cec05d6ddd4bd7478",
|
||||
"vout": 47,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "483045022100de0d039786d03fd1fbf2ba4383ed17b4120ab9ef5e5f197b413808bf74aede1402206bf601514f8a27777fad96f51ed3da65f129df03abd42fedf69af0a415b82f2b012102261c5be43f15ff9c6af795759047ccaec9f4805d5a31e3240a97313d7de858c0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"txid": "2f5c2782669c16916769904a4d608bf7b805affab860bac38f17f99fbd740d79",
|
||||
"vout": 1,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "483045022100917523b954ea3e27c18c63d791070cbf37efa0066741719af153d80336b2b08102206fed9413ab193cfee63177a4b4ae44383a444669165438ec77f4193f9abcbe5f012103de17ca81a7ce2655abc63f2a5d0f85ab41c0cadd6e564eadb409fbe614bc679e"
|
||||
}
|
||||
},
|
||||
{
|
||||
"txid": "869fc961182d68ac6968804d9223d2f751a1f44a234b744882fb9d2b8ec2647a",
|
||||
"vout": 0,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "483045022100bc8d6aa9da128d9cb98bdb949fa25bb3a9d4e126b03d0ad66caa39c00ec1abff02200b90a3717c4f179ddbdeffe203a2c1ca58d3f4c51317955a47eef7ea0eea447f012102d73d8c7df63c5dcab39d0b22ce2e4380611be0e71c4a324f369c91e3818cdb9a"
|
||||
}
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 422.06931345,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914ae06b5f3af5d9e81f1a1a63dd0a64f19dafd7bc588ac",
|
||||
"addresses": [
|
||||
"Dsgq575bWosNScm9HigQDVHQdTkFQd3g7rC"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 125.0778373,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a9148f2273ad9bb74f78fc3e24a9b29ed4756958001f88ac",
|
||||
"addresses": [
|
||||
"Dse1jPcustc7sMudDycgH4HGbV85HCY3MMr"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"time": 1484891644,
|
||||
"blocktime": 1484891644
|
||||
}
|
||||
]
|
||||
},
|
||||
"100011": {
|
||||
"height": 100011,
|
||||
"hash": "00000000000000399eee039c7ebcdeaf7940e3b0bfa6b2d1dd332285e9572749",
|
||||
"noTxs": 5,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "0100000004a1e19215642f11d809bc002b515d7b4dfa14c9488800f1f43650ca0c9ae03fac0200000000ffffffffa585dd9ddd253c81bed3641bfa41ec13e6de72f52d2f76845833e4c4a8f0d2120000000000ffffffffaaa6f277fc9ec80d875fd9ce57c1a3598665f30f627ed4c40c56856c7acec1ab0000000000ffffffffc50f02dd06e930f3e20093fcc53749e30564084665bb7cf2868092d508c8be010200000000ffffffff020756d01f0000000000001976a91486dbe10bdd00a6b8e16456966525dc484f52ce8288ac2ca6f9320100000000001976a914a472eab42acbe0a80b96e1245877223f45e889d688ac00000000000000000424b2305f000000008c850100000000006b483045022100c34624406214b85e80dafe5a6ac5c3651caf71d64f35f2472ca0f40187c5215c02202c401683ec1428076f03a7f30168892de2c2ff4e77d0001a7638ba57670f34c3012103da2b1c13507c9b96b399520c3233f6c81eb4beed2d30be8077ad3a04854e4fecdc2ff75800000000a0860100010000006a47304402202043e50984305d8a81867270dd1a78fd75c5a4cadca6ffec801b0333f1c3f50c0220187dc23684e2f0257cb264a606c9fd2325d4579716763f2f2946e6951f112a7101210309a4f12413c7524e330e5dc08611a968f076d547ef7b62be13ae54629e7554105fc06e3b00000000aa860100020000006a47304402205efdee5709a934bf18634aac3b4a15f461537e71e8cc0e071bf8375c40b59aad02203a13124efcf243ac853016120e66714b2127b0b369792bf0d22aecee39e304e80121023d74b0497fbdc497ad116f4e9dd1ccb08c1cad4baaf81257b9e6b706ff872e9b6ccf3e5f000000009c850100000000006a473044022041c129f846ede6cce7eb027700d669c1c5e4b5fbd54b4f19577449072381db58022072a0715a22922cd2f3abcb7b760f4120e1f84b7ed53ff3ffe3534a4a55cec04b012103da2b1c13507c9b96b399520c3233f6c81eb4beed2d30be8077ad3a04854e4fec",
|
||||
"txid": "fbbb2c7c8e2e6c3d840b614089f238077cc60940f4d9089d413daf770e0ab976",
|
||||
"version": 1,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "ac3fe09a0cca5036f4f1008848c914fa4d7b5d512b00bc09d8112f641592e1a1",
|
||||
"vout": 2,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "483045022100c34624406214b85e80dafe5a6ac5c3651caf71d64f35f2472ca0f40187c5215c02202c401683ec1428076f03a7f30168892de2c2ff4e77d0001a7638ba57670f34c3012103da2b1c13507c9b96b399520c3233f6c81eb4beed2d30be8077ad3a04854e4fec"
|
||||
}
|
||||
},
|
||||
{
|
||||
"txid": "12d2f0a8c4e4335884762f2df572dee613ec41fa1b64d3be813c25dd9ddd85a5",
|
||||
"vout": 0,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "47304402202043e50984305d8a81867270dd1a78fd75c5a4cadca6ffec801b0333f1c3f50c0220187dc23684e2f0257cb264a606c9fd2325d4579716763f2f2946e6951f112a7101210309a4f12413c7524e330e5dc08611a968f076d547ef7b62be13ae54629e755410"
|
||||
}
|
||||
},
|
||||
{
|
||||
"txid": "abc1ce7a6c85560cc4d47e620ff3658659a3c157ced95f870dc89efc77f2a6aa",
|
||||
"vout": 0,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "47304402205efdee5709a934bf18634aac3b4a15f461537e71e8cc0e071bf8375c40b59aad02203a13124efcf243ac853016120e66714b2127b0b369792bf0d22aecee39e304e80121023d74b0497fbdc497ad116f4e9dd1ccb08c1cad4baaf81257b9e6b706ff872e9b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"txid": "01bec808d5928086f27cbb6546086405e34937c5fc9300e2f330e906dd020fc5",
|
||||
"vout": 2,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "473044022041c129f846ede6cce7eb027700d669c1c5e4b5fbd54b4f19577449072381db58022072a0715a22922cd2f3abcb7b760f4120e1f84b7ed53ff3ffe3534a4a55cec04b012103da2b1c13507c9b96b399520c3233f6c81eb4beed2d30be8077ad3a04854e4fec"
|
||||
}
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 5.33747207,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a91486dbe10bdd00a6b8e16456966525dc484f52ce8288ac",
|
||||
"addresses": [
|
||||
"DsdFySbuMDf5DuLT9y2fKWMhHvbe1p3wFyt"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 51.501891,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914a472eab42acbe0a80b96e1245877223f45e889d688ac",
|
||||
"addresses": [
|
||||
"DsfxRyT6X6Rkq1RdecTsjrA6yhFx7eZX3K6"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"time": 1484894155,
|
||||
"blocktime": 1484894155
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
214
tests/sync/testdata/decred_testnet.json
vendored
Normal file
214
tests/sync/testdata/decred_testnet.json
vendored
Normal file
@ -0,0 +1,214 @@
|
||||
{
|
||||
"connectBlocks": {
|
||||
"syncRanges": [
|
||||
{"lower": 120000, "upper": 120050}
|
||||
],
|
||||
"blocks": {
|
||||
"120017": {
|
||||
"height": 120017,
|
||||
"hash": "0000000003cff9fc185bc527c022b0a6fc44746863efcf2b61fc6fc3580e4871",
|
||||
"noTxs": 3,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "01000000026953d41701ea33dd3bb2aecaf69e42ad9659f21d4a3659ac33e612c92b910fe60400000000ffffffff69866379514807b1dbbef897d0cbb72fded8898ed2e99a048a2d265481d562460300000001ffffffff036a5c5d000000000000001976a91499c0d32d658f014238ceeb8ef3cdf7ba397c999688ac870d48df0000000000001976a91499c0d32d658f014238ceeb8ef3cdf7ba397c999688ac5c5ddc110000000000001976a914557d1cdbdc4fd6bb7f8f703eb843c256f4b2526588ac00000000000000000265b0170f00000000d0d40100020000006b483045022100def05196b7a16030045d9e667ee5dfc3f327481b46125b908e3bfc3efbb5116602204c1833285de9ae89bc8e07bf538b9c77da0bf27bab65fc01b42dab03fe67b3c701210350c221d43e52e6949a46aaa0db4e4a57870fa1752fdd0695b18c342077601839ae286ae200000000c0d40100010000006a47304402206d6ffc181b98f00d2470adf18b157f1a1c4a3d6b181b8d7bb6163f3d8ad560e602206fbbe567e1895fb51b306785056f0d4585aaaa334cdc6590e6cd8b13f24ddceb012103c7935596dd3242629002f8fb52038ded4c273244ae59d543657596b595816b99",
|
||||
"txid": "226091c0ab80e59759790afbbb94055aa221a806d7d0e479fcc9b9c9325c9e1a",
|
||||
"version": 2,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "e60f912bc912e633ac59364a1df25996ad429ef6caaeb23bdd33ea0117d45369",
|
||||
"vout": 4,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "483045022100def05196b7a16030045d9e667ee5dfc3f327481b46125b908e3bfc3efbb5116602204c1833285de9ae89bc8e07bf538b9c77da0bf27bab65fc01b42dab03fe67b3c701210350c221d43e52e6949a46aaa0db4e4a57870fa1752fdd0695b18c342077601839"
|
||||
}
|
||||
},
|
||||
{
|
||||
"txid": "4662d58154262d8a049ae9d28e89d8de2fb7cbd097f8bedbb107485179638669",
|
||||
"vout": 3,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "47304402206d6ffc181b98f00d2470adf18b157f1a1c4a3d6b181b8d7bb6163f3d8ad560e602206fbbe567e1895fb51b306785056f0d4585aaaa334cdc6590e6cd8b13f24ddceb012103c7935596dd3242629002f8fb52038ded4c273244ae59d543657596b595816b99"
|
||||
}
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.06118506,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a91499c0d32d658f014238ceeb8ef3cdf7ba397c999688ac",
|
||||
"addresses": [
|
||||
"Tsf36rctvWvXBq4tkLzuAfH3MfjHPbEBdiZ"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 37.46041223,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a91499c0d32d658f014238ceeb8ef3cdf7ba397c999688ac",
|
||||
"addresses": [
|
||||
"Tsf36rctvWvXBq4tkLzuAfH3MfjHPbEBdiZ"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 2.99654492,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914557d1cdbdc4fd6bb7f8f703eb843c256f4b2526588ac",
|
||||
"addresses": [
|
||||
"TsYp9nrGuJvxsjMaEgXjgLUgaUQ2KmT84Ps"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"time": 1548564206,
|
||||
"blocktime": 1548564206
|
||||
}
|
||||
]
|
||||
},
|
||||
"120030": {
|
||||
"height": 120030,
|
||||
"hash": "0000000000960cd08532a50b860f627251619252eb3f4038db6ddf0defff2b79",
|
||||
"noTxs": 3,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "010000000238f6741258c584e3ecc2700acf7b65d4bcab45cc2b2652ac3dd2bcd123364f740300000001ffffffffadc1972f460461dc4ec85ef705a6472e1f9312877dc49f6abed23a888bd2cd220300000001ffffffff056a5c5d000000000000001976a914eb3d6580786e0c1ea7be0bd79547ca2d020bae9d88ac870d48df0000000000001976a914eb3d6580786e0c1ea7be0bd79547ca2d020bae9d88ac6a5c5d000000000000001976a914eb3d6580786e0c1ea7be0bd79547ca2d020bae9d88ac870d48df0000000000001976a914eb3d6580786e0c1ea7be0bd79547ca2d020bae9d88acce1d05010000000000001976a914b7132cd2bfdb2c17c376dd1745b60004563d0a6a88ac000000000000000002ae286ae200000000cdd40100000000006b4830450221008b5bb27b0464586ef8c4d9e02985cc88189c0e65a79a5b1dbd36e73e7728c276022015435d1c31b3c2560ef03e2091870db6f04946fefffc33dc0986bc582f2356e0012103c7935596dd3242629002f8fb52038ded4c273244ae59d543657596b595816b9998dde5dd00000000cdd40100010000006a47304402206897b511153501779cce981b392ea4e352df2f585b8c8951331009739c51939b02204c8308c55b3822356c91869b9b73b85122dfabbcbf2b40d96102c99fc08ab6ab012103c7935596dd3242629002f8fb52038ded4c273244ae59d543657596b595816b99",
|
||||
"txid": "c95c7409a0fee08e0fcb8fdab14e17f9a8313abafd7cd4bf69af9fde727a4e98",
|
||||
"version": 1,
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.06118506,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914eb3d6580786e0c1ea7be0bd79547ca2d020bae9d88ac",
|
||||
"addresses": [
|
||||
"TsnTxpYTCjpBRo6NdiEeGFksV7s8dEixaMc"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 37.46041223,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914eb3d6580786e0c1ea7be0bd79547ca2d020bae9d88ac",
|
||||
"addresses": [
|
||||
"TsnTxpYTCjpBRo6NdiEeGFksV7s8dEixaMc"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.06118506,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914eb3d6580786e0c1ea7be0bd79547ca2d020bae9d88ac",
|
||||
"addresses": [
|
||||
"TsnTxpYTCjpBRo6NdiEeGFksV7s8dEixaMc"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 37.46041223,
|
||||
"n": 3,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914eb3d6580786e0c1ea7be0bd79547ca2d020bae9d88ac",
|
||||
"addresses": [
|
||||
"TsnTxpYTCjpBRo6NdiEeGFksV7s8dEixaMc"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.17112526,
|
||||
"n": 4,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914b7132cd2bfdb2c17c376dd1745b60004563d0a6a88ac",
|
||||
"addresses": [
|
||||
"Tshi96yRHGfPJWz31yTo5UvDrNQ64VTq56c"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"time": 1548565264,
|
||||
"blocktime": 1548565264
|
||||
},
|
||||
{
|
||||
"hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff032b015e0800000000000017a914d585cd7426d25b4ea5faf1e6987aacfeda3db94287000000000000000000000e6a0cded401003387c553de73dc715cea34320000000000001976a9149bb0ab1ab455c220c575304aa6bb31dad3eff19888ac0000000000000000012e08923a0000000000000000ffffffff0800002f646372642f",
|
||||
"txid": "72cb7f936a46014fdcef8fc10148274ea95c562948f0aac490ea942290553066",
|
||||
"version": 1,
|
||||
"vin": [
|
||||
{
|
||||
"coinbase": "00002f646372642f",
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 1.40378411,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "a914d585cd7426d25b4ea5faf1e6987aacfeda3db94287",
|
||||
"addresses": [
|
||||
"TcrypGAcGCRVXrES7hWqVZb5oLJKCZEtoL1"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "6a0cded401003387c553de73dc71"
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 8.42328668,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a9149bb0ab1ab455c220c575304aa6bb31dad3eff19888ac",
|
||||
"addresses": [
|
||||
"TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"time": 1548565264,
|
||||
"blocktime": 1548565264
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"connectBlocks": {
|
||||
"syncRanges": [
|
||||
{"lower": 120000, "upper": 120047}
|
||||
],
|
||||
"fakeBlocks": {
|
||||
"120001": {
|
||||
"height": 120001,
|
||||
"hash": "00000000000000e463222f52668f5b66f15c65ee4db3c60ca6853ffa93ed21f3"
|
||||
},
|
||||
"120023": {
|
||||
"height": 120023,
|
||||
"hash": "00000000000002327a29e7fdf539e773e960e631622c46cf859efebdbb78f9a8"
|
||||
},
|
||||
"120047": {
|
||||
"height": 120047,
|
||||
"hash": "000000000000034fa12d7d3167b5cc37ae8b8e5e305b243c4842fdc576453837"
|
||||
}
|
||||
},
|
||||
"realBlocks": {
|
||||
"120001": {
|
||||
"height": 120001,
|
||||
"hash": "0000000007bf728a0fd7b41825763da0bf41bc86aea1813fe52d658ca76d1b2c"
|
||||
},
|
||||
"120023": {
|
||||
"height": 120023,
|
||||
"hash": "0000000003d011dc3b52c13ea2098367bc6d8de9c66ba322a02461d1bc125395"
|
||||
},
|
||||
"120047": {
|
||||
"height": 120047,
|
||||
"hash": "00000000038ec6283472cb71d404eacef8d62045a3bb9a45f7c314cf98ee4d4e"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,16 @@
|
||||
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
|
||||
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
|
||||
},
|
||||
"decred": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "MempoolSync",
|
||||
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
|
||||
"sync": ["ConnectBlocksParallel", "ConnectBlocks"]
|
||||
},
|
||||
"decred_testnet": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "MempoolSync",
|
||||
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
|
||||
"sync": ["ConnectBlocksParallel", "ConnectBlocks"]
|
||||
},
|
||||
"digibyte": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
|
||||
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user