From 42d687112fa62d5cb659d7070c17a70f6427f8df Mon Sep 17 00:00:00 2001 From: Jakub Matys Date: Thu, 28 Jun 2018 15:32:32 +0200 Subject: [PATCH] Improvement of tests - added Bitcoin mainnet, rewritten mempool tests --- Gopkg.lock | 8 +- bchain/coins/bch/bcashrpc_test.go | 1 - bchain/coins/btc/bitcoinrpc_test.go | 95 ++++++++--- bchain/coins/dash/dashrpc_test.go | 1 - bchain/coins/eth/ethrpc_test.go | 1 - bchain/coins/namecoin/namecoinrpc_test.go | 1 - bchain/coins/vertcoin/vertcoinrpc_test.go | 1 - bchain/coins/zec/zcashrpc_test.go | 1 - bchain/tests/rpc/config.json | 45 +++-- bchain/tests/rpc/rpc.go | 194 ++++++++++++---------- bchain/tests/rpc/testdata/Bitcoin.json | 192 +++++++++++++++++++++ 11 files changed, 412 insertions(+), 128 deletions(-) create mode 100644 bchain/tests/rpc/testdata/Bitcoin.json diff --git a/Gopkg.lock b/Gopkg.lock index d3306553..a4242fc2 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -37,6 +37,12 @@ packages = ["."] revision = "12e86f41eb040d3b85b5d8e3a3a4bed035517c52" +[[projects]] + name = "github.com/deckarep/golang-set" + packages = ["."] + revision = "1d4478f51bed434f1dadf96dcd9b43aabac66795" + version = "v1.7" + [[projects]] name = "github.com/ethereum/go-ethereum" packages = [".","common","common/hexutil","common/math","core/types","crypto","crypto/secp256k1","crypto/sha3","ethclient","ethdb","log","metrics","params","rlp","rpc","trie"] @@ -202,6 +208,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "97b5e11b3aa46e6b54a5c3fd7835c49f324f9821a1c641e5682f60ee6716d8c2" + inputs-digest = "afcce626624acc0db803a97ce0238798cf034dc999161f32b4158c8033542cb7" solver-name = "gps-cdcl" solver-version = 1 diff --git a/bchain/coins/bch/bcashrpc_test.go b/bchain/coins/bch/bcashrpc_test.go index 70c5e69e..44060fc3 100644 --- a/bchain/coins/bch/bcashrpc_test.go +++ b/bchain/coins/bch/bcashrpc_test.go @@ -33,7 +33,6 @@ func TestMain(m *testing.M) { if err != nil { panic(err) } - t.TryConnect() rpcTest = t diff --git a/bchain/coins/btc/bitcoinrpc_test.go b/bchain/coins/btc/bitcoinrpc_test.go index 28da6dd8..a127fa49 100644 --- a/bchain/coins/btc/bitcoinrpc_test.go +++ b/bchain/coins/btc/bitcoinrpc_test.go @@ -11,67 +11,114 @@ import ( "testing" ) -func getRPCClient(cfg json.RawMessage) (bchain.BlockChain, error) { - c, err := NewBitcoinRPC(cfg, nil) - if err != nil { - return nil, err +func getRPCClient(chain string) func(json.RawMessage) (bchain.BlockChain, error) { + return func(cfg json.RawMessage) (bchain.BlockChain, error) { + c, err := NewBitcoinRPC(cfg, nil) + if err != nil { + return nil, err + } + cli := c.(*BitcoinRPC) + cli.Parser = NewBitcoinParser(GetChainParams(chain), cli.ChainConfig) + if err != nil { + return nil, err + } + cli.Mempool = bchain.NewUTXOMempool(cli, cli.ChainConfig.MempoolWorkers, cli.ChainConfig.MempoolSubWorkers) + return cli, nil } - cli := c.(*BitcoinRPC) - cli.Parser = NewBitcoinParser(GetChainParams("test"), cli.ChainConfig) - if err != nil { - return nil, err - } - cli.Mempool = bchain.NewUTXOMempool(cli, cli.ChainConfig.MempoolWorkers, cli.ChainConfig.MempoolSubWorkers) - return cli, nil } -var rpcTest *rpc.Test +var tests struct { + mainnet *rpc.Test + testnet *rpc.Test +} func TestMain(m *testing.M) { flag.Parse() - t, err := rpc.NewTest("Bitcoin Testnet", getRPCClient) + t, err := rpc.NewTest("Bitcoin", getRPCClient("main")) if err != nil { panic(err) } - t.TryConnect() - rpcTest = t + tests.mainnet = t + + t, err = rpc.NewTest("Bitcoin Testnet", getRPCClient("test")) + if err != nil { + panic(err) + } + + tests.testnet = t os.Exit(m.Run()) } func TestBitcoinRPC_GetBlockHash(t *testing.T) { - rpcTest.TestGetBlockHash(t) + tests.mainnet.TestGetBlockHash(t) } func TestBitcoinRPC_GetBlock(t *testing.T) { - rpcTest.TestGetBlock(t) + tests.mainnet.TestGetBlock(t) } func TestBitcoinRPC_GetTransaction(t *testing.T) { - rpcTest.TestGetTransaction(t) + tests.mainnet.TestGetTransaction(t) } func TestBitcoinRPC_GetTransactionForMempool(t *testing.T) { - rpcTest.TestGetTransactionForMempool(t) + tests.mainnet.TestGetTransactionForMempool(t) } func TestBitcoinRPC_MempoolSync(t *testing.T) { - rpcTest.TestMempoolSync(t) + tests.mainnet.TestMempoolSync(t) } func TestBitcoinRPC_GetMempoolEntry(t *testing.T) { - rpcTest.TestGetMempoolEntry(t) + tests.mainnet.TestGetMempoolEntry(t) } func TestBitcoinRPC_SendRawTransaction(t *testing.T) { - rpcTest.TestSendRawTransaction(t) + tests.mainnet.TestSendRawTransaction(t) } func TestBitcoinRPC_EstimateSmartFee(t *testing.T) { - rpcTest.TestEstimateSmartFee(t) + tests.mainnet.TestEstimateSmartFee(t) } func TestBitcoinRPC_EstimateFee(t *testing.T) { - rpcTest.TestEstimateFee(t) + tests.mainnet.TestEstimateFee(t) +} + +func TestBitcoinTestnetRPC_GetBlockHash(t *testing.T) { + tests.testnet.TestGetBlockHash(t) +} + +func TestBitcoinTestnetRPC_GetBlock(t *testing.T) { + tests.testnet.TestGetBlock(t) +} + +func TestBitcoinTestnetRPC_GetTransaction(t *testing.T) { + tests.testnet.TestGetTransaction(t) +} + +func TestBitcoinTestnetRPC_GetTransactionForMempool(t *testing.T) { + tests.testnet.TestGetTransactionForMempool(t) +} + +func TestBitcoinTestnetRPC_MempoolSync(t *testing.T) { + tests.testnet.TestMempoolSync(t) +} + +func TestBitcoinTestnetRPC_GetMempoolEntry(t *testing.T) { + tests.testnet.TestGetMempoolEntry(t) +} + +func TestBitcoinTestnetRPC_SendRawTransaction(t *testing.T) { + tests.testnet.TestSendRawTransaction(t) +} + +func TestBitcoinTestnetRPC_EstimateSmartFee(t *testing.T) { + tests.testnet.TestEstimateSmartFee(t) +} + +func TestBitcoinTestnetRPC_EstimateFee(t *testing.T) { + tests.testnet.TestEstimateFee(t) } diff --git a/bchain/coins/dash/dashrpc_test.go b/bchain/coins/dash/dashrpc_test.go index c0415c11..ed3b35e6 100644 --- a/bchain/coins/dash/dashrpc_test.go +++ b/bchain/coins/dash/dashrpc_test.go @@ -30,7 +30,6 @@ func TestMain(m *testing.M) { if err != nil { panic(err) } - t.TryConnect() rpcTest = t diff --git a/bchain/coins/eth/ethrpc_test.go b/bchain/coins/eth/ethrpc_test.go index dcfbacaf..db71706a 100644 --- a/bchain/coins/eth/ethrpc_test.go +++ b/bchain/coins/eth/ethrpc_test.go @@ -30,7 +30,6 @@ func TestMain(m *testing.M) { if err != nil { panic(err) } - t.TryConnect() rpcTest = t diff --git a/bchain/coins/namecoin/namecoinrpc_test.go b/bchain/coins/namecoin/namecoinrpc_test.go index a342225e..59af97d6 100644 --- a/bchain/coins/namecoin/namecoinrpc_test.go +++ b/bchain/coins/namecoin/namecoinrpc_test.go @@ -33,7 +33,6 @@ func TestMain(m *testing.M) { if err != nil { panic(err) } - t.TryConnect() rpcTest = t diff --git a/bchain/coins/vertcoin/vertcoinrpc_test.go b/bchain/coins/vertcoin/vertcoinrpc_test.go index c1a46604..8aeadff9 100644 --- a/bchain/coins/vertcoin/vertcoinrpc_test.go +++ b/bchain/coins/vertcoin/vertcoinrpc_test.go @@ -33,7 +33,6 @@ func TestMain(m *testing.M) { if err != nil { panic(err) } - t.TryConnect() rpcTest = t diff --git a/bchain/coins/zec/zcashrpc_test.go b/bchain/coins/zec/zcashrpc_test.go index 7d8aea6f..8541329a 100644 --- a/bchain/coins/zec/zcashrpc_test.go +++ b/bchain/coins/zec/zcashrpc_test.go @@ -30,7 +30,6 @@ func TestMain(m *testing.M) { if err != nil { panic(err) } - t.TryConnect() rpcTest = t diff --git a/bchain/tests/rpc/config.json b/bchain/tests/rpc/config.json index 170dda67..e4019e07 100644 --- a/bchain/tests/rpc/config.json +++ b/bchain/tests/rpc/config.json @@ -1,4 +1,39 @@ { + "Bcash": { + "url": "http://localhost:8031", + "user": "rpc", + "pass": "rpc" + }, + "Bitcoin": { + "url": "http://localhost:8030", + "user": "rpc", + "pass": "rpc" + }, + "Dash": { + "url": "http://localhost:8033", + "user": "rpc", + "pass": "rpc" + }, + "Ethereum": { + "url": "ws://localhost:8036", + "user": null, + "pass": null + }, + "Zcash": { + "url": "http://localhost:8032", + "user": "rpc", + "pass": "rpc" + }, + "Vertcoin": { + "url": "http://localhost:8040", + "user": "rpc", + "pass": "rpc" + }, + "Namecoin": { + "url": "http://localhost:8039", + "user": "rpc", + "pass": "rpc" + }, "Bcash Testnet": { "url": "http://localhost:18031", "user": "rpc", @@ -23,15 +58,5 @@ "url": "http://localhost:18032", "user": "rpc", "pass": "rpc" - }, - "Vertcoin": { - "url": "http://localhost:8040", - "user": "rpc", - "pass": "rpc" - }, - "Namecoin": { - "url": "http://localhost:8039", - "user": "rpc", - "pass": "rpc" } } diff --git a/bchain/tests/rpc/rpc.go b/bchain/tests/rpc/rpc.go index 460ec6e1..d3909f29 100644 --- a/bchain/tests/rpc/rpc.go +++ b/bchain/tests/rpc/rpc.go @@ -9,7 +9,8 @@ import ( "net" "reflect" "testing" - "time" + + "github.com/deckarep/golang-set" ) type TestConfig struct { @@ -21,7 +22,6 @@ type TestConfig struct { type TestData struct { BlockHeight uint32 `json:"blockHeight"` BlockHash string `json:"blockHash"` - BlockHex string `json:"blockHex"` BlockTxs []string `json:"blockTxs"` TxDetails map[string]*bchain.Tx `json:"txDetails"` } @@ -35,31 +35,57 @@ type Test struct { type TestChainFactoryFunc func(json.RawMessage) (bchain.BlockChain, error) func NewTest(coin string, factory TestChainFactoryFunc) (*Test, error) { - cfg, err := LoadRPCConfig(coin) - if err != nil { - return nil, err - } - cli, err := factory(cfg) - if err != nil { - return nil, err - } - td, err := LoadTestData(coin) + var ( + connected = true + cli bchain.BlockChain + cfg json.RawMessage + td *TestData + err error + ) + + cfg, err = LoadRPCConfig(coin) if err != nil { return nil, err } - if td.TxDetails != nil { - parser := cli.GetChainParser() + cli, err = factory(cfg) + if err != nil { + if isNetError(err) { + connected = false + } else { + return nil, err + } + } else { + td, err = LoadTestData(coin) + if err != nil { + return nil, err + } - for _, tx := range td.TxDetails { - err := setTxAddresses(parser, tx) - if err != nil { - return nil, err + if td.TxDetails != nil { + parser := cli.GetChainParser() + + for _, tx := range td.TxDetails { + err := setTxAddresses(parser, tx) + if err != nil { + return nil, err + } } } + + _, err = cli.GetBlockChainInfo() + if err != nil && isNetError(err) { + connected = false + } } - return &Test{Client: cli, TestData: td}, nil + return &Test{Client: cli, TestData: td, connected: connected}, nil +} + +func isNetError(err error) bool { + if _, ok := err.(net.Error); ok { + return true + } + return false } func setTxAddresses(parser bchain.BlockChainParser, tx *bchain.Tx) error { @@ -78,18 +104,6 @@ func setTxAddresses(parser bchain.BlockChainParser, tx *bchain.Tx) error { return err } -func (rt *Test) TryConnect() { - _, err := rt.Client.GetBlockChainInfo() - if err != nil { - switch err.(type) { - case net.Error: - rt.connected = false - return - } - } - rt.connected = true -} - func (rt *Test) skipUnconnected(t *testing.T) { if !rt.connected { t.Skip("Skipping test, not connected to backend service") @@ -154,44 +168,6 @@ func (rt *Test) TestGetTransaction(t *testing.T) { } } -func (rt *Test) getMempool(t *testing.T) []string { - var ( - txs []string - err error - ) - // attempts to get transactions for 2 min - for i := 0; i < 8; i++ { - txs, err = rt.Client.GetMempool() - if err != nil { - t.Fatal(err) - } - if len(txs) == 0 { - time.Sleep(15 * time.Second) - continue - } - - // done - break - } - if len(txs) == 0 { - t.Skipf("Skipping test, all attempts to get mempool failed") - } - - return txs -} - -func (rt *Test) getMempoolTransaction(t *testing.T, txid string) *bchain.Tx { - tx, err := rt.Client.GetTransactionForMempool(txid) - if err != nil { - t.Fatal(err) - } - if tx.Confirmations > 0 { - t.Skip("Skipping test, transaction moved away from mepool") - } - - return tx -} - func (rt *Test) TestGetTransactionForMempool(t *testing.T) { rt.skipUnconnected(t) @@ -211,10 +187,25 @@ func (rt *Test) TestGetTransactionForMempool(t *testing.T) { } } +func (rt *Test) getMempool(t *testing.T) []string { + txs, err := rt.Client.GetMempool() + if err != nil { + t.Fatal(err) + } + if len(txs) == 0 { + t.Skip("Skipping test, mempool is empty") + } + + return txs +} + func (rt *Test) getMempoolAddresses(t *testing.T, txs []string) map[string][]string { txid2addrs := map[string][]string{} for i := 0; i < len(txs); i++ { - tx := rt.getMempoolTransaction(t, txs[i]) + tx, err := rt.Client.GetTransactionForMempool(txs[i]) + if err != nil { + t.Fatal(err) + } addrs := []string{} for _, vin := range tx.Vin { for _, a := range vin.Addresses { @@ -238,32 +229,34 @@ func (rt *Test) TestMempoolSync(t *testing.T) { for i := 0; i < 3; i++ { txs := rt.getMempool(t) - txid2addrs := rt.getMempoolAddresses(t, txs) - if len(txid2addrs) == 0 { - t.Fatal("No transaction in mempool has any address") - } n, err := rt.Client.ResyncMempool(nil) if err != nil { t.Fatal(err) } - - if tmp := rt.getMempool(t); len(txs) != len(tmp) { - // mempool reset + if n == 0 { + // no transactions to test continue } - if len(txs) != n { - t.Fatalf("ResyncMempool() returned different number of transactions than backend call") + txs = intersect(txs, rt.getMempool(t)) + if len(txs) == 0 { + // no transactions to test + continue + } + + txid2addrs := rt.getMempoolAddresses(t, txs) + if len(txid2addrs) == 0 { + t.Skip("Skipping test, no addresses in mempool") } for txid, addrs := range txid2addrs { for _, a := range addrs { - txs, err := rt.Client.GetMempoolTransactions(a) + got, err := rt.Client.GetMempoolTransactions(a) if err != nil { t.Fatal(err) } - if !containsString(txs, txid) { + if !containsString(got, txid) { t.Errorf("ResyncMempool() - for address %s, transaction %s wasn't found in mempool", a, txid) return } @@ -276,6 +269,23 @@ func (rt *Test) TestMempoolSync(t *testing.T) { t.Skip("Skipping test, all attempts to sync mempool failed due to network state changes") } +func intersect(a, b []string) []string { + setA := mapset.NewSet() + for _, v := range a { + setA.Add(v) + } + setB := mapset.NewSet() + for _, v := range b { + setB.Add(v) + } + inter := setA.Intersect(setB) + res := make([]string, 0, inter.Cardinality()) + for v := range inter.Iter() { + res = append(res, v.(string)) + } + return res +} + func containsString(slice []string, s string) bool { for i := 0; i < len(slice); i++ { if slice[i] == s { @@ -295,16 +305,26 @@ func (rt *Test) TestGetMempoolEntry(t *testing.T) { t.Fatal(err) } - tx := rt.getMempoolTransaction(t, txs[rand.Intn(len(txs))]) - e, err := rt.Client.GetMempoolEntry(tx.Txid) + txid := txs[rand.Intn(len(txs))] + tx, err := rt.Client.GetTransactionForMempool(txid) if err != nil { - if err, ok := err.(*bchain.RPCError); ok && err.Code == -5 { - // mempool reset - continue - } + t.Fatal(err) + } + if tx.Confirmations > 0 { + // tx confirmed + continue } - if e.Height-h > 1 { + e, err := rt.Client.GetMempoolEntry(txid) + if err != nil { + if err, ok := err.(*bchain.RPCError); ok && err.Code == -5 { + // tx confirmed + continue + } + t.Fatal(err) + } + + if d := int(e.Height) - int(h); d < -1 || d > 1 { t.Errorf("GetMempoolEntry() got height %d, want %d", e.Height, h) } if e.Size <= 0 { diff --git a/bchain/tests/rpc/testdata/Bitcoin.json b/bchain/tests/rpc/testdata/Bitcoin.json new file mode 100644 index 00000000..0f7a09e4 --- /dev/null +++ b/bchain/tests/rpc/testdata/Bitcoin.json @@ -0,0 +1,192 @@ +{ + "blockHeight": 529150, + "blockHash": "00000000000000000035835503f43c878ebb643f3b40bdfd0dfda760da74e73c", + "blockTxs": [ + "8dd1379174e262d12a32d217e87a7caf09fa1b9e48a6fe010cac219f18c6de58", + "5fce44793b328ca5f142caadbf29efc78a0059d7a6379dff81fc6447b519a7c3", + "d5daab5d57ef089b0464932443bb52a818860e93c6a23d9a66e0749e0cc146da", + "96bf6e66ed65e6003b1c751a51ad6a4fa17465c73b64211989ebd50413a9cdc9", + "a81addeae3cddf2cb69a70f4dd85e2829bacc474b98a97ece6a7872cd15c37fe", + "dd6169e9227bc00e2f2bddf4f1eef6126998d984ce13083d7a11c6972ec6d25d", + "ca211af71c54c3d90b83851c1d35a73669040b82742dd7f95e39953b032f7d39", + "0d1463f05662ef6fa73f37c030908b8d890b8dabe217c25ebaf07057ecafafca", + "2276c68760b3ff3d32cf4ede7e4eb4be95b01d04629145a16bc57830b33fbc01", + "3abc8d3485a7505087997d63a72bb86d3cfce1b6b0057da722e2ae24715d8be5", + "88db71956b653875a06a84bbe6ef166df3b50c94a908f82d457192835be47c07", + "973e394c11a4339b25803eff85e9299688489c7e71b9110e77a6c469f996f9b4", + "7afbc04c39707ca334a6db6b94ec2421798770d6593b4ce1f19f64f6a6ae77b2", + "2a047219a5858c5a0068822b81775789ee07cdf0cdc91a2775dd2d520f390aa2", + "2ea3eebcdb11b46f0e5b42b7718eb1fb709b625bec98bee5e3bb7de32d456360", + "d37ee9bebeedb5aac15ece6e5d497371a43b4e93a060a92006530eb77d6fbd8a", + "a029561cbffc0b79bdc23fa987e463b16342aef976562ce13c213fa556d860ec", + "8f0e21edeff8b6654338c67b4e6980d82634c0d10509923afcab7a831a46683c", + "6884c257e43d4c7a81739acf852952b45d5bfb5fc07816fca326b94b174733c3", + "0206990ff4387238953164731fc7b3d216432e58db21a180bfd3eeeb8bf3e36a", + "f05d76253daaa0584feeac9b1ff7e57e8962dfa0731a8875a52871b201ce3bc5", + "65149c5de02e58416b7f923c0db4f1b3a945afbd8bd815beefc052e14bd7ac7d", + "e732c8fb6a2da39b22a39207d84ac0bd7450ced28617a47cc6b5b1166b9a74ec", + "24f4a06a88f234b63935ef74a7e42223ec8ab22689d497b2a29aee526dbdbb3e", + "6066e3885dd13f8ffdcb0d1a849a45bb79e0a2d0c140f7a45e957f9e9c1d7d39", + "43b3fa3c6e857df0a52ddc3d2e7fdb0c6593b1cda65251b5a4eef06bced31883", + "86c5b32ee229a59339caca990a364490fb9e2c7e5493810982849cb1d8e13f1a", + "95c9ea5d7c79cba2fd3ae868f1a73fe242ca3917ceef7027e9251b4d666fc43d", + "655296c13411a8973e97cab768f560f0dd297994c0189e9ca286adf4ec04393e", + "2cf86550a3a2497009b296e451cb94e58b713f3c2859e273fe2e4e160784672d", + "e89effee8a2787007b49d2fcdc880c66b2c1dc76bbf258e25f4dc07e8b362a8f", + "191f2d7ef3f7c2693a9930f4fcfa80769139fb7e10289dde71caa9102a329c39", + "6d5cf483c0013281a267bfa1d69fbe0a372c93b51eafc6c3b1ed2bb880b420e5", + "932684b0ce065488c8da5bc92b3a0c082971474ca48957c6c5c59bd20c37f285", + "33666f78256725d68a15b8e52c9c84ab6dbd7f74da474eb92e0909b3ff0f1c73", + "6e1df5ec9851f403b994d6a53e5aa8912838c9b775d250cdf31fcda0c2f1f4a4", + "ce87eccf8a294e2e0110d56a6ec1e5a4f854aea74a5bf7e7b00f9dd32ecb9341", + "820ed342a2e62613173c365b6ef9b35e4956d46a9f310fe4c4228488bf981a1e", + "4bd73f5b3e2f833e2922a62816d7fac391e7ecc3f628c7f15964efb64c4868fc", + "c3f2fab2af1efe1d8725497567cd6c791372ce3c71a2358e0f2ca175bdeaf9ca", + "aa4498ad03c15068515b21df561560fbbfc56d1bc05e8f396d2d22e023cfd19f", + "956e313eeb2a3364b6439d23338ea2e48a98ab04b9c82e2776d844c6247b4b77", + "22787283751d501687c42396110a59a390b45cc05ccf57493e523e2d66bfff31", + "a5a1c30d7b2e01e0b26179c74da3ecc1b68265583184fd548f5059d312ae3414", + "62ecf5ec79801ed7e4012ca8037d8deedc95e5c30ca7ec112a90d4ec5506eb6b", + "1898e3f94a7d68c73e828a0c87a74ff6f172f2b24240858229e63d603616fb21", + "cee2b19dc3021ecfb8a1f68060757d828088387708571c6a224e89a1ed9c14e6", + "930d785b0fa219930886cd5e93bab9f1f2111c67a5c089ee329745b2678c841d", + "7ad9179b7d990637f905ae3eb74b65792a3a7440aa7f59ba5274e211b26f649b", + "3e1ee4ddd2a990fc3195117005b4c53c9367316e85813ee437c43e927af08155", + "76cd3f44f4186757b0f2b83b66dfa02a85012f93a25d2ad670f30b97382adbb7", + "2e48b382fb84ac83f34e8c37baf89e6a87836b6eaf2a9180dcfd03d2816b4aee", + "dea813a8a0f702ba2800dd7046b138e81568edb089d26d93ee7a979225690e91", + "512c4c97ad1763bae3c0998d154e0e32897da633bc1558963d75dc57de164c8c", + "39d34eaec9b399df80b3d05b4b211f1b7b220578d13c9666a63f202afa8857f3", + "a79823ca003b64d78ccf28fad693eebba7e94c3089340c202199a459bc50dcbb", + "faef230df22406f367ba2e838b16eca0b581249a4476acc1a978613816dbec02", + "d99db7838ccb76c0ecfaa57b2c690cec56a711a5d880fe4e54853256bf213079", + "115122280ce9b781014584968e4b3e851e37141d81ebd403179c48c908672e92", + "7e08d9ec508fbaa69163e5aff05baf0e57af7503e11ac0dfe5dfeaf7b5f223c5", + "55c019b5aa708fe6c35e188db3f9561ff552ddb7edd142e3b4ca3743ee5ac6da", + "dff909d9469761d365083d1ba2e8702879d3455b03f09d98bbf3eee43ed155f6", + "81ce5eef1fcf57787da3c9deb75e715ba4039ea3c4a48cf5ed5206b889c3bc27", + "4d5d6fb00364fc40880f2912a28f1a6db506061dc85820d19af84ec31b5e5e60", + "4e9d2f8ff1b2e603d31449373d3b8a312661d6405e4be0aa2c679fecd486df7f", + "f7eb6eb8a3b5699956581c3e68b08f05da818e7bb1e2c28276cd638cacda7994", + "8b485e079126145df5704a813662e08f748fad098ea8d9b6b896879625724392", + "f7f05d889261ecc26968dbe03051e113584df5ca0c4bcff9e27664413c2e73d6", + "b4c531ea13369004393b5fc6835f5ac74bdc2f23adc5ad8e44a34256867496e2", + "3f63d00841e688e7006c7dff76c04a8caf183db36e00fb130b2a9bf64fad4cc3", + "edec5964af29256872caff91a8b74308346a10eaa1d17e5eeba4f5f0a4da8109", + "0d5db84f971ebdacae606c5f7c55a1d309ce6b45c5c1cab4097007047af4639b", + "2e438cad664dd495a7aaec45622880e3255f0822583b3ab5f7921800f49f962f", + "bb1c7b6b4c921283ac7f3683226d913824050bbb2150d58dae47c0dc541541a8", + "65d7aeaff3c481baca79cb2f3ca98026214258ca35bc78d4fa19aac8b01403ba", + "2ca7b1392be38314e4faffd4dd586327f27320fc7eeaaa8389f29a56a6e2d6ed", + "63da0eb29ad6c2523348f3bf97f78bd76124cd668a91b499553ac30aad7667d1", + "3f53be6d1ea2147f0c125da925b0bf95eadc74091e99fd9b79b562c507699446", + "023359558fa22bfa518f82e6a261079cce8b59b1cbe286d11ec741dae72a8d30", + "44d69736e6bef4885b6a2442ea4644da3c56586bc6cbabc984a23ce4a266cf54", + "e735844e2f88551f8da8a69171a05fda47fbdaa4ecd3d29b98eb167789f7a3ac", + "60428a78761e84826168626da18e7407780bd1188c13e6d4f12aaa67b632e0bb", + "ada31e8d2c0670ed091a0f75008ea3f0c37fc000f704c4b60575a69a0fda96c1", + "bc31227b15cf5ddcd5035c220dc9a37c39213dbaa97e536eb36339251750e202", + "90d51a46469bd8185245760ed25425dca259ffb1bda812c06bfabbf99dd54bf7", + "c46129d75b7ec2d85d166f1347a4c47c461d3dcd823a1f9b6e25ef41bd04d6a2", + "1ffe0fabb57af65df1dd8cf433298118deb9b10f7b91036e4accb94275d66801", + "6a7250ede081a5df65c263bef36954027cf4956e920f4dc8a143a9dc735f7470", + "f6a768252ef749a1ebc5a7240eae4367d1a0d1fbb7659839ed994c261c887ac6", + "01899428b0238563ec0e219bd96221aa9c2c24fd0287c174e147d5bab2a72ce2", + "d998a18380d0ff7a772e32b6a9d92236c0634965ae5f51a0a18e9136320554ef", + "85e95bada40bbf15df5929c8f052f6dc1eb12322c321441fa5aa009ac40f19d2", + "1b0dee271db263e03a7da56364773e89b1ba2153e7ccf0d4c1c80797eef8478c", + "19c80c35d5322c290c0ac68bd80507b826b17b6a9fca6f14ba5d054b573fb5e4", + "f9a4ccfad5e6c49c75d254d3bdb196c37731b99e99315551b6de111da09fb36e", + "edb417ef42cccd22f1e6f6fc6dc66c2c1217a155fab18c1ceb494f33c6e03c5d", + "75c25c7fd1f06d5d63e3265c3eb35c0e05aedaeac7e5b085285ddfab34f0a83d", + "92b420a8508fed0b94227f331aec6d6593444ff889724197973c89df609f504b", + "f9305e5fcbe1a303702388aaffb9a2cfd1b246a1db77bbd393b48a0276b54d54", + "f5eb58e2dc2fb8afb00508d1c407453f1e36a7a8cfa2e67006aad545a8558f63", + "67a7f5340f42fe61534716aa6b5fff1e04e31afb355b0431e2f61363ceef9095", + "4f1588d77167028e76db46ce3d0abb8722e02a8b1900a73d7bc0e1fef6c841be", + "456d721f9ea96d2cd7e60cec26eb294e897427bffa6a4c3bb25acfdc086173bf", + "eb68c506de42f1b59ec0ab7273872f78b0eee5d2ef57a09ece87b37c342fecda", + "0ffa190c414ffbb75a50853e9167f10778aca7fd7ac10d6b7178708812e143d2", + "c29a75edd34b55322b79c38a1a44be6e43e737539da389000b4d8b6e00d53c38", + "858b16eed193442d2ed01fa8687a5052d45ae27ef0fe285252bc7e734879497f", + "5bb96e0791ac797516e5fec6b70b469b1d1fd05bdd58c7d212b6a77af9bdff84", + "f141f208343ddeeb9da8797a2e15850979e71a1bbe69b225652fedb00c5e4987", + "a206757d3d27493d8ce80ed84ae907d283f7db7bf057aaccc182a15809b847b5" + ], + "txDetails": { + "4bd73f5b3e2f833e2922a62816d7fac391e7ecc3f628c7f15964efb64c4868fc": { + "hex": "02000000000101193855a716b4149420373dffa67ba2a16f4c696d7f257afeb2976ddd4902a0bc0100000017160014ef97a680f7d7ee02181749ed80b1bf23c6ae399efeffffff02255552000000000017a914f3346caa9e5349158cc18368bb72dd0c1790350a87f04e4800000000001976a914070628f88d48ef88034e967e582f57ecfad8c03788ac024830450221009dab05b240a5ae78a295f6a2c95e163691dc652c2d1c09cd34f8c8fcbfbb2677022019f53e52a4435f6038b99ec9f00854bd9de9ef84bf649013fc30efec1e26538a012103788b41f06266c5a98fb07af5f5fca21375aad9a3ae11fc9ae826cb48ee6b08aabc120800", + "txid": "4bd73f5b3e2f833e2922a62816d7fac391e7ecc3f628c7f15964efb64c4868fc", + "blocktime": 1529915213, + "time": 1529915213, + "locktime": 529084, + "vin": [ + { + "txid": "bca00249dd6d97b2fe7a257f6d694c6fa1a27ba6ff3d37209414b416a7553819", + "vout": 1, + "sequence": 4294967294, + "scriptSig": { + "hex": "160014ef97a680f7d7ee02181749ed80b1bf23c6ae399e" + } + } + ], + "vout": [ + { + "value": 0.05395749, + "n": 0, + "scriptPubKey": { + "hex": "a914f3346caa9e5349158cc18368bb72dd0c1790350a87" + } + }, + { + "value": 0.04738800, + "n": 1, + "scriptPubKey": { + "hex": "76a914070628f88d48ef88034e967e582f57ecfad8c03788ac" + } + } + ] + }, + "faef230df22406f367ba2e838b16eca0b581249a4476acc1a978613816dbec02": { + "hex": "0200000002a73d71157ae5f4372fe4624681bd72946a026e87a90cb2f307675146d5883941000000006a47304402202a6339b584730131f07c0c69ea40f08bc5c44cb161036509d2d0bef103c178c702206c0536316244acfdb0a27bf9a2ba4a6830b19ddc0fafd92027619dd4aa290bf1012102964e49b139cf408a30d4fc15e079789491689be74d63797e6bcbbe4191c8b691fefffffff9709ad0025e3968919c638559d00f8c8240b9b26a6624cc008548047e7af488010000006a47304402201cac13c2cbac8e536bc922462f810ffc086e8cf4a51a7f73d8d08aaf56372d6902206cc3518b6024d9b7c4f2f30e5dbb954f6d4d77e6b76eccc1081a8ed505892a7001210209fa85c88fb0b628305a169ca62a103c0da2cace04300810ae57755ef31caae9feffffff02ea3a0f00000000001976a9143e3fc495d359f2d346af7b42f70ddc7bb4981c1788ac40c06503000000001976a914f4274a0adee47dfab83664493bf252e3da0b5f5988acfd120800", + "txid": "faef230df22406f367ba2e838b16eca0b581249a4476acc1a978613816dbec02", + "blocktime": 1529915213, + "time": 1529915213, + "locktime": 529149, + "vin": [ + { + "txid": "413988d546516707f3b20ca9876e026a9472bd814662e42f37f4e57a15713da7", + "vout": 0, + "sequence": 4294967294, + "scriptSig": { + "hex": "47304402202a6339b584730131f07c0c69ea40f08bc5c44cb161036509d2d0bef103c178c702206c0536316244acfdb0a27bf9a2ba4a6830b19ddc0fafd92027619dd4aa290bf1012102964e49b139cf408a30d4fc15e079789491689be74d63797e6bcbbe4191c8b691" + } + }, + { + "txid": "88f47a7e04488500cc24666ab2b940828c0fd05985639c9168395e02d09a70f9", + "vout": 1, + "sequence": 4294967294, + "scriptSig": { + "hex": "47304402201cac13c2cbac8e536bc922462f810ffc086e8cf4a51a7f73d8d08aaf56372d6902206cc3518b6024d9b7c4f2f30e5dbb954f6d4d77e6b76eccc1081a8ed505892a7001210209fa85c88fb0b628305a169ca62a103c0da2cace04300810ae57755ef31caae9" + } + } + ], + "vout": [ + { + "value": 0.00998122, + "n": 0, + "scriptPubKey": { + "hex": "76a9143e3fc495d359f2d346af7b42f70ddc7bb4981c1788ac" + } + }, + { + "value": 0.57000000, + "n": 1, + "scriptPubKey": { + "hex": "76a914f4274a0adee47dfab83664493bf252e3da0b5f5988ac" + } + } + ] + } + } +}