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 }