diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 0062cd25..4c251f59 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -21,6 +21,7 @@ import ( "blockbook/bchain/coins/myriad" "blockbook/bchain/coins/namecoin" "blockbook/bchain/coins/pivx" + "blockbook/bchain/coins/qtum" "blockbook/bchain/coins/vertcoin" "blockbook/bchain/coins/xzc" "blockbook/bchain/coins/zec" @@ -76,6 +77,8 @@ func init() { BlockChainFactories["Fujicoin"] = fujicoin.NewFujicoinRPC BlockChainFactories["Flo"] = flo.NewFloRPC BlockChainFactories["Bellcoin"] = bellcoin.NewBellcoinRPC + BlockChainFactories["Qtum"] = qtum.NewQtumRPC + BlockChainFactories["Qtum Testnet"] = qtum.NewQtumRPC } // GetCoinNameFromConfig gets coin name and coin shortcut from config file diff --git a/bchain/coins/qtum/qtumparser.go b/bchain/coins/qtum/qtumparser.go new file mode 100644 index 00000000..701fac47 --- /dev/null +++ b/bchain/coins/qtum/qtumparser.go @@ -0,0 +1,149 @@ +package qtum + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + "blockbook/bchain/coins/utils" + "bytes" + "encoding/json" + "github.com/martinboehm/btcd/wire" + "github.com/martinboehm/btcutil/chaincfg" + "io" +) + +const ( + MainnetMagic wire.BitcoinNet = 0xf1cfa6d3 + TestnetMagic wire.BitcoinNet = 0x0d221506 +) + +var ( + MainNetParams chaincfg.Params + TestNetParams chaincfg.Params +) + +func init() { + MainNetParams = chaincfg.MainNetParams + MainNetParams.Net = MainnetMagic + MainNetParams.PubKeyHashAddrID = []byte{58} + MainNetParams.ScriptHashAddrID = []byte{50} + MainNetParams.Bech32HRPSegwit = "qc" + + TestNetParams = chaincfg.TestNet3Params + TestNetParams.Net = TestnetMagic + TestNetParams.PubKeyHashAddrID = []byte{120} + TestNetParams.ScriptHashAddrID = []byte{110} + TestNetParams.Bech32HRPSegwit = "tq" +} + +// QtumParser handle +type QtumParser struct { + *btc.BitcoinParser +} + +// NewQtumParser returns new DashParser instance +func NewQtumParser(params *chaincfg.Params, c *btc.Configuration) *QtumParser { + return &QtumParser{ + BitcoinParser: btc.NewBitcoinParser(params, c), + } +} + +// GetChainParams contains network parameters for the main Qtum network, +// the regression test Qtum network, the test Qtum network and +// the simulation test Qtum 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 { + panic(err) + } + } + switch chain { + case "test": + return &TestNetParams + default: + return &MainNetParams + } +} + +func parseBlockHeader(r io.Reader) (*wire.BlockHeader, error) { + h := &wire.BlockHeader{} + err := h.Deserialize(r) + + // hash_state_root 32 + // hash_utxo_root 32 + // hash_prevout_stake 32 + // hash_prevout_n 4 + buf := make([]byte, 100) + _, err = io.ReadFull(r, buf) + if err != nil { + return nil, err + } + + sigLength, err := wire.ReadVarInt(r, 0) + if err != nil { + return nil, err + } + sigBuf := make([]byte, sigLength) + _, err = io.ReadFull(r, sigBuf) + if err != nil { + return nil, err + } + + return h, err +} + +func (p *QtumParser) ParseBlock(b []byte) (*bchain.Block, error) { + r := bytes.NewReader(b) + w := wire.MsgBlock{} + + h, err := parseBlockHeader(r) + if err != nil { + return nil, err + } + + err = utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w) + if 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 +} + +// ParseTxFromJson parses JSON message containing transaction and returns Tx struct +func (p *QtumParser) ParseTxFromJson(msg json.RawMessage) (*bchain.Tx, error) { + var tx bchain.Tx + err := json.Unmarshal(msg, &tx) + if err != nil { + return nil, err + } + + for i := range tx.Vout { + vout := &tx.Vout[i] + // convert vout.JsonValue to big.Int and clear it, it is only temporary value used for unmarshal + vout.ValueSat, err = p.AmountToBigInt(vout.JsonValue) + if err != nil { + return nil, err + } + vout.JsonValue = "" + + if vout.ScriptPubKey.Addresses == nil { + vout.ScriptPubKey.Addresses = []string{} + } + } + + return &tx, nil +} diff --git a/bchain/coins/qtum/qtumparser_test.go b/bchain/coins/qtum/qtumparser_test.go new file mode 100644 index 00000000..c86d4239 --- /dev/null +++ b/bchain/coins/qtum/qtumparser_test.go @@ -0,0 +1,304 @@ +// +build unittest + +package qtum + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + "encoding/hex" + "math/big" + "os" + "reflect" + "testing" + + "github.com/martinboehm/btcutil/chaincfg" +) + +func TestMain(m *testing.M) { + c := m.Run() + chaincfg.ResetParams() + os.Exit(c) +} + +func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) { + type args struct { + address string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "P2PKH1", + args: args{address: "QiZfqrMLAtfzLCjXTHyLSiNDV6xydoocme"}, + want: "76a914f0e2aff6730b53b9986a5db8ca17c59426134a0988ac", + wantErr: false, + }, + { + name: "P2PKH2", + args: args{address: "Qcy8wvP1StA3cB9JcPdivXvqUqwai6t1tC"}, + want: "76a914b3822026c7f758b43a0882d7f2cbfa954702e45688ac", + wantErr: false, + }, + { + name: "P2SH1", + args: args{address: "MHx35M7aq4FcudwVSYziTkpbm9Hz8wJL7y"}, + want: "a9146e3c881d51d62a668362a5bba28be438f9c548e287", + wantErr: false, + }, + { + name: "P2SH2", + args: args{address: "MSNLeFGHY8iY2Kio57PnKuwWdCQinjuPDC"}, + want: "a914ca962f788569443b33ec673208ccdcfaac6873b487", + wantErr: false, + }, + { + name: "witness_v0_keyhash", + args: args{address: "qc1q3msdh3npg5ufvwm2sxltxcet6hll9tjzu8ym0d"}, + want: "00148ee0dbc6614538963b6a81beb3632bd5fff2ae42", + wantErr: false, + }, + { + name: "witness_v0_scripthashx", + args: args{address: "qc1q0uvgqxwqt0u4esawwcff7652jreqye30kmp4sj5dtydee50pze0s0ljwxp"}, + want: "00207f188019c05bf95cc3ae76129f6a8a90f202662fb6c3584a8d591b9cd1e1165f", + wantErr: false, + }, + } + parser := NewQtumParser(GetChainParams("main"), &btc.Configuration{}) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parser.GetAddrDescFromAddress(tt.args.address) + if (err != nil) != tt.wantErr { + t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr) + return + } + h := hex.EncodeToString(got) + if !reflect.DeepEqual(h, tt.want) { + t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want) + } + }) + } +} + +func Test_GetAddressesFromAddrDesc(t *testing.T) { + type args struct { + script string + } + tests := []struct { + name string + args args + want []string + want2 bool + wantErr bool + }{ + { + name: "P2PKH", + args: args{script: "76a914f0e2aff6730b53b9986a5db8ca17c59426134a0988ac"}, + want: []string{"QiZfqrMLAtfzLCjXTHyLSiNDV6xydoocme"}, + want2: true, + wantErr: false, + }, + { + name: "P2SH", + args: args{script: "a9146e3c881d51d62a668362a5bba28be438f9c548e287"}, + want: []string{"MHx35M7aq4FcudwVSYziTkpbm9Hz8wJL7y"}, + want2: true, + wantErr: false, + }, + { + name: "P2WPKH", + args: args{script: "00148ee0dbc6614538963b6a81beb3632bd5fff2ae42"}, + want: []string{"qc1q3msdh3npg5ufvwm2sxltxcet6hll9tjzu8ym0d"}, + want2: true, + wantErr: false, + }, + { + name: "P2WSH", + args: args{script: "00207f188019c05bf95cc3ae76129f6a8a90f202662fb6c3584a8d591b9cd1e1165f"}, + want: []string{"qc1q0uvgqxwqt0u4esawwcff7652jreqye30kmp4sj5dtydee50pze0s0ljwxp"}, + want2: true, + wantErr: false, + }, + { + name: "OP_RETURN ascii", + args: args{script: "6a0461686f6a"}, + want: []string{"OP_RETURN (ahoj)"}, + want2: false, + wantErr: false, + }, + { + name: "OP_RETURN hex", + args: args{script: "6a072020f1686f6a20"}, + want: []string{"OP_RETURN 2020f1686f6a20"}, + want2: false, + wantErr: false, + }, + } + + parser := NewQtumParser(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("outputScriptToAddresses() 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) + } + }) + } +} + +var ( + testTx1 bchain.Tx + testTxPacked1 = "00050fc08bc88ede00010000000336e691ab7f236d7c772b18e967c324b92ad1ba79e4641fd868f737d08f11857a000000006b483045022100bdef630a30ea681be3d2a66bbbc994100509effe9b85b384a0f8d75685eca97802206b7a5e58115deffe3f8f35d4c22dc52eb2cc1632ef18a48498731c09255c2fa9812102cc0f9a0906c0e8dffb3262778be1b3bc75e2d636fa01a7c2c129b1f3f30f21d3fffffffffa3ba1bd605b4db3594a8f5fd3cdec34d3044e3e26dee66908235e8643e9f50f010000006b483045022100f1889232cae3860876025317002bbc9a7e68b172c0595df5db8a1e59a12254150220557bebe548bae1b8fe3474375caca12cab1789e8d6e2c9bd6ab1b4a2c3e3691a812102cc0f9a0906c0e8dffb3262778be1b3bc75e2d636fa01a7c2c129b1f3f30f21d3ffffffff06fb670847bea092c352198f327e39fac3f568d57a100cb4a7db991485dda546170000006b483045022100a2fba32aebca4eaa261f9ebd2b956ac22d9c29e7f65868acd60165077dcfbc85022011864df322178f515260c9f4c098112bfbc23bf257f38005b80a34271d08149b812102cc0f9a0906c0e8dffb3262778be1b3bc75e2d636fa01a7c2c129b1f3f30f21d3ffffffff020088526a740000001976a914b61ba6aa3cc8be40e7553c8728ab3a303cbd4f2188acec1e0923000000001976a9148e896f90d402cdb5517f7d1f32a3d9d400e4bbcb88ac00000000" +) + +func init() { + testTx1 = bchain.Tx{ + Hex: "010000000336e691ab7f236d7c772b18e967c324b92ad1ba79e4641fd868f737d08f11857a000000006b483045022100bdef630a30ea681be3d2a66bbbc994100509effe9b85b384a0f8d75685eca97802206b7a5e58115deffe3f8f35d4c22dc52eb2cc1632ef18a48498731c09255c2fa9812102cc0f9a0906c0e8dffb3262778be1b3bc75e2d636fa01a7c2c129b1f3f30f21d3fffffffffa3ba1bd605b4db3594a8f5fd3cdec34d3044e3e26dee66908235e8643e9f50f010000006b483045022100f1889232cae3860876025317002bbc9a7e68b172c0595df5db8a1e59a12254150220557bebe548bae1b8fe3474375caca12cab1789e8d6e2c9bd6ab1b4a2c3e3691a812102cc0f9a0906c0e8dffb3262778be1b3bc75e2d636fa01a7c2c129b1f3f30f21d3ffffffff06fb670847bea092c352198f327e39fac3f568d57a100cb4a7db991485dda546170000006b483045022100a2fba32aebca4eaa261f9ebd2b956ac22d9c29e7f65868acd60165077dcfbc85022011864df322178f515260c9f4c098112bfbc23bf257f38005b80a34271d08149b812102cc0f9a0906c0e8dffb3262778be1b3bc75e2d636fa01a7c2c129b1f3f30f21d3ffffffff020088526a740000001976a914b61ba6aa3cc8be40e7553c8728ab3a303cbd4f2188acec1e0923000000001976a9148e896f90d402cdb5517f7d1f32a3d9d400e4bbcb88ac00000000", + Blocktime: 1552013184, + Txid: "40cc76f3d9747472c49a7c162628d5794e1fb3e5c28e5787b3c6c1178c794e8c", + LockTime: 0, + Version: 1, + Vin: []bchain.Vin{ + { + ScriptSig: bchain.ScriptSig{ + Hex: "483045022100bdef630a30ea681be3d2a66bbbc994100509effe9b85b384a0f8d75685eca97802206b7a5e58115deffe3f8f35d4c22dc52eb2cc1632ef18a48498731c09255c2fa9812102cc0f9a0906c0e8dffb3262778be1b3bc75e2d636fa01a7c2c129b1f3f30f21d3", + }, + Txid: "7a85118fd037f768d81f64e479bad12ab924c367e9182b777c6d237fab91e636", + Vout: 0, + Sequence: 4294967295, + }, + { + ScriptSig: bchain.ScriptSig{ + Hex: "483045022100f1889232cae3860876025317002bbc9a7e68b172c0595df5db8a1e59a12254150220557bebe548bae1b8fe3474375caca12cab1789e8d6e2c9bd6ab1b4a2c3e3691a812102cc0f9a0906c0e8dffb3262778be1b3bc75e2d636fa01a7c2c129b1f3f30f21d3", + }, + Txid: "0ff5e943865e230869e6de263e4e04d334eccdd35f8f4a59b34d5b60bda13bfa", + Vout: 1, + Sequence: 4294967295, + }, + { + ScriptSig: bchain.ScriptSig{ + Hex: "483045022100a2fba32aebca4eaa261f9ebd2b956ac22d9c29e7f65868acd60165077dcfbc85022011864df322178f515260c9f4c098112bfbc23bf257f38005b80a34271d08149b812102cc0f9a0906c0e8dffb3262778be1b3bc75e2d636fa01a7c2c129b1f3f30f21d3", + }, + Txid: "46a5dd851499dba7b40c107ad568f5c3fa397e328f1952c392a0be470867fb06", + Vout: 23, + Sequence: 4294967295, + }, + }, + Vout: []bchain.Vout{ + { + ValueSat: *big.NewInt(500000000000), + N: 0, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "76a914b61ba6aa3cc8be40e7553c8728ab3a303cbd4f2188ac", + Addresses: []string{ + "QdCtDST9o3JzQN1tchkpakAgGT4oSRhJec", + }, + }, + }, + { + ValueSat: *big.NewInt(587800300), + N: 1, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "76a9148e896f90d402cdb5517f7d1f32a3d9d400e4bbcb88ac", + Addresses: []string{ + "QZbehkDJekWHZeyKGwXDWqxy8m7RGWPeGn", + }, + }, + }, + }, + } +} + +func Test_PackTx(t *testing.T) { + type args struct { + tx bchain.Tx + height uint32 + blockTime int64 + parser *QtumParser + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "Qtum-1", + args: args{ + tx: testTx1, + height: 331712, + blockTime: 1552013184, + parser: NewQtumParser(GetChainParams("main"), &btc.Configuration{}), + }, + want: testTxPacked1, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.args.parser.PackTx(&tt.args.tx, tt.args.height, tt.args.blockTime) + if (err != nil) != tt.wantErr { + t.Errorf("packTx() error = %v, wantErr %v", err, tt.wantErr) + return + } + h := hex.EncodeToString(got) + if !reflect.DeepEqual(h, tt.want) { + t.Errorf("packTx() = %v, want %v", h, tt.want) + } + }) + } +} + +func Test_UnpackTx(t *testing.T) { + type args struct { + packedTx string + parser *QtumParser + } + tests := []struct { + name string + args args + want *bchain.Tx + want1 uint32 + wantErr bool + }{ + { + name: "Qtum-1", + args: args{ + packedTx: testTxPacked1, + parser: NewQtumParser(GetChainParams("main"), &btc.Configuration{}), + }, + want: &testTx1, + want1: 331712, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b, _ := hex.DecodeString(tt.args.packedTx) + got, got1, err := tt.args.parser.UnpackTx(b) + if (err != nil) != tt.wantErr { + t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("unpackTx() got = %v, want %v", got, tt.want) + } + if got1 != tt.want1 { + t.Errorf("unpackTx() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} diff --git a/bchain/coins/qtum/qtumrpc.go b/bchain/coins/qtum/qtumrpc.go new file mode 100644 index 00000000..b5a8cafc --- /dev/null +++ b/bchain/coins/qtum/qtumrpc.go @@ -0,0 +1,61 @@ +package qtum + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + "encoding/json" + "github.com/golang/glog" +) + +// QtumRPC is an interface to JSON-RPC bitcoind service. +type QtumRPC struct { + *btc.BitcoinRPC +} + +// NewQtumRPC returns new QtumRPC instance. +func NewQtumRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) { + b, err := btc.NewBitcoinRPC(config, pushHandler) + if err != nil { + return nil, err + } + + s := &QtumRPC{ + b.(*btc.BitcoinRPC), + } + s.RPCMarshaler = btc.JSONMarshalerV1{} + s.ChainConfig.SupportsEstimateSmartFee = true + + return s, nil +} + +// Initialize initializes QtumRPC instance. +func (b *QtumRPC) Initialize() error { + chainName, err := b.GetChainInfoAndInitializeMempool(b) + if err != nil { + return err + } + + params := GetChainParams(chainName) + + // always create parser + b.Parser = NewQtumParser(params, b.ChainConfig) + + // parameters for getInfo request + if params.Net == MainnetMagic { + b.Testnet = false + b.Network = "livenet" + } else { + b.Testnet = true + b.Network = "testnet" + } + + glog.Info("rpc: block chain ", params.Name) + + return nil +} + +// GetTransactionForMempool returns a transaction by the transaction ID +// It could be optimized for mempool, i.e. without block time and confirmations +func (b *QtumRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) { + return b.GetTransaction(txid) +} diff --git a/configs/coins/qtum.json b/configs/coins/qtum.json new file mode 100644 index 00000000..476d3c76 --- /dev/null +++ b/configs/coins/qtum.json @@ -0,0 +1,70 @@ +{ + "coin": { + "name": "Qtum", + "shortcut": "QTUM", + "label": "Qtum", + "alias": "qtum" + }, + "ports": { + "backend_rpc": 8088, + "backend_message_queue": 38388, + "blockbook_internal": 9088, + "blockbook_public": 9188 + }, + "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-qtum", + "package_revision": "satoshilabs-1", + "system_user": "qtum", + "version": "0.17.2", + "binary_url": "https://github.com/qtumproject/qtum/releases/download/mainnet-ignition-v0.17.2/qtum-0.17.2-x86_64-linux-gnu.tar.gz", + "verification_type": "sha256", + "verification_source": "f51926bf704c541e3ead5e5a14da6a4807cb7b761c23830aa37f044824e4fb33", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": [ + "bin/qtum-qt" + ], + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/qtumd -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", + "logevents": "1" + } + }, + "blockbook": { + "package_name": "blockbook-qtum", + "system_user": "blockbook-qtum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "xpub_magic": 76067358, + "xpub_magic_segwit_p2sh": 77429938, + "xpub_magic_segwit_native": 78792518, + "slip44": 2301, + "additional_params": {} + } + }, + "meta": { + "package_maintainer": "CodeFace", + "package_maintainer_email": "codeface@qtum.info" + } +} \ No newline at end of file diff --git a/configs/coins/qtum_testnet.json b/configs/coins/qtum_testnet.json new file mode 100644 index 00000000..525131b3 --- /dev/null +++ b/configs/coins/qtum_testnet.json @@ -0,0 +1,70 @@ +{ + "coin": { + "name": "Qtum Testnet", + "shortcut": "TQTUM", + "label": "Qtum Testnet", + "alias": "qtum_testnet" + }, + "ports": { + "backend_rpc": 18088, + "backend_message_queue": 48388, + "blockbook_internal": 19088, + "blockbook_public": 19188 + }, + "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-qtum-testnet", + "package_revision": "satoshilabs-1", + "system_user": "qtum", + "version": "0.17.2", + "binary_url": "https://github.com/qtumproject/qtum/releases/download/mainnet-ignition-v0.17.2/qtum-0.17.2-x86_64-linux-gnu.tar.gz", + "verification_type": "sha256", + "verification_source": "f51926bf704c541e3ead5e5a14da6a4807cb7b761c23830aa37f044824e4fb33", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": [ + "bin/qtum-qt" + ], + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/qtumd -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/testnet3/*.log", + "postinst_script_template": "", + "service_type": "forking", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": false, + "server_config_file": "bitcoin_like.conf", + "client_config_file": "bitcoin_like_client.conf", + "additional_params": { + "deprecatedrpc": "estimatefee", + "logevents": "1" + } + }, + "blockbook": { + "package_name": "blockbook-qtum-testnet", + "system_user": "blockbook-qtum", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "xpub_magic": 70617039, + "xpub_magic_segwit_p2sh": 71979618, + "xpub_magic_segwit_native": 73342198, + "slip44": 1, + "additional_params": {} + } + }, + "meta": { + "package_maintainer": "CodeFace", + "package_maintainer_email": "codeface@qtum.info" + } +} \ No newline at end of file diff --git a/docs/ports.md b/docs/ports.md index 569a3169..51c4b72b 100644 --- a/docs/ports.md +++ b/docs/ports.md @@ -26,6 +26,7 @@ | Koto | 9051 | 9151 | 8051 | 38351 | | Bellcoin | 9052 | 9152 | 8052 | 38352 | | Flo | 9066 | 9166 | 8066 | 38366 | +| Qtum | 9088 | 9188 | 8088 | 38388 | | Bitcoin Testnet | 19030 | 19130 | 18030 | 48330 | | Bitcoin Cash Testnet | 19031 | 19131 | 18031 | 48331 | | Zcash Testnet | 19032 | 19132 | 18032 | 48332 | @@ -38,5 +39,6 @@ | PIVX Testnet | 19049 | 19149 | 18049 | 48349 | | Koto Testnet | 19051 | 19151 | 18051 | 48351 | | Flo Testnet | 19066 | 19166 | 18066 | 48366 | +| Qtum Testnet | 19088 | 19188 | 18088 | 48388 | > NOTE: This document is generated from coin definitions in `configs/coins`. diff --git a/tests/rpc/testdata/qtum.json b/tests/rpc/testdata/qtum.json new file mode 100644 index 00000000..ca9dc9ff --- /dev/null +++ b/tests/rpc/testdata/qtum.json @@ -0,0 +1,170 @@ +{ + "blockHeight": 333514, + "blockHash": "249208b04314e1541c15d7676f09d2a47f8ea7be69b0271678304c5312d44873", + "blockTime": 1552272592, + "blockTxs": [ + "1dbdf1b25e3e85fc62158112468e65dba5d63b3824878650809a9718e71174eb", + "a391909c9521ee8e4489705366bf0bcddf84d8a34726ddfd503854ae4cdb4654" + ], + "txDetails": { + "1dbdf1b25e3e85fc62158112468e65dba5d63b3824878650809a9718e71174eb": { + "hex": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0503ca160500ffffffff020000000000000000000000000000000000266a24aa21a9ed598bdca4eab6001d5753ba33e9a50b1986649f4b477948b5f761decd6b742b4e0120000000000000000000000000000000000000000000000000000000000000000000000000", + "txid": "1dbdf1b25e3e85fc62158112468e65dba5d63b3824878650809a9718e71174eb", + "blocktime": 1552272592, + "time": 1552272592, + "locktime": 0, + "version": 2, + "vin": [ + { + "coinbase": "03ca160500", + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0, + "n": 0, + "scriptPubKey": { + "hex": "", + "type": "nonstandard" + } + }, + { + "value": 0, + "n": 1, + "scriptPubKey": { + "hex": "6a24aa21a9ed598bdca4eab6001d5753ba33e9a50b1986649f4b477948b5f761decd6b742b4e" + } + } + ] + }, + "a391909c9521ee8e4489705366bf0bcddf84d8a34726ddfd503854ae4cdb4654": { + "hex": "0200000001bb1bddb412ba1deda03d139889fc892e395d9ae20e5f956d8acb23ae64dc530401000000484730440220243bfff81362d09ee38fda1ac381905fef9af24cf3240a15c14985978c4cccee02204220fa5157693bfa01dd8a3b94ac23b35ba5bad20ff0f8f34c2a248ffacc4f8201ffffffff0b000000000000000000802c8c630400000023210254aaf2f2a7e1de6ff886fdd25ed92d472ecbf6aba7959fcc84079faef9fd0ee0ac005a6202000000001976a914a9f8f3b055324f6b2d6bcac328ec2d7e3cd22d8b88ac005a6202000000001976a914e05a3073deebefc491f782c8af8ad2899a6bf36388ac005a6202000000001976a914dbb6c84e8c0ea78482cc1db809d37572a8bf748e88ac005a6202000000001976a91423158af7bb43843d05bab9210917b9063413a3ae88ac005a6202000000001976a91490d7395c53fb6047a2ff7c7e2b42527ebc81832288ac005a6202000000001976a9140fb6d44794ae58756164b3377c3512286511840488ac005a6202000000001976a91453f6dc6a60a98921a3d72aea5dba2aefb6d7bd3888ac005a6202000000001976a914f94467916b9e0d985f90722e0d6f5b65846dfb5e88ac005a6202000000001976a91453f6dc6a60a98921a3d72aea5dba2aefb6d7bd3888ac00000000", + "txid": "a391909c9521ee8e4489705366bf0bcddf84d8a34726ddfd503854ae4cdb4654", + "blocktime": 1552272592, + "time": 1552272592, + "locktime": 0, + "version": 2, + "vin": [ + { + "txid": "0453dc64ae23cb8a6d955f0ee29a5d392e89fc8998133da0ed1dba12b4dd1bbb", + "vout": 1, + "scriptSig": { + "hex": "4730440220243bfff81362d09ee38fda1ac381905fef9af24cf3240a15c14985978c4cccee02204220fa5157693bfa01dd8a3b94ac23b35ba5bad20ff0f8f34c2a248ffacc4f8201" + }, + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.00000000, + "n": 0, + "scriptPubKey": { + "hex": "", + "type": "nonstandard" + } + }, + { + "value": 188.50000000, + "n": 1, + "scriptPubKey": { + "hex": "210254aaf2f2a7e1de6ff886fdd25ed92d472ecbf6aba7959fcc84079faef9fd0ee0ac", + "addresses": [ + "Qd3xCT4kKgTDw85vqgr1jXyHq3GqZvurpa" + ] + } + }, + { + "value": 0.40000000, + "n": 2, + "scriptPubKey": { + "hex": "76a914a9f8f3b055324f6b2d6bcac328ec2d7e3cd22d8b88ac", + "addresses": [ + "Qc6iYCZWn4BauKXGYirRG8pMtgdHMk2dzn" + ] + } + }, + { + "value": 0.40000000, + "n": 3, + "scriptPubKey": { + "hex": "76a914e05a3073deebefc491f782c8af8ad2899a6bf36388ac", + "addresses": [ + "Qh4FWjExZZj4v81xeLPG6oLB2tTnLt8BMQ" + ] + } + }, + { + "value": 0.40000000, + "n": 4, + "scriptPubKey": { + "hex": "76a914dbb6c84e8c0ea78482cc1db809d37572a8bf748e88ac", + "addresses": [ + "Qgdj4FrXXxLVkxPDYtFNNGZNd9TJrNjtpM" + ] + } + }, + { + "value": 0.40000000, + "n": 5, + "scriptPubKey": { + "hex": "76a91423158af7bb43843d05bab9210917b9063413a3ae88ac", + "addresses": [ + "QPoVZqSCxtWkHgNt8jkipv6KNJUMmE7gRT" + ] + } + }, + { + "value": 0.40000000, + "n": 6, + "scriptPubKey": { + "hex": "76a91490d7395c53fb6047a2ff7c7e2b42527ebc81832288ac", + "addresses": [ + "QZoqExD34XsaPyx61AmTRTLfSzwYK6pXXY" + ] + } + }, + { + "value": 0.40000000, + "n": 7, + "scriptPubKey": { + "hex": "76a9140fb6d44794ae58756164b3377c3512286511840488ac", + "addresses": [ + "QN35GNbDzzMAHLWBvej9vGucFLpZMoVz6N" + ] + } + }, + { + "value": 0.40000000, + "n": 8, + "scriptPubKey": { + "hex": "76a91453f6dc6a60a98921a3d72aea5dba2aefb6d7bd3888ac", + "addresses": [ + "QUFwvRXTnjnWmGVYxaXZoEAmMkKtkth8ND" + ] + } + }, + { + "value": 0.40000000, + "n": 9, + "scriptPubKey": { + "hex": "76a914f94467916b9e0d985f90722e0d6f5b65846dfb5e88ac", + "addresses": [ + "QjKzJtymBHQfuc7zGU6LSE1eHfJr2dFXpE" + ] + } + }, + { + "value": 0.40000000, + "n": 10, + "scriptPubKey": { + "hex": "76a91453f6dc6a60a98921a3d72aea5dba2aefb6d7bd3888ac", + "addresses": [ + "QUFwvRXTnjnWmGVYxaXZoEAmMkKtkth8ND" + ] + } + } + ] + } + } +} \ No newline at end of file diff --git a/tests/sync/testdata/qtum.json b/tests/sync/testdata/qtum.json new file mode 100644 index 00000000..ca4ba79c --- /dev/null +++ b/tests/sync/testdata/qtum.json @@ -0,0 +1,222 @@ +{ + "connectBlocks": { + "syncRanges": [ + { + "lower": 333494, + "upper": 333514 + } + ], + "blocks": { + "333514": { + "height": 333514, + "hash": "249208b04314e1541c15d7676f09d2a47f8ea7be69b0271678304c5312d44873", + "noTxs": 2, + "txDetails": [ + { + "hex": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0503ca160500ffffffff020000000000000000000000000000000000266a24aa21a9ed598bdca4eab6001d5753ba33e9a50b1986649f4b477948b5f761decd6b742b4e0120000000000000000000000000000000000000000000000000000000000000000000000000", + "txid": "1dbdf1b25e3e85fc62158112468e65dba5d63b3824878650809a9718e71174eb", + "blocktime": 1552272592, + "time": 1552272592, + "locktime": 0, + "version": 2, + "vin": [ + { + "coinbase": "03ca160500", + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0, + "n": 0, + "scriptPubKey": { + "hex": "", + "type": "nonstandard" + } + }, + { + "value": 0, + "n": 1, + "scriptPubKey": { + "hex": "6a24aa21a9ed598bdca4eab6001d5753ba33e9a50b1986649f4b477948b5f761decd6b742b4e" + } + } + ] + }, + { + "hex": "0200000001bb1bddb412ba1deda03d139889fc892e395d9ae20e5f956d8acb23ae64dc530401000000484730440220243bfff81362d09ee38fda1ac381905fef9af24cf3240a15c14985978c4cccee02204220fa5157693bfa01dd8a3b94ac23b35ba5bad20ff0f8f34c2a248ffacc4f8201ffffffff0b000000000000000000802c8c630400000023210254aaf2f2a7e1de6ff886fdd25ed92d472ecbf6aba7959fcc84079faef9fd0ee0ac005a6202000000001976a914a9f8f3b055324f6b2d6bcac328ec2d7e3cd22d8b88ac005a6202000000001976a914e05a3073deebefc491f782c8af8ad2899a6bf36388ac005a6202000000001976a914dbb6c84e8c0ea78482cc1db809d37572a8bf748e88ac005a6202000000001976a91423158af7bb43843d05bab9210917b9063413a3ae88ac005a6202000000001976a91490d7395c53fb6047a2ff7c7e2b42527ebc81832288ac005a6202000000001976a9140fb6d44794ae58756164b3377c3512286511840488ac005a6202000000001976a91453f6dc6a60a98921a3d72aea5dba2aefb6d7bd3888ac005a6202000000001976a914f94467916b9e0d985f90722e0d6f5b65846dfb5e88ac005a6202000000001976a91453f6dc6a60a98921a3d72aea5dba2aefb6d7bd3888ac00000000", + "txid": "a391909c9521ee8e4489705366bf0bcddf84d8a34726ddfd503854ae4cdb4654", + "blocktime": 1552272592, + "time": 1552272592, + "locktime": 0, + "version": 2, + "vin": [ + { + "txid": "0453dc64ae23cb8a6d955f0ee29a5d392e89fc8998133da0ed1dba12b4dd1bbb", + "vout": 1, + "scriptSig": { + "hex": "4730440220243bfff81362d09ee38fda1ac381905fef9af24cf3240a15c14985978c4cccee02204220fa5157693bfa01dd8a3b94ac23b35ba5bad20ff0f8f34c2a248ffacc4f8201" + }, + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.00000000, + "n": 0, + "scriptPubKey": { + "hex": "", + "type": "nonstandard" + } + }, + { + "value": 188.50000000, + "n": 1, + "scriptPubKey": { + "hex": "210254aaf2f2a7e1de6ff886fdd25ed92d472ecbf6aba7959fcc84079faef9fd0ee0ac", + "addresses": [ + "Qd3xCT4kKgTDw85vqgr1jXyHq3GqZvurpa" + ] + } + }, + { + "value": 0.40000000, + "n": 2, + "scriptPubKey": { + "hex": "76a914a9f8f3b055324f6b2d6bcac328ec2d7e3cd22d8b88ac", + "addresses": [ + "Qc6iYCZWn4BauKXGYirRG8pMtgdHMk2dzn" + ] + } + }, + { + "value": 0.40000000, + "n": 3, + "scriptPubKey": { + "hex": "76a914e05a3073deebefc491f782c8af8ad2899a6bf36388ac", + "addresses": [ + "Qh4FWjExZZj4v81xeLPG6oLB2tTnLt8BMQ" + ] + } + }, + { + "value": 0.40000000, + "n": 4, + "scriptPubKey": { + "hex": "76a914dbb6c84e8c0ea78482cc1db809d37572a8bf748e88ac", + "addresses": [ + "Qgdj4FrXXxLVkxPDYtFNNGZNd9TJrNjtpM" + ] + } + }, + { + "value": 0.40000000, + "n": 5, + "scriptPubKey": { + "hex": "76a91423158af7bb43843d05bab9210917b9063413a3ae88ac", + "addresses": [ + "QPoVZqSCxtWkHgNt8jkipv6KNJUMmE7gRT" + ] + } + }, + { + "value": 0.40000000, + "n": 6, + "scriptPubKey": { + "hex": "76a91490d7395c53fb6047a2ff7c7e2b42527ebc81832288ac", + "addresses": [ + "QZoqExD34XsaPyx61AmTRTLfSzwYK6pXXY" + ] + } + }, + { + "value": 0.40000000, + "n": 7, + "scriptPubKey": { + "hex": "76a9140fb6d44794ae58756164b3377c3512286511840488ac", + "addresses": [ + "QN35GNbDzzMAHLWBvej9vGucFLpZMoVz6N" + ] + } + }, + { + "value": 0.40000000, + "n": 8, + "scriptPubKey": { + "hex": "76a91453f6dc6a60a98921a3d72aea5dba2aefb6d7bd3888ac", + "addresses": [ + "QUFwvRXTnjnWmGVYxaXZoEAmMkKtkth8ND" + ] + } + }, + { + "value": 0.40000000, + "n": 9, + "scriptPubKey": { + "hex": "76a914f94467916b9e0d985f90722e0d6f5b65846dfb5e88ac", + "addresses": [ + "QjKzJtymBHQfuc7zGU6LSE1eHfJr2dFXpE" + ] + } + }, + { + "value": 0.40000000, + "n": 10, + "scriptPubKey": { + "hex": "76a91453f6dc6a60a98921a3d72aea5dba2aefb6d7bd3888ac", + "addresses": [ + "QUFwvRXTnjnWmGVYxaXZoEAmMkKtkth8ND" + ] + } + } + ] + } + ] + } + } + }, + "handleFork": { + "syncRanges": [ + { + "lower": 333494, + "upper": 333514 + } + ], + "fakeBlocks": { + "333511": { + "height": 333511, + "hash": "fb0e87a1ccadd775e7e7d31497ef30d8cf2ccc2bcbc57396a364c948b6c34016" + }, + "333512": { + "height": 333512, + "hash": "b5e1eb1e02a8f31390152534c4e05a3976ee6ba14689cfc0ba9a866de460b887" + }, + "333513": { + "height": 333513, + "hash": "1a2b9b3c74865c23612bd4dcb97b84213a4fc549cb5e8585a0117d53e9464e00" + }, + "333514": { + "height": 333514, + "hash": "5603634c6dd553695f46b4f30010b3181087215c7ed291992d1c2864d70f46fe" + } + }, + "realBlocks": { + "333511": { + "height": 333511, + "hash": "120d0f656b367137ea0ca88f928753d8fd2d0964cddb5474a4769a93a385f1b8" + }, + "333512": { + "height": 333512, + "hash": "cade18715c6ebb00ad530659cf1b2f3f1aacd5d87d5dc60bbfad61e886516dc2" + }, + "333513": { + "height": 333513, + "hash": "0c1648c30144646876ea4909ed9bacd6642904cb5bdd84e7180304f7b7ffae65" + }, + "333514": { + "height": 333514, + "hash": "249208b04314e1541c15d7676f09d2a47f8ea7be69b0271678304c5312d44873" + } + } + } +} diff --git a/tests/tests.json b/tests/tests.json index 6de78578..df34f0b4 100644 --- a/tests/tests.json +++ b/tests/tests.json @@ -120,5 +120,10 @@ "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"], "sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"] + }, + "qtum": { + "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", + "EstimateSmartFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"], + "sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"] } }