WIP: sync integration tests
parent
f6fffefec9
commit
5754523317
|
@ -1,74 +0,0 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"blockbook/bchain"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TestData struct {
|
||||
BlockHeight uint32 `json:"blockHeight"`
|
||||
BlockHash string `json:"blockHash"`
|
||||
BlockTime int64 `json:"blockTime"`
|
||||
BlockTxs []string `json:"blockTxs"`
|
||||
TxDetails map[string]*bchain.Tx `json:"txDetails"`
|
||||
}
|
||||
|
||||
func joinPathsWithCommonElement(p1, p2 string) (string, bool) {
|
||||
idx := strings.IndexRune(p2, filepath.Separator)
|
||||
if idx <= 0 {
|
||||
return "", false
|
||||
}
|
||||
p2root := p2[:idx]
|
||||
idx = strings.LastIndex(p1, p2root)
|
||||
if idx < 0 {
|
||||
return "", false
|
||||
}
|
||||
prefix := p1[:idx]
|
||||
return filepath.Join(prefix, p2), true
|
||||
}
|
||||
|
||||
func readDataFile(dir, relDir, filename string) ([]byte, error) {
|
||||
var err error
|
||||
dir, err = filepath.Abs(dir)
|
||||
if err == nil {
|
||||
dir, err = filepath.EvalSymlinks(dir)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, ok := joinPathsWithCommonElement(dir, relDir)
|
||||
if !ok {
|
||||
return nil, errors.New("Path not found")
|
||||
}
|
||||
filename = strings.Replace(filename, " ", "_", -1)
|
||||
path = filepath.Join(path, filename)
|
||||
return ioutil.ReadFile(path)
|
||||
}
|
||||
|
||||
func LoadTestData(coin string, parser bchain.BlockChainParser) (*TestData, error) {
|
||||
b, err := readDataFile(".", "bchain/tests/rpc/testdata", coin+".json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var v TestData
|
||||
err = json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// convert amounts in test json to bit.Int and clear the temporary JsonValue
|
||||
for _, tx := range v.TxDetails {
|
||||
for i := range tx.Vout {
|
||||
vout := &tx.Vout[i]
|
||||
vout.ValueSat, err = parser.AmountToBigInt(vout.JsonValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vout.JsonValue = ""
|
||||
}
|
||||
}
|
||||
return &v, nil
|
||||
}
|
|
@ -59,9 +59,5 @@
|
|||
"meta": {
|
||||
"package_maintainer": "Jakub Matys",
|
||||
"package_maintainer_email": "jakub.matys@satoshilabs.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
|
||||
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,9 +59,5 @@
|
|||
"meta": {
|
||||
"package_maintainer": "Jakub Matys",
|
||||
"package_maintainer_email": "jakub.matys@satoshilabs.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
|
||||
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,9 +60,5 @@
|
|||
"meta": {
|
||||
"package_maintainer": "Jakub Matys",
|
||||
"package_maintainer_email": "jakub.matys@satoshilabs.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
|
||||
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,9 +60,5 @@
|
|||
"meta": {
|
||||
"package_maintainer": "Jakub Matys",
|
||||
"package_maintainer_email": "jakub.matys@satoshilabs.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
|
||||
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,9 +61,5 @@
|
|||
"meta": {
|
||||
"package_maintainer": "Jakub Matys",
|
||||
"package_maintainer_email": "jakub.matys@satoshilabs.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
|
||||
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,9 +61,5 @@
|
|||
"meta": {
|
||||
"package_maintainer": "Jakub Matys",
|
||||
"package_maintainer_email": "jakub.matys@satoshilabs.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
|
||||
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,8 +63,5 @@
|
|||
"meta": {
|
||||
"package_maintainer": "Jakub Matys",
|
||||
"package_maintainer_email": "jakub.matys@satoshilabs.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,8 +53,5 @@
|
|||
"meta": {
|
||||
"package_maintainer": "Jakub Matys",
|
||||
"package_maintainer_email": "jakub.matys@satoshilabs.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,9 +60,5 @@
|
|||
"meta": {
|
||||
"package_maintainer": "Jakub Matys",
|
||||
"package_maintainer_email": "jakub.matys@satoshilabs.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
|
||||
"EstimateFee"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,9 +60,5 @@
|
|||
"meta": {
|
||||
"package_maintainer": "wakiyamap",
|
||||
"package_maintainer_email": "wakiyamap@gmail.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
|
||||
"EstimateFee"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,9 +67,5 @@
|
|||
"meta": {
|
||||
"package_maintainer": "Jakub Matys",
|
||||
"package_maintainer_email": "jakub.matys@satoshilabs.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "MempoolSync", "EstimateSmartFee",
|
||||
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,9 +58,5 @@
|
|||
"meta": {
|
||||
"package_maintainer": "Jakub Matys",
|
||||
"package_maintainer_email": "jakub.matys@satoshilabs.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
|
||||
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,9 +60,5 @@
|
|||
"meta": {
|
||||
"package_maintainer": "Jakub Matys",
|
||||
"package_maintainer_email": "jakub.matys@satoshilabs.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
|
||||
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,9 +60,5 @@
|
|||
"meta": {
|
||||
"package_maintainer": "Jakub Matys",
|
||||
"package_maintainer_email": "jakub.matys@satoshilabs.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
|
||||
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,19 @@ fi
|
|||
|
||||
host=$1
|
||||
|
||||
get_port() {
|
||||
data=$1
|
||||
key=$2
|
||||
echo "${data}" | gawk "match(\$0, /\"${key}\":\s+([0-9]+)/, a) {print a[1]}" -
|
||||
}
|
||||
|
||||
# change dir to root of git repository
|
||||
cd $(cd $(dirname $(readlink -f $0)) && git rev-parse --show-toplevel)
|
||||
|
||||
# get all testnet ports from configs/
|
||||
testnet_ports=$(gawk 'match($0, /"rpcURL":\s+"(http|ws):\/\/[^:]+:([0-9]+)"/, a) {print a[2]}' configs/*_testnet*.json)
|
||||
ports=$(gawk 'match($0, /"backend_rpc":\s+([0-9]+)/, a) {print a[1]}' configs/coins/*.json)
|
||||
|
||||
for port in $testnet_ports
|
||||
for port in $ports
|
||||
do
|
||||
ssh -nNT -L $port:localhost:$port $host &
|
||||
pid=$!
|
||||
|
@ -26,9 +32,10 @@ at_exit() {
|
|||
|
||||
trap at_exit EXIT
|
||||
|
||||
wait -n
|
||||
code=$?
|
||||
|
||||
if [ $code != 0 ]; then
|
||||
exit $code
|
||||
fi
|
||||
sleep inf
|
||||
# wait -n
|
||||
# code=$?
|
||||
#
|
||||
# if [ $code != 0 ]; then
|
||||
# exit $code
|
||||
# fi
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"blockbook/bchain"
|
||||
"blockbook/bchain/coins"
|
||||
"blockbook/build/tools"
|
||||
"blockbook/tests/rpc"
|
||||
"blockbook/tests/sync"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type TestFunc func(t *testing.T, coin string, chain bchain.BlockChain, testConfig json.RawMessage)
|
||||
|
||||
var integrationTests = map[string]TestFunc{
|
||||
"rpc": rpc.IntegrationTest,
|
||||
"sync": sync.IntegrationTest,
|
||||
}
|
||||
|
||||
var notConnectedError = errors.New("Not connected to backend server")
|
||||
|
||||
func runIntegrationTests(t *testing.T) {
|
||||
tests, err := loadTests("tests.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for coin, cfg := range tests {
|
||||
t.Run(coin, func(t *testing.T) { runTests(t, coin, cfg) })
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func loadTests(path string) (map[string]map[string]json.RawMessage, error) {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v := make(map[string]map[string]json.RawMessage)
|
||||
err = json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}
|
||||
|
||||
func runTests(t *testing.T, coin string, cfg map[string]json.RawMessage) {
|
||||
if cfg == nil || len(cfg) == 0 {
|
||||
t.Skip("No tests to run")
|
||||
}
|
||||
|
||||
bc, err := makeBlockChain(coin)
|
||||
if err != nil {
|
||||
if err == notConnectedError {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Fatalf("Cannot make blockchain config: %s", err)
|
||||
}
|
||||
|
||||
for test, c := range cfg {
|
||||
if fn, found := integrationTests[test]; found {
|
||||
t.Run(test, func(t *testing.T) { fn(t, coin, bc, c) })
|
||||
} else {
|
||||
t.Errorf("Test not found: %s", test)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeBlockChain(coin string) (bchain.BlockChain, error) {
|
||||
c, err := build.LoadConfig("../configs", coin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputDir, err := ioutil.TempDir("", "integration_test")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
err = build.GeneratePackageDefinitions(c, "../build/templates", outputDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(filepath.Join(outputDir, "blockbook", "blockchaincfg.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cfg json.RawMessage
|
||||
err = json.Unmarshal(b, &cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
coinName, err := getName(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return initBlockChain(coinName, cfg)
|
||||
}
|
||||
|
||||
func getName(raw json.RawMessage) (string, error) {
|
||||
var cfg map[string]interface{}
|
||||
err := json.Unmarshal(raw, &cfg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if n, found := cfg["coin_name"]; found {
|
||||
switch n := n.(type) {
|
||||
case string:
|
||||
return n, nil
|
||||
default:
|
||||
return "", fmt.Errorf("Unexpected type of field `name`: %s", reflect.TypeOf(n))
|
||||
}
|
||||
} else {
|
||||
return "", errors.New("Missing field `name`")
|
||||
}
|
||||
}
|
||||
|
||||
func initBlockChain(coinName string, cfg json.RawMessage) (bchain.BlockChain, error) {
|
||||
factory, found := coins.BlockChainFactories[coinName]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("Factory function not found")
|
||||
}
|
||||
|
||||
cli, err := factory(cfg, func(_ bchain.NotificationType) {})
|
||||
if err != nil {
|
||||
if isNetError(err) {
|
||||
return nil, notConnectedError
|
||||
}
|
||||
return nil, fmt.Errorf("Factory function failed: %s", err)
|
||||
}
|
||||
|
||||
err = cli.Initialize()
|
||||
if err != nil {
|
||||
if isNetError(err) {
|
||||
return nil, notConnectedError
|
||||
}
|
||||
return nil, fmt.Errorf("BlockChain initialization failed: %s", err)
|
||||
}
|
||||
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
func isNetError(err error) bool {
|
||||
if _, ok := err.(net.Error); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// +build integration
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIntegration(t *testing.T) {
|
||||
runIntegrationTests(t)
|
||||
}
|
|
@ -1,20 +1,12 @@
|
|||
// +build integration
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"blockbook/bchain"
|
||||
"blockbook/bchain/coins"
|
||||
"blockbook/build/tools"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -35,124 +27,31 @@ var testMap = map[string]func(t *testing.T, th *TestHandler){
|
|||
}
|
||||
|
||||
type TestHandler struct {
|
||||
Client bchain.BlockChain
|
||||
TestData *TestData
|
||||
connected bool
|
||||
Chain bchain.BlockChain
|
||||
TestData *TestData
|
||||
}
|
||||
|
||||
var notConnectedError = errors.New("Not connected to backend server")
|
||||
|
||||
func TestRPCIntegration(t *testing.T) {
|
||||
src := os.Getenv("BLOCKBOOK_SRC")
|
||||
if src == "" {
|
||||
t.Fatalf("Missing environment variable BLOCKBOOK_SRC")
|
||||
}
|
||||
|
||||
configsDir := filepath.Join(src, "configs")
|
||||
templateDir := filepath.Join(src, "build/templates")
|
||||
|
||||
noTests := 0
|
||||
skippedTests := make([]string, 0, 10)
|
||||
|
||||
err := filepath.Walk(filepath.Join(configsDir, "coins"), func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() || info.Name()[0] == '.' {
|
||||
return nil
|
||||
}
|
||||
|
||||
n := strings.TrimSuffix(info.Name(), ".json")
|
||||
c, err := build.LoadConfig(configsDir, n)
|
||||
if err != nil {
|
||||
t.Errorf("%s: cannot load configuration: %s", n, err)
|
||||
return nil
|
||||
}
|
||||
if len(c.IntegrationTests["rpc"]) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg, err := makeBlockChainConfig(c, templateDir)
|
||||
if err != nil {
|
||||
t.Errorf("%s: cannot make blockchain config: %s", n, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
t.Run(c.Coin.Alias, func(t *testing.T) {
|
||||
noTests += 1
|
||||
err := runTests(t, c.Coin.Name, c.Coin.Alias, cfg, c.IntegrationTests["rpc"])
|
||||
if err != nil {
|
||||
if err == notConnectedError {
|
||||
skippedTests = append(skippedTests, c.Coin.Alias)
|
||||
t.Skip(err)
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(skippedTests) > 0 {
|
||||
t.Errorf("Too many skipped tests due to connection issues: %q", skippedTests)
|
||||
}
|
||||
type TestData struct {
|
||||
BlockHeight uint32 `json:"blockHeight"`
|
||||
BlockHash string `json:"blockHash"`
|
||||
BlockTime int64 `json:"blockTime"`
|
||||
BlockTxs []string `json:"blockTxs"`
|
||||
TxDetails map[string]*bchain.Tx `json:"txDetails"`
|
||||
}
|
||||
|
||||
func makeBlockChainConfig(c *build.Config, templateDir string) (json.RawMessage, error) {
|
||||
outputDir, err := ioutil.TempDir("", "rpc_test")
|
||||
func IntegrationTest(t *testing.T, coin string, chain bchain.BlockChain, testConfig json.RawMessage) {
|
||||
tests, err := getTests(testConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
t.Fatalf("Failed loading of test list: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(outputDir)
|
||||
|
||||
err = build.GeneratePackageDefinitions(c, templateDir, outputDir)
|
||||
parser := chain.GetChainParser()
|
||||
td, err := loadTestData(coin, parser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
t.Fatalf("Failed loading of test data: %s", err)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(filepath.Join(outputDir, "blockbook", "blockchaincfg.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var v json.RawMessage
|
||||
err = json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func runTests(t *testing.T, coinName, coinAlias string, cfg json.RawMessage, tests []string) error {
|
||||
cli, err := initBlockChain(coinName, cfg)
|
||||
if err != nil {
|
||||
if err == notConnectedError {
|
||||
return err
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
td, err := LoadTestData(coinAlias, cli.GetChainParser())
|
||||
if err != nil {
|
||||
t.Fatalf("Test data loading failed: %s", err)
|
||||
}
|
||||
|
||||
if td.TxDetails != nil {
|
||||
parser := cli.GetChainParser()
|
||||
|
||||
for _, tx := range td.TxDetails {
|
||||
err := setTxAddresses(parser, tx)
|
||||
if err != nil {
|
||||
t.Fatalf("Test data loading failed: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h := TestHandler{Client: cli, TestData: td}
|
||||
h := TestHandler{Chain: chain, TestData: td}
|
||||
|
||||
for _, test := range tests {
|
||||
if f, found := testMap[test]; found {
|
||||
|
@ -162,40 +61,50 @@ func runTests(t *testing.T, coinName, coinAlias string, cfg json.RawMessage, tes
|
|||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initBlockChain(coinName string, cfg json.RawMessage) (bchain.BlockChain, error) {
|
||||
factory, found := coins.BlockChainFactories[coinName]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("Factory function not found")
|
||||
}
|
||||
|
||||
cli, err := factory(cfg, func(_ bchain.NotificationType) {})
|
||||
func getTests(cfg json.RawMessage) ([]string, error) {
|
||||
var v []string
|
||||
err := json.Unmarshal(cfg, &v)
|
||||
if err != nil {
|
||||
if isNetError(err) {
|
||||
return nil, notConnectedError
|
||||
}
|
||||
return nil, fmt.Errorf("Factory function failed: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = cli.Initialize()
|
||||
if err != nil {
|
||||
if isNetError(err) {
|
||||
return nil, notConnectedError
|
||||
}
|
||||
return nil, fmt.Errorf("BlockChain initialization failed: %s", err)
|
||||
if len(v) == 0 {
|
||||
return nil, errors.New("No tests declared")
|
||||
}
|
||||
|
||||
return cli, nil
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func isNetError(err error) bool {
|
||||
if _, ok := err.(net.Error); ok {
|
||||
return true
|
||||
func loadTestData(coin string, parser bchain.BlockChainParser) (*TestData, error) {
|
||||
path := filepath.Join("rpc/testdata", coin+".json")
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return false
|
||||
var v TestData
|
||||
err = json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, tx := range v.TxDetails {
|
||||
// convert amounts in test json to bit.Int and clear the temporary JsonValue
|
||||
for i := range tx.Vout {
|
||||
vout := &tx.Vout[i]
|
||||
vout.ValueSat, err = parser.AmountToBigInt(vout.JsonValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vout.JsonValue = ""
|
||||
}
|
||||
|
||||
// get addresses parsed
|
||||
err := setTxAddresses(parser, tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &v, nil
|
||||
}
|
||||
|
||||
func setTxAddresses(parser bchain.BlockChainParser, tx *bchain.Tx) error {
|
||||
|
@ -214,7 +123,7 @@ func setTxAddresses(parser bchain.BlockChainParser, tx *bchain.Tx) error {
|
|||
}
|
||||
|
||||
func testGetBlockHash(t *testing.T, h *TestHandler) {
|
||||
hash, err := h.Client.GetBlockHash(h.TestData.BlockHeight)
|
||||
hash, err := h.Chain.GetBlockHash(h.TestData.BlockHeight)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -225,7 +134,7 @@ func testGetBlockHash(t *testing.T, h *TestHandler) {
|
|||
}
|
||||
}
|
||||
func testGetBlock(t *testing.T, h *TestHandler) {
|
||||
blk, err := h.Client.GetBlock(h.TestData.BlockHash, 0)
|
||||
blk, err := h.Chain.GetBlock(h.TestData.BlockHash, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -243,7 +152,7 @@ func testGetBlock(t *testing.T, h *TestHandler) {
|
|||
}
|
||||
func testGetTransaction(t *testing.T, h *TestHandler) {
|
||||
for txid, want := range h.TestData.TxDetails {
|
||||
got, err := h.Client.GetTransaction(txid)
|
||||
got, err := h.Chain.GetTransaction(txid)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -265,7 +174,7 @@ func testGetTransactionForMempool(t *testing.T, h *TestHandler) {
|
|||
// reset fields that are not parsed by BlockChainParser
|
||||
want.Confirmations, want.Blocktime, want.Time = 0, 0, 0
|
||||
|
||||
got, err := h.Client.GetTransactionForMempool(txid)
|
||||
got, err := h.Chain.GetTransactionForMempool(txid)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -280,7 +189,7 @@ func testMempoolSync(t *testing.T, h *TestHandler) {
|
|||
for i := 0; i < 3; i++ {
|
||||
txs := getMempool(t, h)
|
||||
|
||||
n, err := h.Client.ResyncMempool(nil)
|
||||
n, err := h.Chain.ResyncMempool(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -302,7 +211,7 @@ func testMempoolSync(t *testing.T, h *TestHandler) {
|
|||
|
||||
for txid, addrs := range txid2addrs {
|
||||
for _, a := range addrs {
|
||||
got, err := h.Client.GetMempoolTransactions(a)
|
||||
got, err := h.Chain.GetMempoolTransactions(a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -320,12 +229,12 @@ func testMempoolSync(t *testing.T, h *TestHandler) {
|
|||
}
|
||||
func testEstimateSmartFee(t *testing.T, h *TestHandler) {
|
||||
for _, blocks := range []int{1, 2, 3, 5, 10} {
|
||||
fee, err := h.Client.EstimateSmartFee(blocks, true)
|
||||
fee, err := h.Chain.EstimateSmartFee(blocks, true)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if fee.Sign() == -1 {
|
||||
sf := h.Client.GetChainParser().AmountToDecimalString(&fee)
|
||||
sf := h.Chain.GetChainParser().AmountToDecimalString(&fee)
|
||||
if sf != "-1" {
|
||||
t.Errorf("EstimateSmartFee() returned unexpected fee rate: %v", sf)
|
||||
}
|
||||
|
@ -334,12 +243,12 @@ func testEstimateSmartFee(t *testing.T, h *TestHandler) {
|
|||
}
|
||||
func testEstimateFee(t *testing.T, h *TestHandler) {
|
||||
for _, blocks := range []int{1, 2, 3, 5, 10} {
|
||||
fee, err := h.Client.EstimateFee(blocks)
|
||||
fee, err := h.Chain.EstimateFee(blocks)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if fee.Sign() == -1 {
|
||||
sf := h.Client.GetChainParser().AmountToDecimalString(&fee)
|
||||
sf := h.Chain.GetChainParser().AmountToDecimalString(&fee)
|
||||
if sf != "-1" {
|
||||
t.Errorf("EstimateFee() returned unexpected fee rate: %v", sf)
|
||||
}
|
||||
|
@ -348,16 +257,16 @@ func testEstimateFee(t *testing.T, h *TestHandler) {
|
|||
}
|
||||
func testGetBestBlockHash(t *testing.T, h *TestHandler) {
|
||||
for i := 0; i < 3; i++ {
|
||||
hash, err := h.Client.GetBestBlockHash()
|
||||
hash, err := h.Chain.GetBestBlockHash()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
height, err := h.Client.GetBestBlockHeight()
|
||||
height, err := h.Chain.GetBestBlockHeight()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hh, err := h.Client.GetBlockHash(height)
|
||||
hh, err := h.Chain.GetBlockHash(height)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -367,7 +276,7 @@ func testGetBestBlockHash(t *testing.T, h *TestHandler) {
|
|||
}
|
||||
|
||||
// we expect no next block
|
||||
_, err = h.Client.GetBlock("", height+1)
|
||||
_, err = h.Chain.GetBlock("", height+1)
|
||||
if err != nil {
|
||||
if err != bchain.ErrBlockNotFound {
|
||||
t.Error(err)
|
||||
|
@ -379,13 +288,13 @@ func testGetBestBlockHash(t *testing.T, h *TestHandler) {
|
|||
}
|
||||
func testGetBestBlockHeight(t *testing.T, h *TestHandler) {
|
||||
for i := 0; i < 3; i++ {
|
||||
height, err := h.Client.GetBestBlockHeight()
|
||||
height, err := h.Chain.GetBestBlockHeight()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// we expect no next block
|
||||
_, err = h.Client.GetBlock("", height+1)
|
||||
_, err = h.Chain.GetBlock("", height+1)
|
||||
if err != nil {
|
||||
if err != bchain.ErrBlockNotFound {
|
||||
t.Error(err)
|
||||
|
@ -402,7 +311,7 @@ func testGetBlockHeader(t *testing.T, h *TestHandler) {
|
|||
Time: h.TestData.BlockTime,
|
||||
}
|
||||
|
||||
got, err := h.Client.GetBlockHeader(h.TestData.BlockHash)
|
||||
got, err := h.Chain.GetBlockHeader(h.TestData.BlockHash)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -421,7 +330,7 @@ func testGetBlockHeader(t *testing.T, h *TestHandler) {
|
|||
}
|
||||
|
||||
func getMempool(t *testing.T, h *TestHandler) []string {
|
||||
txs, err := h.Client.GetMempool()
|
||||
txs, err := h.Chain.GetMempool()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -435,7 +344,7 @@ func getMempool(t *testing.T, h *TestHandler) []string {
|
|||
func getMempoolAddresses(t *testing.T, h *TestHandler, txs []string) map[string][]string {
|
||||
txid2addrs := map[string][]string{}
|
||||
for i := 0; i < len(txs); i++ {
|
||||
tx, err := h.Client.GetTransactionForMempool(txs[i])
|
||||
tx, err := h.Chain.GetTransactionForMempool(txs[i])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
|
@ -0,0 +1,404 @@
|
|||
package sync
|
||||
|
||||
import (
|
||||
"blockbook/bchain"
|
||||
"blockbook/common"
|
||||
"blockbook/db"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testMap = map[string]func(t *testing.T, th *TestHandler){
|
||||
// "ConnectBlocks": nil,
|
||||
"ConnectBlocksParallel": testConnectBlocksParallel,
|
||||
// "DisconnectBlocks": nil,
|
||||
}
|
||||
|
||||
type TestHandler struct {
|
||||
Coin string
|
||||
Chain bchain.BlockChain
|
||||
TestData *TestData
|
||||
}
|
||||
|
||||
type TestData struct {
|
||||
ConnectBlocksParallel struct {
|
||||
SyncWorkers int `json:"syncWorkers"`
|
||||
SyncChunk int `json:"syncChunk"`
|
||||
} `json:"connectBlocksParallel"`
|
||||
Blocks []BlockInfo `json:"blocks"`
|
||||
}
|
||||
|
||||
type BlockInfo struct {
|
||||
Height uint32 `json:"height"`
|
||||
Hash string `json:"hash"`
|
||||
NoTxs uint32 `json:"noTxs"`
|
||||
TxDetails []*bchain.Tx `json:"txDetails"`
|
||||
}
|
||||
|
||||
func IntegrationTest(t *testing.T, coin string, chain bchain.BlockChain, testConfig json.RawMessage) {
|
||||
tests, err := getTests(testConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed loading of test list: %s", err)
|
||||
}
|
||||
|
||||
parser := chain.GetChainParser()
|
||||
td, err := loadTestData(coin, parser)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed loading of test data: %s", err)
|
||||
}
|
||||
|
||||
h := TestHandler{Coin: coin, Chain: chain, TestData: td}
|
||||
|
||||
for _, test := range tests {
|
||||
if f, found := testMap[test]; found {
|
||||
t.Run(test, func(t *testing.T) { f(t, &h) })
|
||||
} else {
|
||||
t.Errorf("%s: test not found", test)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getTests(cfg json.RawMessage) ([]string, error) {
|
||||
var v []string
|
||||
err := json.Unmarshal(cfg, &v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(v) == 0 {
|
||||
return nil, errors.New("No tests declared")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func loadTestData(coin string, parser bchain.BlockChainParser) (*TestData, error) {
|
||||
path := filepath.Join("sync/testdata", coin+".json")
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var v TestData
|
||||
err = json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, b := range v.Blocks {
|
||||
for _, tx := range b.TxDetails {
|
||||
// convert amounts in test json to bit.Int and clear the temporary JsonValue
|
||||
for i := range tx.Vout {
|
||||
vout := &tx.Vout[i]
|
||||
vout.ValueSat, err = parser.AmountToBigInt(vout.JsonValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vout.JsonValue = ""
|
||||
}
|
||||
|
||||
// get addresses parsed
|
||||
err := setTxAddresses(parser, tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(v.Blocks, func(i, j int) bool {
|
||||
return v.Blocks[i].Height < v.Blocks[j].Height
|
||||
})
|
||||
|
||||
return &v, nil
|
||||
}
|
||||
|
||||
func setTxAddresses(parser bchain.BlockChainParser, tx *bchain.Tx) error {
|
||||
// pack and unpack transaction in order to get addresses decoded - ugly but works
|
||||
var tmp *bchain.Tx
|
||||
b, err := parser.PackTx(tx, 0, 0)
|
||||
if err == nil {
|
||||
tmp, _, err = parser.UnpackTx(b)
|
||||
if err == nil {
|
||||
for i := 0; i < len(tx.Vout); i++ {
|
||||
tx.Vout[i].ScriptPubKey.Addresses = tmp.Vout[i].ScriptPubKey.Addresses
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func makeRocksDB(parser bchain.BlockChainParser, m *common.Metrics, is *common.InternalState) (*db.RocksDB, func(), error) {
|
||||
p, err := ioutil.TempDir("", "sync_test")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
d, err := db.NewRocksDB(p, 100000, parser, m)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
d.SetInternalState(is)
|
||||
|
||||
closer := func() {
|
||||
d.Close()
|
||||
os.RemoveAll(p)
|
||||
}
|
||||
|
||||
return d, closer, nil
|
||||
}
|
||||
|
||||
func withRocksDBAndSyncWorker(t *testing.T, h *TestHandler, fn func(*db.RocksDB, *db.SyncWorker, chan os.Signal)) {
|
||||
m, err := common.GetMetrics(h.Coin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
is := &common.InternalState{}
|
||||
|
||||
d, closer, err := makeRocksDB(h.Chain.GetChainParser(), m, is)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer closer()
|
||||
|
||||
if len(h.TestData.Blocks) == 0 {
|
||||
t.Fatal("No test data")
|
||||
}
|
||||
|
||||
ch := make(chan os.Signal)
|
||||
|
||||
sw, err := db.NewSyncWorker(d, h.Chain, 3, 0, int(h.TestData.Blocks[0].Height), false, ch, m, is)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fn(d, sw, ch)
|
||||
}
|
||||
|
||||
func testConnectBlocksParallel(t *testing.T, h *TestHandler) {
|
||||
withRocksDBAndSyncWorker(t, h, func(d *db.RocksDB, sw *db.SyncWorker, ch chan os.Signal) {
|
||||
lowerHeight := h.TestData.Blocks[0].Height
|
||||
upperHeight := h.TestData.Blocks[len(h.TestData.Blocks)-1].Height
|
||||
upperHash := h.TestData.Blocks[len(h.TestData.Blocks)-1].Hash
|
||||
|
||||
err := sw.ConnectBlocksParallel(lowerHeight, upperHeight)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
height, hash, err := d.GetBestBlock()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if height != upperHeight {
|
||||
t.Fatalf("Upper block height mismatch: %d != %d", height, upperHeight)
|
||||
}
|
||||
if hash != upperHash {
|
||||
t.Fatalf("Upper block hash mismatch: %s != %s", hash, upperHash)
|
||||
}
|
||||
|
||||
t.Run("verifyBlockInfo", func(t *testing.T) { verifyBlockInfo(t, d, h) })
|
||||
t.Run("verifyTransactions", func(t *testing.T) { verifyTransactions(t, d, h) })
|
||||
t.Run("verifyAddresses", func(t *testing.T) { verifyAddresses(t, d, h) })
|
||||
})
|
||||
}
|
||||
|
||||
func verifyBlockInfo(t *testing.T, d *db.RocksDB, h *TestHandler) {
|
||||
for _, block := range h.TestData.Blocks {
|
||||
bi, err := d.GetBlockInfo(block.Height)
|
||||
if err != nil {
|
||||
t.Errorf("GetBlockInfo(%d) error: %s", block.Height, err)
|
||||
continue
|
||||
}
|
||||
if bi == nil {
|
||||
t.Errorf("GetBlockInfo(%d) returned nil", block.Height)
|
||||
continue
|
||||
}
|
||||
|
||||
if bi.Hash != block.Hash {
|
||||
t.Errorf("Block hash mismatch: %s != %s", bi.Hash, block.Hash)
|
||||
}
|
||||
|
||||
if bi.Txs != block.NoTxs {
|
||||
t.Errorf("Number of transactions in block %s mismatch: %d != %d", bi.Hash, bi.Txs, block.NoTxs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func verifyTransactions(t *testing.T, d *db.RocksDB, h *TestHandler) {
|
||||
type txInfo struct {
|
||||
txid string
|
||||
vout uint32
|
||||
isOutput bool
|
||||
}
|
||||
addr2txs := make(map[string][]txInfo)
|
||||
checkMap := make(map[string][]bool)
|
||||
|
||||
for _, block := range h.TestData.Blocks {
|
||||
for _, tx := range block.TxDetails {
|
||||
// for _, vin := range tx.Vin {
|
||||
// if vin.Txid != "" {
|
||||
// ta, err := d.GetTxAddresses(vin.Txid)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// if ta != nil {
|
||||
// if len(ta.Outputs) > int(vin.Vout) {
|
||||
// output := &ta.Outputs[vin.Vout]
|
||||
// voutAddr, _, err := output.Addresses(h.Chain.GetChainParser())
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// t.Logf("XXX: %q", voutAddr)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
for _, vin := range tx.Vin {
|
||||
for _, a := range vin.Addresses {
|
||||
addr2txs[a] = append(addr2txs[a], txInfo{tx.Txid, vin.Vout, false})
|
||||
checkMap[a] = append(checkMap[a], false)
|
||||
}
|
||||
}
|
||||
for _, vout := range tx.Vout {
|
||||
for _, a := range vout.ScriptPubKey.Addresses {
|
||||
addr2txs[a] = append(addr2txs[a], txInfo{tx.Txid, vout.N, true})
|
||||
checkMap[a] = append(checkMap[a], false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lowerHeight := h.TestData.Blocks[0].Height
|
||||
upperHeight := h.TestData.Blocks[len(h.TestData.Blocks)-1].Height
|
||||
for addr, txs := range addr2txs {
|
||||
err := d.GetTransactions(addr, lowerHeight, upperHeight, func(txid string, vout uint32, isOutput bool) error {
|
||||
for i, tx := range txs {
|
||||
if txid == tx.txid && vout == tx.vout && isOutput == tx.isOutput {
|
||||
checkMap[addr][i] = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
for addr, txs := range addr2txs {
|
||||
for i, tx := range txs {
|
||||
if !checkMap[addr][i] {
|
||||
t.Errorf("%s: transaction not found %+v", addr, tx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func verifyAddresses(t *testing.T, d *db.RocksDB, h *TestHandler) {
|
||||
parser := h.Chain.GetChainParser()
|
||||
|
||||
for _, block := range h.TestData.Blocks {
|
||||
for _, tx := range block.TxDetails {
|
||||
ta, err := d.GetTxAddresses(tx.Txid)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
txInfo := getTxInfo(tx)
|
||||
taInfo, err := getTaInfo(parser, ta)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if ta.Height != block.Height {
|
||||
t.Errorf("Tx %s: block height mismatch: %d != %d", tx.Txid, ta.Height, block.Height)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(txInfo.inputs) > 0 && !reflect.DeepEqual(taInfo.inputs, txInfo.inputs) {
|
||||
t.Errorf("Tx %s: inputs mismatch: got %q, want %q", tx.Txid, taInfo.inputs, txInfo.inputs)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(taInfo.outputs, txInfo.outputs) {
|
||||
t.Errorf("Tx %s: outputs mismatch: got %q, want %q", tx.Txid, taInfo.outputs, txInfo.outputs)
|
||||
}
|
||||
|
||||
taValIn := satToFloat(parser, &taInfo.valInSat)
|
||||
taValOut := satToFloat(parser, &taInfo.valOutSat)
|
||||
txValOut := satToFloat(parser, &txInfo.valOutSat)
|
||||
|
||||
if taValOut.Cmp(txValOut) != 0 {
|
||||
t.Errorf("Tx %s: total output amount mismatch: got %s, want %s",
|
||||
tx.Txid, taValOut.String(), txValOut.String())
|
||||
}
|
||||
|
||||
treshold := big.NewFloat(0.0001)
|
||||
if new(big.Float).Sub(taValIn, taValOut).Cmp(treshold) > 0 {
|
||||
t.Errorf("Tx %s: suspicious amounts: input ∑ [%s] - output ∑ [%s] > %s",
|
||||
tx.Txid, taValIn.String(), taValOut.String(), treshold)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type txInfo struct {
|
||||
inputs []string
|
||||
outputs []string
|
||||
valInSat big.Int
|
||||
valOutSat big.Int
|
||||
}
|
||||
|
||||
func getTxInfo(tx *bchain.Tx) *txInfo {
|
||||
info := &txInfo{inputs: []string{}, outputs: []string{}}
|
||||
|
||||
for _, vin := range tx.Vin {
|
||||
for _, a := range vin.Addresses {
|
||||
info.inputs = append(info.inputs, a)
|
||||
}
|
||||
}
|
||||
for _, vout := range tx.Vout {
|
||||
for _, a := range vout.ScriptPubKey.Addresses {
|
||||
info.outputs = append(info.outputs, a)
|
||||
}
|
||||
info.valOutSat.Add(&info.valOutSat, &vout.ValueSat)
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
func getTaInfo(parser bchain.BlockChainParser, ta *db.TxAddresses) (*txInfo, error) {
|
||||
info := &txInfo{inputs: []string{}, outputs: []string{}}
|
||||
|
||||
for i := range ta.Inputs {
|
||||
info.valInSat.Add(&info.valInSat, &ta.Inputs[i].ValueSat)
|
||||
addrs, _, err := ta.Inputs[i].Addresses(parser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info.inputs = append(info.inputs, addrs...)
|
||||
}
|
||||
|
||||
for i := range ta.Outputs {
|
||||
info.valOutSat.Add(&info.valOutSat, &ta.Outputs[i].ValueSat)
|
||||
addrs, _, err := ta.Outputs[i].Addresses(parser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info.outputs = append(info.outputs, addrs...)
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func satToFloat(parser bchain.BlockChainParser, sat *big.Int) *big.Float {
|
||||
f, ok := new(big.Float).SetString(parser.AmountToDecimalString(sat))
|
||||
if !ok {
|
||||
return big.NewFloat(-1)
|
||||
}
|
||||
return f
|
||||
}
|
|
@ -0,0 +1,310 @@
|
|||
{
|
||||
"connectBlocksParallel": {
|
||||
"syncWorkers": 3,
|
||||
"syncChunk": 0
|
||||
},
|
||||
"blocks": [
|
||||
{
|
||||
"height": 541224,
|
||||
"hash": "00000000000000000027fe3aca0241ccee4f3b103b2108457a2dfd7490aa41a8",
|
||||
"noTxs": 1880,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "02000000000101138032859ee12b6b46f2d20ab5bce9564dfcceef9b366c024c5b1bf21b33a47a01000000171600143544c8e7eacc7f624a7748e3623273145394f005feffffff02e09fc92c0000000017a91449c60d71775a5768a139d83e01665602a7319e598740420f00000000001976a91483b47502199d2599b7839ee96011b4340c5554d688ac02483045022100a788bfbc3ebb62ce034061dfe4a500de5ecad7db7c6f40c8d4af55c2d224b3dd02207aff39a1e2c422a96930a977dcb920b0ab80b35e978a7631692f887b0a49d0f0012103919187d19426a9b50012da2cfdebaf8fe45a730066f67ff628c38ce681231a9f22420800",
|
||||
"txid": "04bfdfaa2babb96581657c77729daa4da4fb1d221ba516393bab3e1a44e45c56",
|
||||
"blocktime": 1536829299,
|
||||
"time": 1536829299,
|
||||
"version": 2,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "7aa4331bf21b5b4c026c369befcefc4d56e9bcb50ad2f2466b2be19e85328013",
|
||||
"vout": 1,
|
||||
"sequence": 4294967294,
|
||||
"scriptSig": {
|
||||
"hex": "1600143544c8e7eacc7f624a7748e3623273145394f005"
|
||||
}
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 7.51411168,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "a91449c60d71775a5768a139d83e01665602a7319e5987"
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.01000000,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a91483b47502199d2599b7839ee96011b4340c5554d688ac"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hex": "02000000000101565ce4441a3eab3b3916a51b221dfba44daa9d72777c658165b9ab2baadfbf040000000017160014ef684f89a6656212f5659078c1111523be97f72bfeffffff0240420f00000000001976a914a885121ba438d8145a402e12b91ea1a43a8ba54b88acad5cba2c0000000017a91447f4c3bd4ecb0bd7f4d3489bf640352cab6fb6c58702483045022100f31cb0e57281dd732b20e468cd40dbd018b03631f0c0760565d1349ab46e5e29022046f754795d3a191a7731b470d5b6c5ba9f515c54d354a81e9c265fdfdcfbfd3e012103a596a6b5a398bf1d40778104d56a6317cc65981b6c28082989a4cbf74691d6f722420800",
|
||||
"txid": "b3f61e880609417a18a18a9e089e706822edd230e0a28316a30595f41eafc45f",
|
||||
"blocktime": 1536829299,
|
||||
"time": 1536829299,
|
||||
"version": 2,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "04bfdfaa2babb96581657c77729daa4da4fb1d221ba516393bab3e1a44e45c56",
|
||||
"vout": 0,
|
||||
"sequence": 4294967294,
|
||||
"scriptSig": {
|
||||
"hex": "160014ef684f89a6656212f5659078c1111523be97f72b"
|
||||
}
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.01000000,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914a885121ba438d8145a402e12b91ea1a43a8ba54b88ac"
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 7.50410925,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "a91447f4c3bd4ecb0bd7f4d3489bf640352cab6fb6c587"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"height": 541225,
|
||||
"hash": "00000000000000000023e1bf6bc0a71fb1340c00f256de589108a4768ea82c76",
|
||||
"noTxs": 1971,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "010000000001030d493576a4a74be8c2de4ffbc5307e27fe354241487c3c5a3a00435f960dca9a01000000232200203676b8ea63f8a38db4bd2a36ad3ecb080ff389a4fc56f85c8042b8136f1a5525ffffffffa531960755251678f3e1f0a76657dc39e71d2d069ab01f6269dc5c8d526e78af14000000fdfe0000483045022100ef26a403ec643750e5dd88211fc2a0432e1b7aa76b5f3eec6c88694947ea67bf02201942f492871eb3178e381f96372658f65e5144ade142d502be99738021f2ae7401483045022100d960c87e0e62362bb2f275c0b4f46a7e1ca1957b25585ba95310511910cca4c102200620f7535478428a2458628c0b25eeadf75fc24fafd480a01396f75dd0c1e7e9014c6952210289a0758ddbdee43fb55af018757ae15d880759ae87c152d1831056e042a3a4362102dc2256a7fcdadc5df21bb668681dcaf234cd0f933066cfc3ccb7de71ea6ab4c421038fdea78199b4554e16aa28d76741b8ad75d5265926d16b5c4cd68e2ec1f66f2253aeffffffff58a39080604b5e99dedba40beb18f5316a66d8c1c8fc054a77d72300476ff05e0000000023220020af51de0aec71ce9d8529b5d196d04c00ca5c63888ae1318ad2e8b682481e8763ffffffff027c2d15000000000017a91473829585e38acb264c21ab7e1d8d4814337c4ba287496800000000000017a91450be45be5aa3ee42827c1eb72370fde4665acfaa870400473044022079fa3a1abf6c9265592cbe2b4a7cac5855bd764f66fc7ceed9e15b5d81184b00022024e6c885cb5c9300f0a6a070b1a6a613ea1137a863253d3737a8e75327cb620f01483045022100dc8f2f4b43cf94849e238b1a94cf9ce0a9fe6e875dcef200d4a9704e29fcc1a902200186ac5327fb4c67c2b663b9ca89b7fd1078cff5851abca987beb0ae3faee31001695221021dd05af6e6e36aa7c7fa3a366f8ba5aefbfa96f8d2014d52bb912f413d4ff1662102b5ed803ebd815d2af641793fcc96dc87bbb1b982ba26b82c59d64cce2e44d2522103c0e71c8ebcf021ceb93ac97182e22c89f9b3ef3e82a59750f593f27f0b8f32c553ae00040047304402204236e7aac97e6df9af208cc0238b409e122ea4bdbee67f4df3f69c3b99ca76a60220202fe1ee41236e997ce9f96f07129dd19d7b98a0c084d74e621a01b742c5425001483045022100b34e348784d84dd9a97c3da856676d035460b11b7e62d66c2f04e770a72bafb40220794eebb88a764b14e0c648cc54f7133115348c1063e3d74b1c92282f479a01d501695221027894bff7c2f0a6faab636188dc8e9915a18f4927f21d25a3a927827e42a22f4c210284efcef0f2f18068454982e29aedbae12e8b6e463e21dfe97a29bd7a1caa559a2102d3b7ad3ad3a5719caa7d9e66ed8412f56eb3b6ce09752c2474695b7e01ec345553ae00000000",
|
||||
"txid": "3a05d0b3bbb23b23888708dbdb89b729871e1f8cce69874a03a52d401a745f2b",
|
||||
"blocktime": 1536830751,
|
||||
"time": 1536830751,
|
||||
"version": 1,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "9aca0d965f43003a5a3c7c48414235fe277e30c5fb4fdec2e84ba7a47635490d",
|
||||
"vout": 1,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "2200203676b8ea63f8a38db4bd2a36ad3ecb080ff389a4fc56f85c8042b8136f1a5525"
|
||||
}
|
||||
},
|
||||
{
|
||||
"txid": "af786e528d5cdc69621fb09a062d1de739dc5766a7f0e1f378162555079631a5",
|
||||
"vout": 20,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "00483045022100ef26a403ec643750e5dd88211fc2a0432e1b7aa76b5f3eec6c88694947ea67bf02201942f492871eb3178e381f96372658f65e5144ade142d502be99738021f2ae7401483045022100d960c87e0e62362bb2f275c0b4f46a7e1ca1957b25585ba95310511910cca4c102200620f7535478428a2458628c0b25eeadf75fc24fafd480a01396f75dd0c1e7e9014c6952210289a0758ddbdee43fb55af018757ae15d880759ae87c152d1831056e042a3a4362102dc2256a7fcdadc5df21bb668681dcaf234cd0f933066cfc3ccb7de71ea6ab4c421038fdea78199b4554e16aa28d76741b8ad75d5265926d16b5c4cd68e2ec1f66f2253ae"
|
||||
}
|
||||
},
|
||||
{
|
||||
"txid": "5ef06f470023d7774a05fcc8c1d8666a31f518eb0ba4dbde995e4b608090a358",
|
||||
"vout": 0,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "220020af51de0aec71ce9d8529b5d196d04c00ca5c63888ae1318ad2e8b682481e8763"
|
||||
}
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.01387900,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "a91473829585e38acb264c21ab7e1d8d4814337c4ba287"
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00026697,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "a91450be45be5aa3ee42827c1eb72370fde4665acfaa87"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"height": 541226,
|
||||
"hash": "00000000000000000023fc3b42ff5da9f74c6a672a7f793860f7c03b217628ae",
|
||||
"noTxs": 2479,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "02000000014dd8ff786e18c1c48fbaece303c6da31b02013d9122091d5bb5c14e351178bd1030000006a47304402200f46c8a00c84dfc980a39f1db80b9008a06b1fdbe9893bca36ac9278ce1201e30220455a67d71d968a0a32e7d7fd99f1f32055c0d692abb009d9fdfdfb95d011f14f012102133c477d27b26eac6fc51c4452e7b7a6071b77af1869b750c14601e3821a51cafeffffff02e8f10200000000001976a914b18cf542d661fc17a04a29f91b03056e3d7f2aaa88ac51a39500000000001976a914194e18702f84b5c87f307662887ca7ec18482bc488ac28420800",
|
||||
"txid": "62665cce55418efe3dfe035d6f2c72259f1bf4a4a95dbb6bc27df39e9fd6f1fb",
|
||||
"blocktime": 1536831767,
|
||||
"time": 1536831767,
|
||||
"locktime": 541224,
|
||||
"version": 2,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "d18b1751e3145cbbd5912012d91320b031dac603e3ecba8fc4c1186e78ffd84d",
|
||||
"vout": 3,
|
||||
"sequence": 4294967294,
|
||||
"scriptSig": {
|
||||
"hex": "47304402200f46c8a00c84dfc980a39f1db80b9008a06b1fdbe9893bca36ac9278ce1201e30220455a67d71d968a0a32e7d7fd99f1f32055c0d692abb009d9fdfdfb95d011f14f012102133c477d27b26eac6fc51c4452e7b7a6071b77af1869b750c14601e3821a51ca"
|
||||
}
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.00193000,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914b18cf542d661fc17a04a29f91b03056e3d7f2aaa88ac"
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.09806673,
|
||||
"n": 1,
|
||||
"scriptPubkey": {
|
||||
"hex": "76a914194e18702f84b5c87f307662887ca7ec18482bc488ac"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"height": 541227,
|
||||
"hash": "00000000000000000007d2b42e5ea24bed4aa02d18192c33b9ec7f6685acb16a",
|
||||
"noTxs": 1815,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "010000000176ccfc9ac16ced1eebb4e886169a03b9cff16610b8e1dcd8d380110537f6eff4000000006b483045022100b5417fc0e95b39ce2036d67621eb332dd9091734081108630f0521bd9fec1867022070080f1a568eb39dcbad467eb041862daedf8e7b14e3986bc8ab89947a5c8dc6012103aae7dca1bc6f38f77b767ddc6b3dd0ef86c294543c65bfd5cd6ac535f4f54dcdffffffff0206bfc901000000001976a914059272d492ba10fab46e2fe0154ae9200027974d88ac801d2c04000000001976a9145c02a3783346f0a8cd4c5918c625ab13c5be698e88ac00000000",
|
||||
"txid": "d37262282c3bdd3da47caa663fc21eda2c9db51b43f2a4bde39527fdad569308",
|
||||
"blocktime": 1536831879,
|
||||
"time": 1536831879,
|
||||
"locktime": 0,
|
||||
"version": 1,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "f4eff637051180d3d8dce1b81066f1cfb9039a1686e8b4eb1eed6cc19afccc76",
|
||||
"vout": 0,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "483045022100b5417fc0e95b39ce2036d67621eb332dd9091734081108630f0521bd9fec1867022070080f1a568eb39dcbad467eb041862daedf8e7b14e3986bc8ab89947a5c8dc6012103aae7dca1bc6f38f77b767ddc6b3dd0ef86c294543c65bfd5cd6ac535f4f54dcd"
|
||||
},
|
||||
"addresses": ["1PE9dMki67qUYnX5J8APFTHvUJVWxEUoQP"]
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.29998854,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914059272d492ba10fab46e2fe0154ae9200027974d88ac"
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.70000000,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a9145c02a3783346f0a8cd4c5918c625ab13c5be698e88ac"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"height": 541228,
|
||||
"hash": "0000000000000000001c2444541142d58b126ef608b5f52081aced02612d078f",
|
||||
"noTxs": 2549,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "020000000001012790904fd89e78bf372a8d79bac24ae21c0b60d65761a7fbf1002ce28d4db8fd00000000171600146432f02f2f8f1d1a7d58d83b1815cd300417404dfeffffff027dff11000000000017a91401dfd360c8b640f9da5ede7170dc369d84c3dfcf87503403000000000017a91454f7c31d5a1aac2f6b3d6b4773f62f333e2e8af8870247304402205a89be503e481e37b7175a988f7f1276e6f56d477570d3745dda126f9521bba402201cfafd0f575b6617a6f1d2c3062f398e7be43830a2786d5c9848092cc8382f600121020dc52cfdf50ad27ce9b6701e0d88d2177ad118d43c864a3cc491bd4b7ddf5dc827420800",
|
||||
"txid": "3dbf8bf9d9dc161024c08fc22c938426f975da4a1da7830257cf149c260e6f89",
|
||||
"blocktime": 1536832036,
|
||||
"time": 1536832036,
|
||||
"locktime": 541223,
|
||||
"version": 2,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "fdb84d8de22c00f1fba76157d6600b1ce24ac2ba798d2a37bf789ed84f909027",
|
||||
"vout": 0,
|
||||
"sequence": 4294967294,
|
||||
"scriptSig": {
|
||||
"hex": "1600146432f02f2f8f1d1a7d58d83b1815cd300417404d"
|
||||
}
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.01179517,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "a91401dfd360c8b640f9da5ede7170dc369d84c3dfcf87"
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00210000,
|
||||
"n": 1,
|
||||
"scriptPubkey": {
|
||||
"hex": "a91454f7c31d5a1aac2f6b3d6b4773f62f333e2e8af887"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"height": 541255,
|
||||
"hash": "0000000000000000001375c3e88b4e53b687a70aba4b645ba81c93a4539d724d",
|
||||
"noTxs": 2633,
|
||||
"txDetails": [
|
||||
{
|
||||
"hex": "010000000263bc2bd6ba344d39487c3c8a29eb0b71d118bef0c4318e3f9d6cd6c0a63b2e34010000006b483045022100fe6a3fb0fafa305a7cf0dd55f670e6a6469392d16bd7395ac3eecd31b48f04dc022008e4c23e7657135bb6e11727e3bc4f67aa4c827f4cfb45b27a8df531295b6a5a0121030e2844042326a3dad34937b2a577977b6d13d87a50812f351735fe1ebd999fb7ffffffff17809d201a1cb3a7da30de83f9d92b52e6f78f37c5bc6f3c105127bdb6dc9291000000006a473044022053e8788b622f6ed5ed69bffb6f2c568d42d1bb398eb6c8b9d7d011e1280c261a0220243a3346883ed0acd7a291140ddc520d43100f60e68fd4a242ec70bae95c097e0121030e2844042326a3dad34937b2a577977b6d13d87a50812f351735fe1ebd999fb7ffffffff020a39e800000000001976a914a1d8d0f5edd2bf6808d7156f2884096fd86359fe88ac00e1f505000000001976a914492f305f58b7469202526bff3200a9f2a446fb4988ac00000000",
|
||||
"txid": "dcc55f1443084d97bcf17e7d7ac1d589ea947f4940030779a40cfd9731a24a9e",
|
||||
"blocktime": 1536832036,
|
||||
"time": 1536832036,
|
||||
"locktime": 541223,
|
||||
"version": 2,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "342e3ba6c0d66c9d3f8e31c4f0be18d1710beb298a3c7c48394d34bad62bbc63",
|
||||
"vout": 1,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "483045022100fe6a3fb0fafa305a7cf0dd55f670e6a6469392d16bd7395ac3eecd31b48f04dc022008e4c23e7657135bb6e11727e3bc4f67aa4c827f4cfb45b27a8df531295b6a5a0121030e2844042326a3dad34937b2a577977b6d13d87a50812f351735fe1ebd999fb7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"txid": "9192dcb6bd2751103c6fbcc5378ff7e6522bd9f983de30daa7b31c1a209d8017",
|
||||
"vout": 0,
|
||||
"sequence": 4294967295,
|
||||
"scriptSig": {
|
||||
"hex": "473044022053e8788b622f6ed5ed69bffb6f2c568d42d1bb398eb6c8b9d7d011e1280c261a0220243a3346883ed0acd7a291140ddc520d43100f60e68fd4a242ec70bae95c097e0121030e2844042326a3dad34937b2a577977b6d13d87a50812f351735fe1ebd999fb7"
|
||||
}
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.15218954,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"hex": "76a914a1d8d0f5edd2bf6808d7156f2884096fd86359fe88ac"
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 1.00000000,
|
||||
"n": 1,
|
||||
"scriptPubkey": {
|
||||
"hex": "76a914492f305f58b7469202526bff3200a9f2a446fb4988ac"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"bcash": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
|
||||
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
},
|
||||
"bcash_testnet": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
|
||||
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
},
|
||||
"bitcoin": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
|
||||
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
|
||||
"sync": ["ConnectBlocksParallel"]
|
||||
},
|
||||
"bitcoin_testnet": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
|
||||
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
},
|
||||
"dash": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
|
||||
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
},
|
||||
"dash_testnet": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
|
||||
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
},
|
||||
"dogecoin": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync"]
|
||||
},
|
||||
"ethereum_testnet_ropsten": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight",
|
||||
"GetBlockHeader"]
|
||||
},
|
||||
"litecoin": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
|
||||
"EstimateSmartFee", "EstimateFee"]
|
||||
},
|
||||
"monacoin": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
|
||||
"EstimateSmartFee", "EstimateFee"]
|
||||
},
|
||||
"namecoin": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "MempoolSync", "EstimateSmartFee",
|
||||
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
},
|
||||
"vertcoin": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
|
||||
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
},
|
||||
"zcash": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
|
||||
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
},
|
||||
"zcash_testnet": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
|
||||
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue