github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/les/odr_test.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package les 18 19 import ( 20 "bytes" 21 "context" 22 "crypto/rand" 23 "fmt" 24 "math/big" 25 "reflect" 26 "testing" 27 "time" 28 29 "github.com/ethereum/go-ethereum/common" 30 "github.com/ethereum/go-ethereum/common/math" 31 "github.com/ethereum/go-ethereum/core" 32 "github.com/ethereum/go-ethereum/core/rawdb" 33 "github.com/ethereum/go-ethereum/core/state" 34 "github.com/ethereum/go-ethereum/core/types" 35 "github.com/ethereum/go-ethereum/core/vm" 36 "github.com/ethereum/go-ethereum/ethdb" 37 "github.com/ethereum/go-ethereum/light" 38 "github.com/ethereum/go-ethereum/params" 39 "github.com/ethereum/go-ethereum/rlp" 40 ) 41 42 type odrTestFn func(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte 43 44 func TestOdrGetBlockLes2(t *testing.T) { testOdr(t, 2, 1, true, odrGetBlock) } 45 func TestOdrGetBlockLes3(t *testing.T) { testOdr(t, 3, 1, true, odrGetBlock) } 46 func TestOdrGetBlockLes4(t *testing.T) { testOdr(t, 4, 1, true, odrGetBlock) } 47 48 func odrGetBlock(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { 49 var block *types.Block 50 if bc != nil { 51 block = bc.GetBlockByHash(bhash) 52 } else { 53 block, _ = lc.GetBlockByHash(ctx, bhash) 54 } 55 if block == nil { 56 return nil 57 } 58 rlp, _ := rlp.EncodeToBytes(block) 59 return rlp 60 } 61 62 func TestOdrGetReceiptsLes2(t *testing.T) { testOdr(t, 2, 1, true, odrGetReceipts) } 63 func TestOdrGetReceiptsLes3(t *testing.T) { testOdr(t, 3, 1, true, odrGetReceipts) } 64 func TestOdrGetReceiptsLes4(t *testing.T) { testOdr(t, 4, 1, true, odrGetReceipts) } 65 66 func odrGetReceipts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { 67 var receipts types.Receipts 68 if bc != nil { 69 if number := rawdb.ReadHeaderNumber(db, bhash); number != nil { 70 receipts = rawdb.ReadReceipts(db, bhash, *number, config) 71 } 72 } else { 73 if number := rawdb.ReadHeaderNumber(db, bhash); number != nil { 74 receipts, _ = light.GetBlockReceipts(ctx, lc.Odr(), bhash, *number) 75 } 76 } 77 if receipts == nil { 78 return nil 79 } 80 rlp, _ := rlp.EncodeToBytes(receipts) 81 return rlp 82 } 83 84 func TestOdrAccountsLes2(t *testing.T) { testOdr(t, 2, 1, true, odrAccounts) } 85 func TestOdrAccountsLes3(t *testing.T) { testOdr(t, 3, 1, true, odrAccounts) } 86 func TestOdrAccountsLes4(t *testing.T) { testOdr(t, 4, 1, true, odrAccounts) } 87 88 func odrAccounts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { 89 dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678") 90 acc := []common.Address{bankAddr, userAddr1, userAddr2, dummyAddr} 91 92 var ( 93 res []byte 94 st *state.StateDB 95 err error 96 ) 97 for _, addr := range acc { 98 if bc != nil { 99 header := bc.GetHeaderByHash(bhash) 100 st, err = state.New(header.Root, state.NewDatabase(db), nil) 101 } else { 102 header := lc.GetHeaderByHash(bhash) 103 st = light.NewState(ctx, header, lc.Odr()) 104 } 105 if err == nil { 106 bal := st.GetBalance(addr) 107 rlp, _ := rlp.EncodeToBytes(bal) 108 res = append(res, rlp...) 109 } 110 } 111 return res 112 } 113 114 func TestOdrContractCallLes2(t *testing.T) { testOdr(t, 2, 2, true, odrContractCall) } 115 func TestOdrContractCallLes3(t *testing.T) { testOdr(t, 3, 2, true, odrContractCall) } 116 func TestOdrContractCallLes4(t *testing.T) { testOdr(t, 4, 2, true, odrContractCall) } 117 118 type callmsg struct { 119 types.Message 120 } 121 122 func (callmsg) CheckNonce() bool { return false } 123 124 func odrContractCall(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { 125 data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000") 126 127 var res []byte 128 for i := 0; i < 3; i++ { 129 data[35] = byte(i) 130 if bc != nil { 131 header := bc.GetHeaderByHash(bhash) 132 statedb, err := state.New(header.Root, state.NewDatabase(db), nil) 133 134 if err == nil { 135 from := statedb.GetOrNewStateObject(bankAddr) 136 from.SetBalance(math.MaxBig256) 137 138 msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true)} 139 140 blockContext := core.NewEVMBlockContext(header, bc, nil) 141 txContext := core.NewEVMTxContext(msg) 142 vmenv := vm.NewEVM(blockContext, txContext, statedb, config, vm.Config{NoBaseFee: true}) 143 144 //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) 145 gp := new(core.GasPool).AddGas(math.MaxUint64) 146 // nolint : contextcheck 147 result, _ := core.ApplyMessage(vmenv, msg, gp, context.Background()) 148 res = append(res, result.Return()...) 149 } 150 } else { 151 header := lc.GetHeaderByHash(bhash) 152 state := light.NewState(ctx, header, lc.Odr()) 153 state.SetBalance(bankAddr, math.MaxBig256) 154 msg := callmsg{types.NewMessage(bankAddr, &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true)} 155 blockContext := core.NewEVMBlockContext(header, lc, nil) 156 txContext := core.NewEVMTxContext(msg) 157 vmenv := vm.NewEVM(blockContext, txContext, state, config, vm.Config{NoBaseFee: true}) 158 gp := new(core.GasPool).AddGas(math.MaxUint64) 159 // nolint : contextcheck 160 result, _ := core.ApplyMessage(vmenv, msg, gp, context.Background()) 161 if state.Error() == nil { 162 res = append(res, result.Return()...) 163 } 164 } 165 } 166 return res 167 } 168 169 func TestOdrTxStatusLes2(t *testing.T) { testOdr(t, 2, 1, false, odrTxStatus) } 170 func TestOdrTxStatusLes3(t *testing.T) { testOdr(t, 3, 1, false, odrTxStatus) } 171 func TestOdrTxStatusLes4(t *testing.T) { testOdr(t, 4, 1, false, odrTxStatus) } 172 173 func odrTxStatus(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { 174 var txs types.Transactions 175 if bc != nil { 176 block := bc.GetBlockByHash(bhash) 177 txs = block.Transactions() 178 } else { 179 if block, _ := lc.GetBlockByHash(ctx, bhash); block != nil { 180 btxs := block.Transactions() 181 txs = make(types.Transactions, len(btxs)) 182 for i, tx := range btxs { 183 var err error 184 txs[i], _, _, _, err = light.GetTransaction(ctx, lc.Odr(), tx.Hash()) 185 if err != nil { 186 return nil 187 } 188 } 189 } 190 } 191 rlp, _ := rlp.EncodeToBytes(txs) 192 return rlp 193 } 194 195 // testOdr tests odr requests whose validation guaranteed by block headers. 196 func testOdr(t *testing.T, protocol int, expFail uint64, checkCached bool, fn odrTestFn) { 197 // Assemble the test environment 198 netconfig := testnetConfig{ 199 blocks: 4, 200 protocol: protocol, 201 connect: true, 202 nopruning: true, 203 } 204 server, client, tearDown := newClientServerEnv(t, netconfig) 205 defer tearDown() 206 207 // Ensure the client has synced all necessary data. 208 clientHead := client.handler.backend.blockchain.CurrentHeader() 209 if clientHead.Number.Uint64() != 4 { 210 t.Fatalf("Failed to sync the chain with server, head: %v", clientHead.Number.Uint64()) 211 } 212 // Disable the mechanism that we will wait a few time for request 213 // even there is no suitable peer to send right now. 214 waitForPeers = 0 215 216 test := func(expFail uint64) { 217 // Mark this as a helper to put the failures at the correct lines 218 t.Helper() 219 220 for i := uint64(0); i <= server.handler.blockchain.CurrentHeader().Number.Uint64(); i++ { 221 bhash := rawdb.ReadCanonicalHash(server.db, i) 222 b1 := fn(light.NoOdr, server.db, server.handler.server.chainConfig, server.handler.blockchain, nil, bhash) 223 224 // Set the timeout as 1 second here, ensure there is enough time 225 // for travis to make the action. 226 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 227 b2 := fn(ctx, client.db, client.handler.backend.chainConfig, nil, client.handler.backend.blockchain, bhash) 228 cancel() 229 230 eq := bytes.Equal(b1, b2) 231 exp := i < expFail 232 if exp && !eq { 233 t.Fatalf("odr mismatch: have %x, want %x", b2, b1) 234 } 235 if !exp && eq { 236 t.Fatalf("unexpected odr match") 237 } 238 } 239 } 240 241 // expect retrievals to fail (except genesis block) without a les peer 242 client.handler.backend.peers.lock.Lock() 243 client.peer.speer.hasBlockHook = func(common.Hash, uint64, bool) bool { return false } 244 client.handler.backend.peers.lock.Unlock() 245 test(expFail) 246 247 // expect all retrievals to pass 248 client.handler.backend.peers.lock.Lock() 249 client.peer.speer.hasBlockHook = func(common.Hash, uint64, bool) bool { return true } 250 client.handler.backend.peers.lock.Unlock() 251 test(5) 252 253 // still expect all retrievals to pass, now data should be cached locally 254 if checkCached { 255 client.handler.backend.peers.unregister(client.peer.speer.id) 256 time.Sleep(time.Millisecond * 10) // ensure that all peerSetNotify callbacks are executed 257 test(5) 258 } 259 } 260 261 func TestGetTxStatusFromUnindexedPeersLES4(t *testing.T) { testGetTxStatusFromUnindexedPeers(t, lpv4) } 262 263 func testGetTxStatusFromUnindexedPeers(t *testing.T, protocol int) { 264 var ( 265 blocks = 8 266 netconfig = testnetConfig{ 267 blocks: blocks, 268 protocol: protocol, 269 nopruning: true, 270 } 271 ) 272 server, client, tearDown := newClientServerEnv(t, netconfig) 273 defer tearDown() 274 275 // Iterate the chain, create the tx indexes locally 276 var ( 277 testHash common.Hash 278 testStatus light.TxStatus 279 280 txs = make(map[common.Hash]*types.Transaction) // Transaction objects set 281 blockNumbers = make(map[common.Hash]uint64) // Transaction hash to block number mappings 282 blockHashes = make(map[common.Hash]common.Hash) // Transaction hash to block hash mappings 283 intraIndex = make(map[common.Hash]uint64) // Transaction intra-index in block 284 ) 285 for number := uint64(1); number < server.backend.Blockchain().CurrentBlock().NumberU64(); number++ { 286 block := server.backend.Blockchain().GetBlockByNumber(number) 287 if block == nil { 288 t.Fatalf("Failed to retrieve block %d", number) 289 } 290 for index, tx := range block.Transactions() { 291 txs[tx.Hash()] = tx 292 blockNumbers[tx.Hash()] = number 293 blockHashes[tx.Hash()] = block.Hash() 294 intraIndex[tx.Hash()] = uint64(index) 295 296 if testHash == (common.Hash{}) { 297 testHash = tx.Hash() 298 testStatus = light.TxStatus{ 299 Status: core.TxStatusIncluded, 300 Lookup: &rawdb.LegacyTxLookupEntry{ 301 BlockHash: block.Hash(), 302 BlockIndex: block.NumberU64(), 303 Index: uint64(index), 304 }, 305 } 306 } 307 } 308 } 309 // serveMsg processes incoming GetTxStatusMsg and sends the response back. 310 serveMsg := func(peer *testPeer, txLookup uint64) error { 311 msg, err := peer.app.ReadMsg() 312 if err != nil { 313 return err 314 } 315 if msg.Code != GetTxStatusMsg { 316 return fmt.Errorf("message code mismatch: got %d, expected %d", msg.Code, GetTxStatusMsg) 317 } 318 var r GetTxStatusPacket 319 if err := msg.Decode(&r); err != nil { 320 return err 321 } 322 stats := make([]light.TxStatus, len(r.Hashes)) 323 for i, hash := range r.Hashes { 324 number, exist := blockNumbers[hash] 325 if !exist { 326 continue // Filter out unknown transactions 327 } 328 min := uint64(blocks) - txLookup 329 if txLookup != txIndexUnlimited && (txLookup == txIndexDisabled || number < min) { 330 continue // Filter out unindexed transactions 331 } 332 stats[i].Status = core.TxStatusIncluded 333 stats[i].Lookup = &rawdb.LegacyTxLookupEntry{ 334 BlockHash: blockHashes[hash], 335 BlockIndex: number, 336 Index: intraIndex[hash], 337 } 338 } 339 data, _ := rlp.EncodeToBytes(stats) 340 reply := &reply{peer.app, TxStatusMsg, r.ReqID, data} 341 reply.send(testBufLimit) 342 return nil 343 } 344 345 var testspecs = []struct { 346 peers int 347 txLookups []uint64 348 txs []common.Hash 349 results []light.TxStatus 350 }{ 351 // Retrieve mined transaction from the empty peerset 352 { 353 peers: 0, 354 txLookups: []uint64{}, 355 txs: []common.Hash{testHash}, 356 results: []light.TxStatus{{}}, 357 }, 358 // Retrieve unknown transaction from the full peers 359 { 360 peers: 3, 361 txLookups: []uint64{txIndexUnlimited, txIndexUnlimited, txIndexUnlimited}, 362 txs: []common.Hash{randomHash()}, 363 results: []light.TxStatus{{}}, 364 }, 365 // Retrieve mined transaction from the full peers 366 { 367 peers: 3, 368 txLookups: []uint64{txIndexUnlimited, txIndexUnlimited, txIndexUnlimited}, 369 txs: []common.Hash{testHash}, 370 results: []light.TxStatus{testStatus}, 371 }, 372 // Retrieve mixed transactions from the full peers 373 { 374 peers: 3, 375 txLookups: []uint64{txIndexUnlimited, txIndexUnlimited, txIndexUnlimited}, 376 txs: []common.Hash{randomHash(), testHash}, 377 results: []light.TxStatus{{}, testStatus}, 378 }, 379 // Retrieve mixed transactions from unindexed peer(but the target is still available) 380 { 381 peers: 3, 382 txLookups: []uint64{uint64(blocks) - testStatus.Lookup.BlockIndex, uint64(blocks) - testStatus.Lookup.BlockIndex - 1, uint64(blocks) - testStatus.Lookup.BlockIndex - 2}, 383 txs: []common.Hash{randomHash(), testHash}, 384 results: []light.TxStatus{{}, testStatus}, 385 }, 386 // Retrieve mixed transactions from unindexed peer(but the target is not available) 387 { 388 peers: 3, 389 txLookups: []uint64{uint64(blocks) - testStatus.Lookup.BlockIndex - 1, uint64(blocks) - testStatus.Lookup.BlockIndex - 1, uint64(blocks) - testStatus.Lookup.BlockIndex - 2}, 390 txs: []common.Hash{randomHash(), testHash}, 391 results: []light.TxStatus{{}, {}}, 392 }, 393 } 394 for _, testspec := range testspecs { 395 // Create a bunch of server peers with different tx history 396 var ( 397 serverPeers []*testPeer 398 closeFns []func() 399 ) 400 for i := 0; i < testspec.peers; i++ { 401 peer, closePeer, _ := client.newRawPeer(t, fmt.Sprintf("server-%d", i), protocol, testspec.txLookups[i]) 402 serverPeers = append(serverPeers, peer) 403 closeFns = append(closeFns, closePeer) 404 405 // Create a one-time routine for serving message 406 go func(i int, peer *testPeer, lookup uint64) { 407 serveMsg(peer, lookup) 408 }(i, peer, testspec.txLookups[i]) 409 } 410 411 // Send out the GetTxStatus requests, compare the result with 412 // expected value. 413 r := &light.TxStatusRequest{Hashes: testspec.txs} 414 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 415 defer cancel() 416 417 err := client.handler.backend.odr.RetrieveTxStatus(ctx, r) 418 if err != nil { 419 t.Errorf("Failed to retrieve tx status %v", err) 420 } else { 421 if !reflect.DeepEqual(testspec.results, r.Status) { 422 t.Errorf("Result mismatch, diff") 423 } 424 } 425 426 // Close all connected peers and start the next round 427 for _, closeFn := range closeFns { 428 closeFn() 429 } 430 } 431 } 432 433 // randomHash generates a random blob of data and returns it as a hash. 434 func randomHash() common.Hash { 435 var hash common.Hash 436 if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil { 437 panic(err) 438 } 439 return hash 440 }