diff --git a/blockbook.go b/blockbook.go index c45728f7..8bd998d9 100644 --- a/blockbook.go +++ b/blockbook.go @@ -293,7 +293,7 @@ func main() { if *synchronize { internalState.SyncMode = true internalState.InitialSync = true - if err := syncWorker.ResyncIndex(nil); err != nil { + if err := syncWorker.ResyncIndex(nil, true); err != nil { glog.Error("resyncIndex ", err) return } @@ -427,7 +427,7 @@ func syncIndexLoop() { glog.Info("syncIndexLoop starting") // resync index about every 15 minutes if there are no chanSyncIndex requests, with debounce 1 second tickAndDebounce(time.Duration(*resyncIndexPeriodMs)*time.Millisecond, debounceResyncIndexMs*time.Millisecond, chanSyncIndex, func() { - if err := syncWorker.ResyncIndex(onNewBlockHash); err != nil { + if err := syncWorker.ResyncIndex(onNewBlockHash, false); err != nil { glog.Error("syncIndexLoop ", errors.ErrorStack(err)) } }) diff --git a/db/sync.go b/db/sync.go index 468c2fca..6bae828f 100644 --- a/db/sync.go +++ b/db/sync.go @@ -47,11 +47,11 @@ var errSynced = errors.New("synced") // ResyncIndex synchronizes index to the top of the blockchain // onNewBlock is called when new block is connected, but not in initial parallel sync -func (w *SyncWorker) ResyncIndex(onNewBlock bchain.OnNewBlockFunc) error { +func (w *SyncWorker) ResyncIndex(onNewBlock bchain.OnNewBlockFunc, initialSync bool) error { start := time.Now() w.is.StartedSync() - err := w.resyncIndex(onNewBlock) + err := w.resyncIndex(onNewBlock, initialSync) switch err { case nil: @@ -76,7 +76,7 @@ func (w *SyncWorker) ResyncIndex(onNewBlock bchain.OnNewBlockFunc) error { return err } -func (w *SyncWorker) resyncIndex(onNewBlock bchain.OnNewBlockFunc) error { +func (w *SyncWorker) resyncIndex(onNewBlock bchain.OnNewBlockFunc, initialSync bool) error { remoteBestHash, err := w.chain.GetBestBlockHash() if err != nil { return err @@ -99,7 +99,7 @@ func (w *SyncWorker) resyncIndex(onNewBlock bchain.OnNewBlockFunc) error { if remoteHash != localBestHash { // forked - the remote hash differs from the local hash at the same height glog.Info("resync: local is forked at height ", localBestHeight, ", local hash ", localBestHash, ", remote hash", remoteHash) - return w.handleFork(localBestHeight, localBestHash, onNewBlock) + return w.handleFork(localBestHeight, localBestHash, onNewBlock, initialSync) } glog.Info("resync: local at ", localBestHeight, " is behind") w.startHeight = localBestHeight + 1 @@ -130,13 +130,13 @@ func (w *SyncWorker) resyncIndex(onNewBlock bchain.OnNewBlockFunc) error { } // after parallel load finish the sync using standard way, // new blocks may have been created in the meantime - return w.resyncIndex(onNewBlock) + return w.resyncIndex(onNewBlock, initialSync) } } - return w.connectBlocks(onNewBlock) + return w.connectBlocks(onNewBlock, initialSync) } -func (w *SyncWorker) handleFork(localBestHeight uint32, localBestHash string, onNewBlock bchain.OnNewBlockFunc) error { +func (w *SyncWorker) handleFork(localBestHeight uint32, localBestHash string, onNewBlock bchain.OnNewBlockFunc, initialSync bool) error { // find forked blocks, disconnect them and then synchronize again var height uint32 hashes := []string{localBestHash} @@ -161,18 +161,19 @@ func (w *SyncWorker) handleFork(localBestHeight uint32, localBestHash string, on if err := w.DisconnectBlocks(height+1, localBestHeight, hashes); err != nil { return err } - return w.resyncIndex(onNewBlock) + return w.resyncIndex(onNewBlock, initialSync) } -func (w *SyncWorker) connectBlocks(onNewBlock bchain.OnNewBlockFunc) error { +func (w *SyncWorker) connectBlocks(onNewBlock bchain.OnNewBlockFunc, initialSync bool) error { bch := make(chan blockResult, 8) done := make(chan struct{}) defer close(done) go w.getBlockChain(bch, done) - var lastRes blockResult - for res := range bch { + var lastRes, empty blockResult + + connect := func(res blockResult) error { lastRes = res if res.err != nil { return res.err @@ -187,6 +188,34 @@ func (w *SyncWorker) connectBlocks(onNewBlock bchain.OnNewBlockFunc) error { if res.block.Height > 0 && res.block.Height%1000 == 0 { glog.Info("connected block ", res.block.Height, " ", res.block.Hash) } + + return nil + } + + if initialSync { + ConnectLoop: + for { + select { + case <-w.chanOsSignal: + return errors.Errorf("connectBlocks interrupted at height %d", lastRes.block.Height) + case res := <-bch: + if res == empty { + break ConnectLoop + } + err := connect(res) + if err != nil { + return err + } + } + } + } else { + // while regular sync, OS sig is handled by waitForSignalAndShutdown + for res := range bch { + err := connect(res) + if err != nil { + return err + } + } } if lastRes.block != nil { diff --git a/db/test_helper.go b/db/test_helper.go new file mode 100644 index 00000000..5d5d4984 --- /dev/null +++ b/db/test_helper.go @@ -0,0 +1,19 @@ +// +build integration + +package db + +import ( + "blockbook/bchain" +) + +func SetBlockChain(w *SyncWorker, chain bchain.BlockChain) { + w.chain = chain +} + +func ConnectBlocks(w *SyncWorker, onNewBlock bchain.OnNewBlockFunc, initialSync bool) error { + return w.connectBlocks(onNewBlock, initialSync) +} + +func HandleFork(w *SyncWorker, localBestHeight uint32, localBestHash string, onNewBlock bchain.OnNewBlockFunc, initialSync bool) error { + return w.handleFork(localBestHeight, localBestHash, onNewBlock, initialSync) +} diff --git a/tests/doc.go b/tests/doc.go new file mode 100644 index 00000000..12f5c534 --- /dev/null +++ b/tests/doc.go @@ -0,0 +1,2 @@ +// Package tests provides functions for loading and running integration tests +package tests diff --git a/tests/integration.go b/tests/integration.go index 990a6064..e0483472 100644 --- a/tests/integration.go +++ b/tests/integration.go @@ -1,3 +1,5 @@ +// +build integration + package tests import ( @@ -14,6 +16,7 @@ import ( "os" "path/filepath" "reflect" + "sort" "testing" "github.com/jakm/btcutil/chaincfg" @@ -34,7 +37,14 @@ func runIntegrationTests(t *testing.T) { t.Fatal(err) } - for coin, cfg := range tests { + keys := make([]string, 0, len(tests)) + for k := range tests { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, coin := range keys { + cfg := tests[coin] t.Run(coin, func(t *testing.T) { runTests(t, coin, cfg) }) } diff --git a/tests/rpc/doc.go b/tests/rpc/doc.go new file mode 100644 index 00000000..4d4eb2d9 --- /dev/null +++ b/tests/rpc/doc.go @@ -0,0 +1,2 @@ +// Package rpc implements integration tests of blockchain RPC layer +package rpc diff --git a/tests/rpc/rpc.go b/tests/rpc/rpc.go index ecca3b74..b5f2216d 100644 --- a/tests/rpc/rpc.go +++ b/tests/rpc/rpc.go @@ -1,9 +1,10 @@ +// +build integration + package rpc import ( "blockbook/bchain" "encoding/json" - "errors" "io/ioutil" "path/filepath" "reflect" @@ -11,6 +12,7 @@ import ( "time" "github.com/deckarep/golang-set" + "github.com/juju/errors" ) var testMap = map[string]func(t *testing.T, th *TestHandler){ @@ -108,18 +110,19 @@ func loadTestData(coin string, parser bchain.BlockChainParser) (*TestData, error } 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 - } + for i := range tx.Vout { + ad, err := parser.GetAddrDescFromVout(&tx.Vout[i]) + if err != nil { + return err } + addrs := []string{} + a, s, err := parser.GetAddressesFromAddrDesc(ad) + if err == nil && s { + addrs = append(addrs, a...) + } + tx.Vout[i].ScriptPubKey.Addresses = addrs } - return err + return nil } func testGetBlockHash(t *testing.T, h *TestHandler) { @@ -204,7 +207,7 @@ func testMempoolSync(t *testing.T, h *TestHandler) { continue } - txid2addrs := getMempoolAddresses(t, h, txs) + txid2addrs := getTxid2addrs(t, h, txs) if len(txid2addrs) == 0 { t.Skip("Skipping test, no addresses in mempool") } @@ -213,7 +216,7 @@ func testMempoolSync(t *testing.T, h *TestHandler) { for _, a := range addrs { got, err := h.Chain.GetMempoolTransactions(a) if err != nil { - t.Fatal(err) + t.Fatalf("address %q: %s", a, err) } if !containsString(got, txid) { t.Errorf("ResyncMempool() - for address %s, transaction %s wasn't found in mempool", a, txid) @@ -341,26 +344,21 @@ func getMempool(t *testing.T, h *TestHandler) []string { return txs } -func getMempoolAddresses(t *testing.T, h *TestHandler, txs []string) map[string][]string { +func getTxid2addrs(t *testing.T, h *TestHandler, txs []string) map[string][]string { txid2addrs := map[string][]string{} - for i := 0; i < len(txs); i++ { + for i := range txs { tx, err := h.Chain.GetTransactionForMempool(txs[i]) if err != nil { + if isMissingTx(err) { + continue + } t.Fatal(err) } + setTxAddresses(h.Chain.GetChainParser(), tx) addrs := []string{} - for _, vin := range tx.Vin { - for _, a := range vin.Addresses { - if isSearchableAddr(a) { - addrs = append(addrs, a) - } - } - } - for _, vout := range tx.Vout { - for _, a := range vout.ScriptPubKey.Addresses { - if isSearchableAddr(a) { - addrs = append(addrs, a) - } + for j := range tx.Vout { + for _, a := range tx.Vout[j].ScriptPubKey.Addresses { + addrs = append(addrs, a) } } if len(addrs) > 0 { @@ -370,8 +368,18 @@ func getMempoolAddresses(t *testing.T, h *TestHandler, txs []string) map[string] return txid2addrs } -func isSearchableAddr(addr string) bool { - return len(addr) > 3 && addr[:3] != "OP_" +func isMissingTx(err error) bool { + switch e1 := err.(type) { + case *errors.Err: + switch e2 := e1.Cause().(type) { + case *bchain.RPCError: + if e2.Code == -5 { // "No such mempool or blockchain transaction" + return true + } + } + } + + return false } func intersect(a, b []string) []string { @@ -392,7 +400,7 @@ func intersect(a, b []string) []string { } func containsString(slice []string, s string) bool { - for i := 0; i < len(slice); i++ { + for i := range slice { if slice[i] == s { return true } diff --git a/tests/sync/connectblocks.go b/tests/sync/connectblocks.go new file mode 100644 index 00000000..1c481ee3 --- /dev/null +++ b/tests/sync/connectblocks.go @@ -0,0 +1,265 @@ +// +build integration + +package sync + +import ( + "blockbook/bchain" + "blockbook/db" + "fmt" + "math/big" + "os" + "reflect" + "strings" + "testing" +) + +func testConnectBlocks(t *testing.T, h *TestHandler) { + for _, rng := range h.TestData.ConnectBlocks.SyncRanges { + withRocksDBAndSyncWorker(t, h, rng.Lower, func(d *db.RocksDB, sw *db.SyncWorker, ch chan os.Signal) { + upperHash, err := h.Chain.GetBlockHash(rng.Upper) + if err != nil { + t.Fatal(err) + } + + err = db.ConnectBlocks(sw, func(hash string, height uint32) { + if hash == upperHash { + close(ch) + } + }, true) + if err != nil { + if err.Error() != fmt.Sprintf("connectBlocks interrupted at height %d", rng.Upper) { + t.Fatal(err) + } + } + + height, hash, err := d.GetBestBlock() + if err != nil { + t.Fatal(err) + } + if height != rng.Upper { + t.Fatalf("Upper block height mismatch: %d != %d", height, rng.Upper) + } + if hash != upperHash { + t.Fatalf("Upper block hash mismatch: %s != %s", hash, upperHash) + } + + t.Run("verifyBlockInfo", func(t *testing.T) { verifyBlockInfo(t, d, h, rng) }) + t.Run("verifyTransactions", func(t *testing.T) { verifyTransactions(t, d, h, rng) }) + t.Run("verifyAddresses", func(t *testing.T) { verifyAddresses(t, d, h, rng) }) + }) + } +} + +func testConnectBlocksParallel(t *testing.T, h *TestHandler) { + for _, rng := range h.TestData.ConnectBlocks.SyncRanges { + withRocksDBAndSyncWorker(t, h, rng.Lower, func(d *db.RocksDB, sw *db.SyncWorker, ch chan os.Signal) { + upperHash, err := h.Chain.GetBlockHash(rng.Upper) + if err != nil { + t.Fatal(err) + } + + err = sw.ConnectBlocksParallel(rng.Lower, rng.Upper) + if err != nil { + t.Fatal(err) + } + + height, hash, err := d.GetBestBlock() + if err != nil { + t.Fatal(err) + } + if height != rng.Upper { + t.Fatalf("Upper block height mismatch: %d != %d", height, rng.Upper) + } + if hash != upperHash { + t.Fatalf("Upper block hash mismatch: %s != %s", hash, upperHash) + } + + t.Run("verifyBlockInfo", func(t *testing.T) { verifyBlockInfo(t, d, h, rng) }) + t.Run("verifyTransactions", func(t *testing.T) { verifyTransactions(t, d, h, rng) }) + t.Run("verifyAddresses", func(t *testing.T) { verifyAddresses(t, d, h, rng) }) + }) + } +} + +func verifyBlockInfo(t *testing.T, d *db.RocksDB, h *TestHandler, rng Range) { + for height := rng.Lower; height <= rng.Upper; height++ { + block, found := h.TestData.ConnectBlocks.Blocks[height] + if !found { + continue + } + + bi, err := d.GetBlockInfo(height) + if err != nil { + t.Errorf("GetBlockInfo(%d) error: %s", height, err) + continue + } + if bi == nil { + t.Errorf("GetBlockInfo(%d) returned nil", 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, rng Range) { + type txInfo struct { + txid string + vout uint32 + isOutput bool + } + addr2txs := make(map[string][]txInfo) + checkMap := make(map[string][]bool) + + for height := rng.Lower; height <= rng.Upper; height++ { + block, found := h.TestData.ConnectBlocks.Blocks[height] + if !found { + continue + } + + for _, tx := range block.TxDetails { + 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) + } + } + } + } + + for addr, txs := range addr2txs { + err := d.GetTransactions(addr, rng.Lower, rng.Upper, 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, rng Range) { + parser := h.Chain.GetChainParser() + + for height := rng.Lower; height <= rng.Upper; height++ { + block, found := h.TestData.ConnectBlocks.Blocks[height] + if !found { + continue + } + + 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 != height { + t.Errorf("Tx %s: block height mismatch: %d != %d", tx.Txid, ta.Height, 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) + } + + if taInfo.valOutSat.Cmp(&txInfo.valOutSat) != 0 { + t.Errorf("Tx %s: total output amount mismatch: got %s, want %s", + tx.Txid, taInfo.valOutSat.String(), txInfo.valOutSat.String()) + } + + if len(txInfo.inputs) > 0 { + treshold := "0.0001" + fee := new(big.Int).Sub(&taInfo.valInSat, &taInfo.valOutSat) + if strings.Compare(parser.AmountToDecimalString(fee), treshold) > 0 { + t.Errorf("Tx %s: suspicious amounts: input ∑ [%s] - output ∑ [%s] > %s", + tx.Txid, taInfo.valInSat.String(), taInfo.valOutSat.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, s, err := ta.Inputs[i].Addresses(parser) + if err == nil && s { + for _, a := range addrs { + info.inputs = append(info.inputs, a) + } + } + } + + for i := range ta.Outputs { + info.valOutSat.Add(&info.valOutSat, &ta.Outputs[i].ValueSat) + addrs, s, err := ta.Outputs[i].Addresses(parser) + if err == nil && s { + for _, a := range addrs { + info.outputs = append(info.outputs, a) + } + } + } + + return info, nil +} diff --git a/tests/sync/doc.go b/tests/sync/doc.go new file mode 100644 index 00000000..93a95c82 --- /dev/null +++ b/tests/sync/doc.go @@ -0,0 +1,2 @@ +// Package sync implements integration tests of synchronization code +package sync diff --git a/tests/sync/fakechain.go b/tests/sync/fakechain.go new file mode 100644 index 00000000..6d399d1f --- /dev/null +++ b/tests/sync/fakechain.go @@ -0,0 +1,48 @@ +// +build integration + +package sync + +import "blockbook/bchain" + +type fakeBlockChain struct { + bchain.BlockChain + returnFakes bool + fakeBlocks map[uint32]BlockID + bestHeight uint32 +} + +func (c *fakeBlockChain) GetBestBlockHash() (v string, err error) { + return c.GetBlockHash(c.bestHeight) +} + +func (c *fakeBlockChain) GetBestBlockHeight() (v uint32, err error) { + return c.bestHeight, nil +} + +func (c *fakeBlockChain) GetBlockHash(height uint32) (v string, err error) { + if height > c.bestHeight { + return "", bchain.ErrBlockNotFound + } + if c.returnFakes { + if b, found := c.fakeBlocks[height]; found { + return b.Hash, nil + } + } + return c.BlockChain.GetBlockHash(height) +} + +func (c *fakeBlockChain) GetBlock(hash string, height uint32) (*bchain.Block, error) { + if height > 0 && height > c.bestHeight { + return nil, bchain.ErrBlockNotFound + } + if c.returnFakes { + if hash == "" && height > 0 { + var err error + hash, err = c.GetBlockHash(height) + if err != nil { + return nil, err + } + } + } + return c.BlockChain.GetBlock(hash, height) +} diff --git a/tests/sync/handlefork.go b/tests/sync/handlefork.go new file mode 100644 index 00000000..d9ff3358 --- /dev/null +++ b/tests/sync/handlefork.go @@ -0,0 +1,239 @@ +// +build integration + +package sync + +import ( + "blockbook/bchain" + "blockbook/db" + "fmt" + "math/big" + "os" + "reflect" + "strings" + "testing" +) + +func testHandleFork(t *testing.T, h *TestHandler) { + for _, rng := range h.TestData.HandleFork.SyncRanges { + withRocksDBAndSyncWorker(t, h, rng.Lower, func(d *db.RocksDB, sw *db.SyncWorker, ch chan os.Signal) { + fakeBlocks := getFakeBlocks(h, rng) + chain, err := makeFakeChain(h.Chain, fakeBlocks, rng.Upper) + if err != nil { + t.Fatal(err) + } + + db.SetBlockChain(sw, chain) + + sw.ConnectBlocksParallel(rng.Lower, rng.Upper) + + height, _, err := d.GetBestBlock() + if err != nil { + t.Fatal(err) + } + if height != rng.Upper { + t.Fatalf("Upper block height mismatch: %d != %d", height, rng.Upper) + } + + fakeTxs, err := getTxs(h, d, rng, fakeBlocks) + if err != nil { + t.Fatal(err) + } + fakeAddr2txs := getAddr2TxsMap(fakeTxs) + + verifyTransactions2(t, d, rng, fakeAddr2txs, true) + verifyAddresses2(t, d, h.Chain, fakeBlocks) + + chain.returnFakes = false + + upperHash := fakeBlocks[len(fakeBlocks)-1].Hash + db.HandleFork(sw, rng.Upper, upperHash, func(hash string, height uint32) { + if hash == upperHash { + close(ch) + } + }, true) + + realBlocks := getRealBlocks(h, rng) + realTxs, err := getTxs(h, d, rng, realBlocks) + if err != nil { + t.Fatal(err) + } + realAddr2txs := getAddr2TxsMap(realTxs) + + verifyTransactions2(t, d, rng, fakeAddr2txs, false) + verifyTransactions2(t, d, rng, realAddr2txs, true) + verifyAddresses2(t, d, h.Chain, realBlocks) + }) + } +} + +func verifyAddresses2(t *testing.T, d *db.RocksDB, chain bchain.BlockChain, blks []BlockID) { + parser := chain.GetChainParser() + + for _, b := range blks { + txs, err := getBlockTxs(chain, b.Hash) + if err != nil { + t.Fatal(err) + } + + for _, tx := range txs { + 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 != b.Height { + t.Errorf("Tx %s: block height mismatch: %d != %d", tx.Txid, ta.Height, b.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) + } + + if taInfo.valOutSat.Cmp(&txInfo.valOutSat) != 0 { + t.Errorf("Tx %s: total output amount mismatch: got %s, want %s", + tx.Txid, taInfo.valOutSat.String(), txInfo.valOutSat.String()) + } + + if len(txInfo.inputs) > 0 { + treshold := "0.0001" + fee := new(big.Int).Sub(&taInfo.valInSat, &taInfo.valOutSat) + if strings.Compare(parser.AmountToDecimalString(fee), treshold) > 0 { + t.Errorf("Tx %s: suspicious amounts: input ∑ [%s] - output ∑ [%s] > %s", + tx.Txid, taInfo.valInSat.String(), taInfo.valOutSat.String(), treshold) + } + } + } + } +} + +func verifyTransactions2(t *testing.T, d *db.RocksDB, rng Range, addr2txs map[string][]string, exist bool) { + noErrs := 0 + for addr, txs := range addr2txs { + checkMap := make(map[string]bool, len(txs)) + for _, txid := range txs { + checkMap[txid] = false + } + + err := d.GetTransactions(addr, rng.Lower, rng.Upper, func(txid string, vout uint32, isOutput bool) error { + if isOutput { + checkMap[txid] = true + } + return nil + }) + if err != nil { + t.Fatal(err) + } + + for _, txid := range txs { + if checkMap[txid] != exist { + auxverb := "wasn't" + if !exist { + auxverb = "was" + } + t.Errorf("%s: transaction %s %s found [expected = %t]", addr, txid, auxverb, exist) + noErrs++ + if noErrs >= 10 { + t.Fatal("Too many errors") + } + } + } + } +} + +func getFakeBlocks(h *TestHandler, rng Range) []BlockID { + blks := make([]BlockID, 0, rng.Upper-rng.Lower+1) + for i := rng.Lower; i <= rng.Upper; i++ { + if b, found := h.TestData.HandleFork.FakeBlocks[i]; found { + blks = append(blks, b) + } + } + return blks +} + +func getRealBlocks(h *TestHandler, rng Range) []BlockID { + blks := make([]BlockID, 0, rng.Upper-rng.Lower+1) + for _, b := range h.TestData.HandleFork.RealBlocks { + if b.Height >= rng.Lower && b.Height <= rng.Upper { + blks = append(blks, b) + } + } + return blks +} + +func makeFakeChain(chain bchain.BlockChain, blks []BlockID, upper uint32) (*fakeBlockChain, error) { + if blks[len(blks)-1].Height != upper { + return nil, fmt.Errorf("Range must end with fake block in order to emulate fork [%d != %d]", blks[len(blks)-1].Height, upper) + } + mBlks := make(map[uint32]BlockID, len(blks)) + for i := range blks { + mBlks[blks[i].Height] = blks[i] + } + return &fakeBlockChain{ + BlockChain: chain, + returnFakes: true, + fakeBlocks: mBlks, + bestHeight: upper, + }, nil +} + +func getTxs(h *TestHandler, d *db.RocksDB, rng Range, blks []BlockID) ([]bchain.Tx, error) { + res := make([]bchain.Tx, 0, (rng.Upper-rng.Lower+1)*2000) + + for _, b := range blks { + bi, err := d.GetBlockInfo(b.Height) + if err != nil { + return nil, err + } + if bi.Hash != b.Hash { + return nil, fmt.Errorf("Block hash mismatch: %s != %s", bi.Hash, b.Hash) + } + + txs, err := getBlockTxs(h.Chain, b.Hash) + if err != nil { + return nil, err + } + res = append(res, txs...) + } + + return res, nil +} + +func getBlockTxs(chain bchain.BlockChain, hash string) ([]bchain.Tx, error) { + b, err := chain.GetBlock(hash, 0) + if err != nil { + return nil, fmt.Errorf("GetBlock: %s", err) + } + parser := chain.GetChainParser() + for i := range b.Txs { + err := setTxAddresses(parser, &b.Txs[i]) + if err != nil { + return nil, fmt.Errorf("setTxAddresses [%s]: %s", b.Txs[i].Txid, err) + } + } + return b.Txs, nil +} + +func getAddr2TxsMap(txs []bchain.Tx) map[string][]string { + addr2txs := make(map[string][]string) + for i := range txs { + for j := range txs[i].Vout { + for k := range txs[i].Vout[j].ScriptPubKey.Addresses { + addr := txs[i].Vout[j].ScriptPubKey.Addresses[k] + txid := txs[i].Txid + addr2txs[addr] = append(addr2txs[addr], txid) + } + } + } + return addr2txs +} diff --git a/tests/sync/sync.go b/tests/sync/sync.go index 8d60c993..6c01bfc0 100644 --- a/tests/sync/sync.go +++ b/tests/sync/sync.go @@ -1,3 +1,5 @@ +// +build integration + package sync import ( @@ -7,18 +9,15 @@ import ( "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, + "ConnectBlocks": testConnectBlocks, "ConnectBlocksParallel": testConnectBlocksParallel, - // "DisconnectBlocks": nil, + "HandleFork": testHandleFork, } type TestHandler struct { @@ -27,17 +26,30 @@ type TestHandler struct { TestData *TestData } +type Range struct { + Lower uint32 `json:"lower"` + Upper uint32 `json:"upper"` +} + type TestData struct { - ConnectBlocksParallel struct { - SyncWorkers int `json:"syncWorkers"` - SyncChunk int `json:"syncChunk"` - } `json:"connectBlocksParallel"` - Blocks []BlockInfo `json:"blocks"` + ConnectBlocks struct { + SyncRanges []Range `json:"syncRanges"` + Blocks map[uint32]BlockInfo `json:"blocks"` + } `json:"connectBlocks"` + HandleFork struct { + SyncRanges []Range `json:"syncRanges"` + FakeBlocks map[uint32]BlockID `json:"fakeBlocks"` + RealBlocks map[uint32]BlockID `json:"realBlocks"` + } `json:"handleFork"` +} + +type BlockID struct { + Height uint32 `json:"height"` + Hash string `json:"hash"` } type BlockInfo struct { - Height uint32 `json:"height"` - Hash string `json:"hash"` + BlockID NoTxs uint32 `json:"noTxs"` TxDetails []*bchain.Tx `json:"txDetails"` } @@ -54,10 +66,9 @@ func IntegrationTest(t *testing.T, coin string, chain bchain.BlockChain, testCon 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 { + h := TestHandler{Coin: coin, Chain: chain, TestData: td} t.Run(test, func(t *testing.T) { f(t, &h) }) } else { t.Errorf("%s: test not found", test) @@ -90,7 +101,7 @@ func loadTestData(coin string, parser bchain.BlockChainParser) (*TestData, error return nil, err } - for _, b := range v.Blocks { + for _, b := range v.ConnectBlocks.Blocks { for _, tx := range b.TxDetails { // convert amounts in test json to bit.Int and clear the temporary JsonValue for i := range tx.Vout { @@ -110,26 +121,21 @@ func loadTestData(coin string, parser bchain.BlockChainParser) (*TestData, error } } - 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 - } + for i := range tx.Vout { + ad, err := parser.GetAddrDescFromVout(&tx.Vout[i]) + if err != nil { + return err + } + a, s, err := parser.GetAddressesFromAddrDesc(ad) + if err == nil && s { + tx.Vout[i].ScriptPubKey.Addresses = a } } - return err + return nil } func makeRocksDB(parser bchain.BlockChainParser, m *common.Metrics, is *common.InternalState) (*db.RocksDB, func(), error) { @@ -153,8 +159,23 @@ func makeRocksDB(parser bchain.BlockChainParser, m *common.Metrics, is *common.I 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) +var metricsRegistry = map[string]*common.Metrics{} + +func getMetrics(name string) (*common.Metrics, error) { + if m, found := metricsRegistry[name]; found { + return m, nil + } else { + m, err := common.GetMetrics(name) + if err != nil { + return nil, err + } + metricsRegistry[name] = m + return m, nil + } +} + +func withRocksDBAndSyncWorker(t *testing.T, h *TestHandler, startHeight uint32, fn func(*db.RocksDB, *db.SyncWorker, chan os.Signal)) { + m, err := getMetrics(h.Coin) if err != nil { t.Fatal(err) } @@ -166,239 +187,12 @@ func withRocksDBAndSyncWorker(t *testing.T, h *TestHandler, fn func(*db.RocksDB, } 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) + sw, err := db.NewSyncWorker(d, h.Chain, 8, 0, int(startHeight), 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 -} diff --git a/tests/sync/testdata/bitcoin.json b/tests/sync/testdata/bitcoin.json index e4ca6710..006f317d 100644 --- a/tests/sync/testdata/bitcoin.json +++ b/tests/sync/testdata/bitcoin.json @@ -1,310 +1,459 @@ { - "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" + "connectBlocks": { + "syncRanges": [ + {"lower": 541224, "upper": 541255}, + {"lower": 542835, "upper": 542838} + ], + "blocks": { + "541224": { + "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" + ], + "vout": [ + { + "value": 7.51411168, + "n": 0, + "scriptPubKey": { + "hex": "a91449c60d71775a5768a139d83e01665602a7319e5987" + } }, - "addresses": ["1PE9dMki67qUYnX5J8APFTHvUJVWxEUoQP"] - } - ], - "vout": [ - { - "value": 0.29998854, - "n": 0, - "scriptPubKey": { - "hex": "76a914059272d492ba10fab46e2fe0154ae9200027974d88ac" + { + "value": 0.01000000, + "n": 1, + "scriptPubKey": { + "hex": "76a91483b47502199d2599b7839ee96011b4340c5554d688ac" + } } - }, - { - "value": 0.70000000, - "n": 1, - "scriptPubKey": { - "hex": "76a9145c02a3783346f0a8cd4c5918c625ab13c5be698e88ac" + ] + }, + { + "hex": "02000000000101565ce4441a3eab3b3916a51b221dfba44daa9d72777c658165b9ab2baadfbf040000000017160014ef684f89a6656212f5659078c1111523be97f72bfeffffff0240420f00000000001976a914a885121ba438d8145a402e12b91ea1a43a8ba54b88acad5cba2c0000000017a91447f4c3bd4ecb0bd7f4d3489bf640352cab6fb6c58702483045022100f31cb0e57281dd732b20e468cd40dbd018b03631f0c0760565d1349ab46e5e29022046f754795d3a191a7731b470d5b6c5ba9f515c54d354a81e9c265fdfdcfbfd3e012103a596a6b5a398bf1d40778104d56a6317cc65981b6c28082989a4cbf74691d6f722420800", + "txid": "b3f61e880609417a18a18a9e089e706822edd230e0a28316a30595f41eafc45f", + "blocktime": 1536829299, + "time": 1536829299, + "version": 2, + "vin": [ + { + "txid": "04bfdfaa2babb96581657c77729daa4da4fb1d221ba516393bab3e1a44e45c56", + "vout": 0, + "sequence": 4294967294, + "scriptSig": { + "hex": "160014ef684f89a6656212f5659078c1111523be97f72b" + } } - } - ] - } - ] - }, - { - "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.01000000, + "n": 0, + "scriptPubKey": { + "hex": "76a914a885121ba438d8145a402e12b91ea1a43a8ba54b88ac" + } + }, + { + "value": 7.50410925, + "n": 1, + "scriptPubKey": { + "hex": "a91447f4c3bd4ecb0bd7f4d3489bf640352cab6fb6c587" + } } - } - ], - "vout": [ - { - "value": 0.01179517, - "n": 0, - "scriptPubKey": { - "hex": "a91401dfd360c8b640f9da5ede7170dc369d84c3dfcf87" + ] + } + ] + }, + "541225": { + "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" + } } - }, - { - "value": 0.00210000, - "n": 1, - "scriptPubkey": { - "hex": "a91454f7c31d5a1aac2f6b3d6b4773f62f333e2e8af887" + ], + "vout": [ + { + "value": 0.01387900, + "n": 0, + "scriptPubKey": { + "hex": "a91473829585e38acb264c21ab7e1d8d4814337c4ba287" + } + }, + { + "value": 0.00026697, + "n": 1, + "scriptPubKey": { + "hex": "a91450be45be5aa3ee42827c1eb72370fde4665acfaa87" + } } - } - ] - } - ] - }, - { - "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" + ] + } + ] + }, + "541226": { + "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" + } } - }, - { - "txid": "9192dcb6bd2751103c6fbcc5378ff7e6522bd9f983de30daa7b31c1a209d8017", - "vout": 0, - "sequence": 4294967295, - "scriptSig": { - "hex": "473044022053e8788b622f6ed5ed69bffb6f2c568d42d1bb398eb6c8b9d7d011e1280c261a0220243a3346883ed0acd7a291140ddc520d43100f60e68fd4a242ec70bae95c097e0121030e2844042326a3dad34937b2a577977b6d13d87a50812f351735fe1ebd999fb7" + ], + "vout": [ + { + "value": 0.00193000, + "n": 0, + "scriptPubKey": { + "hex": "76a914b18cf542d661fc17a04a29f91b03056e3d7f2aaa88ac" + } + }, + { + "value": 0.09806673, + "n": 1, + "scriptPubkey": { + "hex": "76a914194e18702f84b5c87f307662887ca7ec18482bc488ac" + } } - } - ], - "vout": [ - { - "value": 0.15218954, - "n": 0, - "scriptPubKey": { - "hex": "76a914a1d8d0f5edd2bf6808d7156f2884096fd86359fe88ac" + ] + } + ] + }, + "541227": { + "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"] } - }, - { - "value": 1.00000000, - "n": 1, - "scriptPubkey": { - "hex": "76a914492f305f58b7469202526bff3200a9f2a446fb4988ac" + ], + "vout": [ + { + "value": 0.29998854, + "n": 0, + "scriptPubKey": { + "hex": "76a914059272d492ba10fab46e2fe0154ae9200027974d88ac" + } + }, + { + "value": 0.70000000, + "n": 1, + "scriptPubKey": { + "hex": "76a9145c02a3783346f0a8cd4c5918c625ab13c5be698e88ac" + } } - } - ] - } - ] + ] + } + ] + }, + "541228": { + "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" + } + } + ] + } + ] + }, + "541255": { + "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" + } + } + ] + } + ] + }, + "542838": { + "height": 542838, + "hash": "00000000000000000001e973be0770d428d3494d4e1b661aafcd8924c892648d", + "noTxs": 31, + "txDetails": [ + { + "hex": "0100000007763056a30225a7f9f1af67e74a7ec67b514bcf094f8790da301b931ad2c8110e000000006b48304502210095e355273d421b93123786510757e10301508de7c4d9fb0c10900c43b6d6488a02206ddcd52d5e3e09083b50b8e020e2ce3f520e45b7be748e2cf79f1dafc44e54380121037d662ed0eab74137653a05001ada5b16c9b13ee4c542666d762564ebf68e3c8bffffffffbd4d49ec91d404facffca906661a1f437ba2e01f7004a769f01e3841d753e238000000006b4830450221008c92a4c432f4b822d234ee817f544673a86263a2eb106d1fd4189f0e8aeb4f64022031ecf397a0a15eeb41ed636d2729e422f57cb8269a74ddf7696df85055d35c5b012102940f4b3a9737f208c8cb1fe5ae8b317d06d0ca6e63dca5a04f735eb7d42377e4ffffffff3e851c0f9f70cb40a2492d811d87a899f6e29a1299f93dc7985e39e2db782559000000006a47304402202f8942571366051dc870be96e602e9415996e7583ecb270d7102b51453c50bb9022021c529a26ea348377863f59b03321279d4775cce1877fb37e3d09eb24b39e45b012102940f4b3a9737f208c8cb1fe5ae8b317d06d0ca6e63dca5a04f735eb7d42377e4ffffffffc9f4fc77c61640d945f14cbf9c7ecc2a89421ed03c3c593858915c995a009273000000006b483045022100e83039eb8d55b21f90a0fbae004bb3b0f0681b7e7a4f1bfceadbe5b470cb483702203be7ce3567967a2a1cdaeb3bd47d57cffd645557ab504d1056c3294c08ec08a6012103f3422741129eb0bfb82ca77f3818371749de4c38eb5f8def51a0abfb81cc1957ffffffff395717f7dfdec0df3e2117966af1e43eca5515f7469618d403a1383b5e4bca8a020000006a4730440220071c6128864f4ee9437b27a9ccdec52961f6a481c5ba01ae993a20a1d9afdfa20220248a5209af572f0af3c922e96def6c8b1967e355a8dfeaa891e69f59c9a37e96012102940f4b3a9737f208c8cb1fe5ae8b317d06d0ca6e63dca5a04f735eb7d42377e4ffffffff2e9ea71c6811838ec46444f08fad8298f8fa83cb1190ccaf98950963f0e68199000000006b483045022100d84f351d070103e614487ef7e8a7f5a6d60b6c51566aa1262111f8be1cad47d702203e0f36fec153718d3ba3a1c7dd8dd7f28691f4673498680d7f094b83db357aa4012103c71e9c61b2092b415b8ae4d5107786770cff927196efd12e39ed55477baefcdeffffffff39e161ac068370063eddd3ede85c9301f23e088f1ee72ac8e12603d7e0e8d6d6000000006a473044022053eb77ad34f47bcf00fdc845119d673948e2c6c0d21e6ec82d903598b32194df0220379127f024b85b964b52d3a7f501d7dad0b77321675d5d1ba6f0dc8f09305837012102940f4b3a9737f208c8cb1fe5ae8b317d06d0ca6e63dca5a04f735eb7d42377e4ffffffff02bb430000000000001976a9147270edc07db25cd4a7b99e54fd3c0c76076ba02688ac00e1f5050000000017a9141458b33294f0791f682c0d181ea757730faeeca88700000000", + "txid": "17da91b3b0590ee84b51174a76c0a61126d2ce82830230371cae549e0c070cd0", + "blocktime": 1537791290, + "time": 1537791290, + "locktime": 0, + "version": 1, + "vin": [ + { + "txid": "48304502210095e355273d421b93123786510757e10301508de7c4d9fb0c10900c43b6d6488a02206ddcd52d5e3e09083b50b8e020e2ce3f520e45b7be748e2cf79f1dafc44e54380121037d662ed0eab74137653a05001ada5b16c9b13ee4c542666d762564ebf68e3c8b", + "vout": 0, + "sequence": 4294967295, + "scriptSig": { + "hex": "48304502210095e355273d421b93123786510757e10301508de7c4d9fb0c10900c43b6d6488a02206ddcd52d5e3e09083b50b8e020e2ce3f520e45b7be748e2cf79f1dafc44e54380121037d662ed0eab74137653a05001ada5b16c9b13ee4c542666d762564ebf68e3c8b" + } + }, + { + "txid": "38e253d741381ef069a704701fe0a27b431f1a6606a9fccffa04d491ec494dbd", + "vout": 0, + "sequence": 4294967295, + "scriptSig": { + "hex": "4830450221008c92a4c432f4b822d234ee817f544673a86263a2eb106d1fd4189f0e8aeb4f64022031ecf397a0a15eeb41ed636d2729e422f57cb8269a74ddf7696df85055d35c5b012102940f4b3a9737f208c8cb1fe5ae8b317d06d0ca6e63dca5a04f735eb7d42377e4" + } + }, + { + "txid": "592578dbe2395e98c73df999129ae2f699a8871d812d49a240cb709f0f1c853e", + "vout": 0, + "sequence": 4294967295, + "scriptSig": { + "hex": "47304402202f8942571366051dc870be96e602e9415996e7583ecb270d7102b51453c50bb9022021c529a26ea348377863f59b03321279d4775cce1877fb37e3d09eb24b39e45b012102940f4b3a9737f208c8cb1fe5ae8b317d06d0ca6e63dca5a04f735eb7d42377e4" + } + }, + { + "txid": "7392005a995c915838593c3cd01e42892acc7e9cbf4cf145d94016c677fcf4c9", + "vout": 0, + "sequence": 4294967295, + "scriptSig": { + "hex": "483045022100e83039eb8d55b21f90a0fbae004bb3b0f0681b7e7a4f1bfceadbe5b470cb483702203be7ce3567967a2a1cdaeb3bd47d57cffd645557ab504d1056c3294c08ec08a6012103f3422741129eb0bfb82ca77f3818371749de4c38eb5f8def51a0abfb81cc1957" + } + }, + { + "txid": "8aca4b5e3b38a103d4189646f71555ca3ee4f16a9617213edfc0dedff7175739", + "vout": 2, + "sequence": 4294967295, + "scriptSig": { + "hex": "4730440220071c6128864f4ee9437b27a9ccdec52961f6a481c5ba01ae993a20a1d9afdfa20220248a5209af572f0af3c922e96def6c8b1967e355a8dfeaa891e69f59c9a37e96012102940f4b3a9737f208c8cb1fe5ae8b317d06d0ca6e63dca5a04f735eb7d42377e4" + } + }, + { + "txid": "9981e6f063099598afcc9011cb83faf89882ad8ff04464c48e8311681ca79e2e", + "vout": 0, + "sequence": 4294967295, + "scriptSig": { + "hex": "483045022100d84f351d070103e614487ef7e8a7f5a6d60b6c51566aa1262111f8be1cad47d702203e0f36fec153718d3ba3a1c7dd8dd7f28691f4673498680d7f094b83db357aa4012103c71e9c61b2092b415b8ae4d5107786770cff927196efd12e39ed55477baefcde" + } + }, + { + "txid": "d6d6e8e0d70326e1c82ae71e8f083ef201935ce8edd3dd3e06708306ac61e139", + "vout": 0, + "sequence": 4294967295, + "scriptSig": { + "hex": "473044022053eb77ad34f47bcf00fdc845119d673948e2c6c0d21e6ec82d903598b32194df0220379127f024b85b964b52d3a7f501d7dad0b77321675d5d1ba6f0dc8f09305837012102940f4b3a9737f208c8cb1fe5ae8b317d06d0ca6e63dca5a04f735eb7d42377e4" + } + } + ], + "vout": [ + { + "value": 0.00017339, + "n": 0, + "scriptPubKey": { + "hex": "76a9147270edc07db25cd4a7b99e54fd3c0c76076ba02688ac" + } + }, + { + "value": 1.00000000, + "n": 1, + "scriptPubKey": { + "hex": "a9141458b33294f0791f682c0d181ea757730faeeca887" + } + } + ] + } + ] + } } - ] + }, + "handleFork": { + "syncRanges": [ + {"lower": 541224, "upper": 541255}, + {"lower": 542835, "upper": 542838} + ], + "fakeBlocks": { + "541253": { + "height": 541253, + "hash": "0000000000000000001af0987bd9f319c5a8105537b3deb54c423b299b133bb6" + }, + "541254": { + "height": 541254, + "hash": "00000000000000000011eebe554e91cf26ba7897195b42997b03ec4f929b27d2" + }, + "541255": { + "height": 541255, + "hash": "0000000000000000000feb2cbefe55cb4bb8cf045fac7ed4b4cc09e8f645a64c" + }, + "542836": { + "height": 542836, + "hash": "000000000000000000218866194a2bc15b92d7dccca1d328a2fa6a9c0befb039" + }, + "542837": { + "height": 542837, + "hash": "00000000000000000023936c5189062f9f9b05895d778c202e2f83f0d119a370" + }, + "542838": { + "height": 542838, + "hash": "000000000000000000155f1289c445127d0cfc360b8cc9f2c46c5850de607307" + } + }, + "realBlocks": { + "541253": { + "height": 541253, + "hash": "000000000000000000045d55641e32a86ff313cd9d8a557d0fe9d0ab4e7eae7f" + }, + "541254": { + "height": 541254, + "hash": "0000000000000000000e08972d3e7e26f30c58313dcd15ce5817b09aef0e69b7" + }, + "541255": { + "height": 541255, + "hash": "0000000000000000001375c3e88b4e53b687a70aba4b645ba81c93a4539d724d" + }, + "542836": { + "height": 542836, + "hash": "0000000000000000000927c61a2a3174c64f2beee103ead0f62b30354f323456" + }, + "542837": { + "height": 542837, + "hash": "000000000000000000158353b8c9865c2261dc96bd780ef7d0017b2c2798c5c7" + }, + "542838": { + "height": 542838, + "hash": "00000000000000000001e973be0770d428d3494d4e1b661aafcd8924c892648d" + } + } + } } diff --git a/tests/sync/testdata/bitcoin_testnet.json b/tests/sync/testdata/bitcoin_testnet.json new file mode 100644 index 00000000..374f4afb --- /dev/null +++ b/tests/sync/testdata/bitcoin_testnet.json @@ -0,0 +1,211 @@ +{ + "connectBlocks": { + "syncRanges": [ + {"lower": 1414976, "upper": 1414996} + ], + "blocks": { + "1414996": { + "height": 1414996, + "hash": "00000000c55bb26fac57344bcb4a2c26579ca009a0e39ab25d9dacccbc0f6dc5", + "noTxs": 61, + "txDetails": [ + { + "hex": "02000000012735ecfd2c0f7948e004b8084fc75b4c37026d0b287d9d4766ab445b5938ca00000000006b483045022100de880217ae17daeff18b9533fbacd4090b8440f61a967c6fed0226c33d609b0b02201f4c01e967e6242c3e93a5da63c7a882b7fa97f4b389b4df96453edce7c051760121035610f74fe29e7065cf5ec6ab97d145fd4de788512c3b73a93f4177585c0f085affffffff0280969800000000001976a9141689469a15bff8d874f11e3b5355737d9e6dbbc488ac5c384703000000001976a914a13dbce34513db0f24c7d533b27a6ae257e4652d88ac00000000", + "txid": "481386544e3bdfa57d0b868cb680b7bd1b5ae41d01bb8be4e298cad1577ac208", + "time": 1538659297, + "blocktime": 1538659297, + "version": 2, + "vin": [ + { + "txid": "00ca38595b44ab66479d7d280b6d02374c5bc74f08b804e048790f2cfdec3527", + "vout": 0, + "scriptSig": { + "hex": "483045022100de880217ae17daeff18b9533fbacd4090b8440f61a967c6fed0226c33d609b0b02201f4c01e967e6242c3e93a5da63c7a882b7fa97f4b389b4df96453edce7c051760121035610f74fe29e7065cf5ec6ab97d145fd4de788512c3b73a93f4177585c0f085a" + }, + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.1, + "n": 0, + "scriptPubKey": { + "hex": "76a9141689469a15bff8d874f11e3b5355737d9e6dbbc488ac" + } + }, + { + "value": 0.54999132, + "n": 1, + "scriptPubKey": { + "hex": "76a914a13dbce34513db0f24c7d533b27a6ae257e4652d88ac" + } + } + ] + }, + { + "hex": "01000000023198e0c324fc3b0361d8a54069de19879b56d728d4d8d0fa96fce4445b7e2045000000006b483045022100a7d66715aae07e3086d6c13210c2b0faf49d9068c2cdb12ff8cf65884abc833c0220116c502fe699053b43b520da08deeef78d8a86b33c6e9866856a659ae8142f0d01210376f1c7f3ef21133651d7df9dbb37602beb82a20f3791f4518d38dccae9d6f165feffffff2b0e1fc2ca525eccf44b868d18189fb853a54e4646626051a93e9819809a90c8000000006a4730440220508f022bd2f7f016535309eba6d8906297e32fd678d9f8d4d1ae2e3ad026734e0220434239e26b3e60f29e25d46fb8c5d00c8e9ab3fb2c50490c667d50d8d8abdc520121022a9bb7d67b6087ffdc8e071a427ed29fb5569f5fdeab981fc4cb321c0c135f59feffffff0210270000000000001976a9145731d95537f4ce729a141690bccea71fecf533bb88ac5e580f00000000001976a914e9ad64791d16f8317020e53a31c992b832094d3088ac53971500", + "txid": "bdcb85ad80693488efcb29f747040bc78ce3d3ba3dd298a4b1e2f9a55d15e6e8", + "time": 1538659297, + "blocktime": 1538659297, + "version": 1, + "vin": [ + { + "txid": "45207e5b44e4fc96fad0d8d428d7569b8719de6940a5d861033bfc24c3e09831", + "vout": 0, + "scriptSig": { + "hex": "483045022100a7d66715aae07e3086d6c13210c2b0faf49d9068c2cdb12ff8cf65884abc833c0220116c502fe699053b43b520da08deeef78d8a86b33c6e9866856a659ae8142f0d01210376f1c7f3ef21133651d7df9dbb37602beb82a20f3791f4518d38dccae9d6f165" + }, + "sequence": 4294967294 + }, + { + "txid": "c8909a8019983ea951606246464ea553b89f18188d864bf4cc5e52cac21f0e2b", + "vout": 0, + "scriptSig": { + "hex": "4730440220508f022bd2f7f016535309eba6d8906297e32fd678d9f8d4d1ae2e3ad026734e0220434239e26b3e60f29e25d46fb8c5d00c8e9ab3fb2c50490c667d50d8d8abdc520121022a9bb7d67b6087ffdc8e071a427ed29fb5569f5fdeab981fc4cb321c0c135f59" + }, + "sequence": 4294967294 + } + ], + "vout": [ + { + "value": 0.0001, + "n": 0, + "scriptPubKey": { + "hex": "76a9145731d95537f4ce729a141690bccea71fecf533bb88ac" + } + }, + { + "value": 0.01005662, + "n": 1, + "scriptPubKey": { + "hex": "76a914e9ad64791d16f8317020e53a31c992b832094d3088ac" + } + } + ] + } + ] + }, + "1414983": { + "height": 1414983, + "hash": "00000000128873524843cec7a4a5718736f7b3c80d6936414e6b5b01b25be443", + "noTxs": 53, + "txDetails": [ + { + "txid": "a485c102d173cd577d6c9d2594c3584e45def4290f8832a6a47817862dba7799", + "version": 1, + "vin": [ + { + "txid": "d60199408b33442d7b16de2dcf2538a815575774150e485f58a573a4b51c13e0", + "vout": 1, + "scriptSig": { + "hex": "483045022100c544080e0f8a02d4f6d76d343da538d4ee348d716f4a1984e440d063ac4230f3022044e987da4e6baf5505876a269a090b3f37a19de8c09f3a4ad0bff4fdaff604e60121025fcc6fa8cea73b326f50d8f4b6aa05a47bc9672b1ba4cb5427d013df9d0c6141" + }, + "sequence": 4294967293 + } + ], + "vout": [ + { + "value": 0.01, + "n": 0, + "scriptPubKey": { + "hex": "76a91472cbe0f81ef95f9a628c81a5c640d70074d3affb88ac" + } + }, + { + "value": 0.09962844, + "n": 1, + "scriptPubKey": { + "hex": "76a91457753cae491018bb85ef2a2b6608720c968a291e88ac" + } + } + ], + "hex": "0100000001e0131cb5a473a5585f480e1574575715a83825cf2dde167b2d44338b409901d6010000006b483045022100c544080e0f8a02d4f6d76d343da538d4ee348d716f4a1984e440d063ac4230f3022044e987da4e6baf5505876a269a090b3f37a19de8c09f3a4ad0bff4fdaff604e60121025fcc6fa8cea73b326f50d8f4b6aa05a47bc9672b1ba4cb5427d013df9d0c6141fdffffff0240420f00000000001976a91472cbe0f81ef95f9a628c81a5c640d70074d3affb88ac5c059800000000001976a91457753cae491018bb85ef2a2b6608720c968a291e88ac46971500", + "time": 1538643603, + "blocktime": 1538643603 + }, + { + "txid": "06d5505f7e78c9979fdcf9507a88a6d386318a752ac5f49d4aa02eeb70bcf873", + "version": 1, + "vin": [ + { + "txid": "5624a2579c3bf48f50008c6d8e5489af61a6d57b97aa802d7bc0d07d1d94bd9a", + "vout": 2, + "scriptSig": { + "hex": "4830450221009a12c53cd7264c2658035a6492c517b6719bd8908e8271ed0c270a44bcda8a260220336351e99c8fdc2a3b752a0e8f1c260ac9e0ac3eb2a5f0d09d347badf33dc7fc012103c043bdba1042b9abafed3bd48ebe43b9a2f0b5f30a34246551130933aa8c2cd1" + }, + "sequence": 4294967295 + }, + { + "txid": "0bb54703799d5346d19caf320a6363b42444d6741b1a322072033bff7f74f763", + "vout": 0, + "scriptSig": { + "hex": "473044022005753709e182bb4cc5770c54b483907e9fa2658fc4dfac774121b4d7b31da340022033ac23bce026c730306a293f8c6de5cd4c557e5cc3e7b70224e1ace1cae64268012102a1f29b6570e5001e526f7bf296ab607539722726f672e7f07c59189cc49afdc8" + }, + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 1.233122, + "n": 0, + "scriptPubKey": { + "hex": "76a914a617880fd0e8118f06e8db07ad09f3e381205aee88ac" + } + }, + { + "value": 0, + "n": 1, + "scriptPubKey": { + "hex": "6a146f6d6e6900000000000000010000000000019577" + } + }, + { + "value": 0.00000546, + "n": 2, + "scriptPubKey": { + "hex": "76a914277d14e23c46f50b43ae63fe6ba420f4d1568fb788ac" + } + } + ], + "hex": "01000000029abd941d7dd0c07b2d80aa977bd5a661af89548e6d8c00508ff43b9c57a22456020000006b4830450221009a12c53cd7264c2658035a6492c517b6719bd8908e8271ed0c270a44bcda8a260220336351e99c8fdc2a3b752a0e8f1c260ac9e0ac3eb2a5f0d09d347badf33dc7fc012103c043bdba1042b9abafed3bd48ebe43b9a2f0b5f30a34246551130933aa8c2cd1ffffffff63f7747fff3b037220321a1b74d64424b463630a32af9cd146539d790347b50b000000006a473044022005753709e182bb4cc5770c54b483907e9fa2658fc4dfac774121b4d7b31da340022033ac23bce026c730306a293f8c6de5cd4c557e5cc3e7b70224e1ace1cae64268012102a1f29b6570e5001e526f7bf296ab607539722726f672e7f07c59189cc49afdc8ffffffff0348985907000000001976a914a617880fd0e8118f06e8db07ad09f3e381205aee88ac0000000000000000166a146f6d6e690000000000000001000000000001957722020000000000001976a914277d14e23c46f50b43ae63fe6ba420f4d1568fb788ac00000000", + "time": 1538643603, + "blocktime": 1538643603 + } + ] + } + } + }, + "handleFork": { + "syncRanges": [ + {"lower": 1414526, "upper": 1414546} + ], + "fakeBlocks": { + "1414544": { + "height": 1414544, + "hash": "00000000aa4bbe624efc4cc0be98f1d49b03abca094a9785ad1e50c20e7634b1" + }, + "1414545": { + "height": 1414545, + "hash": "0000000000035fd65c471e0645190d5392de4dc626358bc001c753ade3b8a44c" + }, + "1414546": { + "height": 1414546, + "hash": "000000000003b4360cebb2c29d7798c22ae31b8ba83941ebed0c61fb56beb765" + } + }, + "realBlocks": { + "1414544": { + "height": 1414544, + "hash": "0000000000012b55c84faf61e201a1c64c6a57e8be3d2b5e11868c39cab5027e" + }, + "1414545": { + "height": 1414545, + "hash": "0000000017020556bdeac9d3bbd2361d11aba79ac39f8a161170ac22d225856d" + }, + "1414546": { + "height": 1414546, + "hash": "0000000000000000b169d17aee62e9f5758a38c7bead2676fc4e4a22a51f17b9" + } + } + } +} diff --git a/tests/tests.json b/tests/tests.json index e323ef71..eab5cd04 100644 --- a/tests/tests.json +++ b/tests/tests.json @@ -9,11 +9,13 @@ }, "bitcoin": { "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", - "EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"] + "EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"], + "sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"] }, "bitcoin_testnet": { "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", - "EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"] + "EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"], + "sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"] }, "dash": { "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",