github.com/aychain/blockbook@v0.1.1-0.20181121092459-6d1fc7e07c5b/tests/sync/handlefork.go (about)

     1  // +build integration
     2  
     3  package sync
     4  
     5  import (
     6  	"blockbook/bchain"
     7  	"blockbook/db"
     8  	"fmt"
     9  	"math/big"
    10  	"os"
    11  	"reflect"
    12  	"strings"
    13  	"testing"
    14  )
    15  
    16  func testHandleFork(t *testing.T, h *TestHandler) {
    17  	for _, rng := range h.TestData.HandleFork.SyncRanges {
    18  		withRocksDBAndSyncWorker(t, h, rng.Lower, func(d *db.RocksDB, sw *db.SyncWorker, ch chan os.Signal) {
    19  			fakeBlocks := getFakeBlocks(h, rng)
    20  			chain, err := makeFakeChain(h.Chain, fakeBlocks, rng.Upper)
    21  			if err != nil {
    22  				t.Fatal(err)
    23  			}
    24  
    25  			db.SetBlockChain(sw, chain)
    26  
    27  			sw.ConnectBlocksParallel(rng.Lower, rng.Upper)
    28  
    29  			height, _, err := d.GetBestBlock()
    30  			if err != nil {
    31  				t.Fatal(err)
    32  			}
    33  			if height != rng.Upper {
    34  				t.Fatalf("Upper block height mismatch: %d != %d", height, rng.Upper)
    35  			}
    36  
    37  			fakeTxs, err := getTxs(h, d, rng, fakeBlocks)
    38  			if err != nil {
    39  				t.Fatal(err)
    40  			}
    41  			fakeAddr2txs := getAddr2TxsMap(fakeTxs)
    42  
    43  			verifyTransactions2(t, d, rng, fakeAddr2txs, true)
    44  			verifyAddresses2(t, d, h.Chain, fakeBlocks)
    45  
    46  			chain.returnFakes = false
    47  
    48  			upperHash := fakeBlocks[len(fakeBlocks)-1].Hash
    49  			db.HandleFork(sw, rng.Upper, upperHash, func(hash string, height uint32) {
    50  				if hash == upperHash {
    51  					close(ch)
    52  				}
    53  			}, true)
    54  
    55  			realBlocks := getRealBlocks(h, rng)
    56  			realTxs, err := getTxs(h, d, rng, realBlocks)
    57  			if err != nil {
    58  				t.Fatal(err)
    59  			}
    60  			realAddr2txs := getAddr2TxsMap(realTxs)
    61  
    62  			verifyTransactions2(t, d, rng, fakeAddr2txs, false)
    63  			verifyTransactions2(t, d, rng, realAddr2txs, true)
    64  			verifyAddresses2(t, d, h.Chain, realBlocks)
    65  		})
    66  	}
    67  }
    68  
    69  func verifyAddresses2(t *testing.T, d *db.RocksDB, chain bchain.BlockChain, blks []BlockID) {
    70  	parser := chain.GetChainParser()
    71  
    72  	for _, b := range blks {
    73  		txs, err := getBlockTxs(chain, b.Hash)
    74  		if err != nil {
    75  			t.Fatal(err)
    76  		}
    77  
    78  		for _, tx := range txs {
    79  			ta, err := d.GetTxAddresses(tx.Txid)
    80  			if err != nil {
    81  				t.Fatal(err)
    82  			}
    83  			if ta == nil {
    84  				t.Errorf("Tx %s: not found in TxAddresses", tx.Txid)
    85  				continue
    86  			}
    87  
    88  			txInfo := getTxInfo(&tx)
    89  			taInfo, err := getTaInfo(parser, ta)
    90  			if err != nil {
    91  				t.Fatal(err)
    92  			}
    93  
    94  			if ta.Height != b.Height {
    95  				t.Errorf("Tx %s: block height mismatch: %d != %d", tx.Txid, ta.Height, b.Height)
    96  				continue
    97  			}
    98  
    99  			if len(txInfo.inputs) > 0 && !reflect.DeepEqual(taInfo.inputs, txInfo.inputs) {
   100  				t.Errorf("Tx %s: inputs mismatch: got %q, want %q", tx.Txid, taInfo.inputs, txInfo.inputs)
   101  			}
   102  
   103  			if !reflect.DeepEqual(taInfo.outputs, txInfo.outputs) {
   104  				t.Errorf("Tx %s: outputs mismatch: got %q, want %q", tx.Txid, taInfo.outputs, txInfo.outputs)
   105  			}
   106  
   107  			if taInfo.valOutSat.Cmp(&txInfo.valOutSat) != 0 {
   108  				t.Errorf("Tx %s: total output amount mismatch: got %s, want %s",
   109  					tx.Txid, taInfo.valOutSat.String(), txInfo.valOutSat.String())
   110  			}
   111  
   112  			if len(txInfo.inputs) > 0 {
   113  				treshold := "0.0001"
   114  				fee := new(big.Int).Sub(&taInfo.valInSat, &taInfo.valOutSat)
   115  				if strings.Compare(parser.AmountToDecimalString(fee), treshold) > 0 {
   116  					t.Errorf("Tx %s: suspicious amounts: input ∑ [%s] - output ∑ [%s] > %s",
   117  						tx.Txid, taInfo.valInSat.String(), taInfo.valOutSat.String(), treshold)
   118  				}
   119  			}
   120  		}
   121  	}
   122  }
   123  
   124  func verifyTransactions2(t *testing.T, d *db.RocksDB, rng Range, addr2txs map[string][]string, exist bool) {
   125  	noErrs := 0
   126  	for addr, txs := range addr2txs {
   127  		checkMap := make(map[string]bool, len(txs))
   128  		for _, txid := range txs {
   129  			checkMap[txid] = false
   130  		}
   131  
   132  		err := d.GetTransactions(addr, rng.Lower, rng.Upper, func(txid string, vout uint32, isOutput bool) error {
   133  			if isOutput {
   134  				checkMap[txid] = true
   135  			}
   136  			return nil
   137  		})
   138  		if err != nil {
   139  			t.Fatal(err)
   140  		}
   141  
   142  		for _, txid := range txs {
   143  			if checkMap[txid] != exist {
   144  				auxverb := "wasn't"
   145  				if !exist {
   146  					auxverb = "was"
   147  				}
   148  				t.Errorf("%s: transaction %s %s found [expected = %t]", addr, txid, auxverb, exist)
   149  				noErrs++
   150  				if noErrs >= 10 {
   151  					t.Fatal("Too many errors")
   152  				}
   153  			}
   154  		}
   155  	}
   156  }
   157  
   158  func getFakeBlocks(h *TestHandler, rng Range) []BlockID {
   159  	blks := make([]BlockID, 0, rng.Upper-rng.Lower+1)
   160  	for i := rng.Lower; i <= rng.Upper; i++ {
   161  		if b, found := h.TestData.HandleFork.FakeBlocks[i]; found {
   162  			blks = append(blks, b)
   163  		}
   164  	}
   165  	return blks
   166  }
   167  
   168  func getRealBlocks(h *TestHandler, rng Range) []BlockID {
   169  	blks := make([]BlockID, 0, rng.Upper-rng.Lower+1)
   170  	for _, b := range h.TestData.HandleFork.RealBlocks {
   171  		if b.Height >= rng.Lower && b.Height <= rng.Upper {
   172  			blks = append(blks, b)
   173  		}
   174  	}
   175  	return blks
   176  }
   177  
   178  func makeFakeChain(chain bchain.BlockChain, blks []BlockID, upper uint32) (*fakeBlockChain, error) {
   179  	if blks[len(blks)-1].Height != upper {
   180  		return nil, fmt.Errorf("Range must end with fake block in order to emulate fork [%d != %d]", blks[len(blks)-1].Height, upper)
   181  	}
   182  	mBlks := make(map[uint32]BlockID, len(blks))
   183  	for i := range blks {
   184  		mBlks[blks[i].Height] = blks[i]
   185  	}
   186  	return &fakeBlockChain{
   187  		BlockChain:  chain,
   188  		returnFakes: true,
   189  		fakeBlocks:  mBlks,
   190  		bestHeight:  upper,
   191  	}, nil
   192  }
   193  
   194  func getTxs(h *TestHandler, d *db.RocksDB, rng Range, blks []BlockID) ([]bchain.Tx, error) {
   195  	res := make([]bchain.Tx, 0, (rng.Upper-rng.Lower+1)*2000)
   196  
   197  	for _, b := range blks {
   198  		bi, err := d.GetBlockInfo(b.Height)
   199  		if err != nil {
   200  			return nil, err
   201  		}
   202  		if bi.Hash != b.Hash {
   203  			return nil, fmt.Errorf("Block hash mismatch: %s != %s", bi.Hash, b.Hash)
   204  		}
   205  
   206  		txs, err := getBlockTxs(h.Chain, b.Hash)
   207  		if err != nil {
   208  			return nil, err
   209  		}
   210  		res = append(res, txs...)
   211  	}
   212  
   213  	return res, nil
   214  }
   215  
   216  func getBlockTxs(chain bchain.BlockChain, hash string) ([]bchain.Tx, error) {
   217  	b, err := chain.GetBlock(hash, 0)
   218  	if err != nil {
   219  		return nil, fmt.Errorf("GetBlock: %s", err)
   220  	}
   221  	parser := chain.GetChainParser()
   222  	for i := range b.Txs {
   223  		err := setTxAddresses(parser, &b.Txs[i])
   224  		if err != nil {
   225  			return nil, fmt.Errorf("setTxAddresses [%s]: %s", b.Txs[i].Txid, err)
   226  		}
   227  	}
   228  	return b.Txs, nil
   229  }
   230  
   231  func getAddr2TxsMap(txs []bchain.Tx) map[string][]string {
   232  	addr2txs := make(map[string][]string)
   233  	for i := range txs {
   234  		for j := range txs[i].Vout {
   235  			for k := range txs[i].Vout[j].ScriptPubKey.Addresses {
   236  				addr := txs[i].Vout[j].ScriptPubKey.Addresses[k]
   237  				txid := txs[i].Txid
   238  				addr2txs[addr] = append(addr2txs[addr], txid)
   239  			}
   240  		}
   241  	}
   242  	return addr2txs
   243  }