github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/intprotocol/handler_test.go (about) 1 // Copyright 2015 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 intprotocol 18 19 import ( 20 "math" 21 "math/big" 22 "math/rand" 23 "testing" 24 "time" 25 26 "github.com/intfoundation/intchain/common" 27 "github.com/intfoundation/intchain/consensus" 28 "github.com/intfoundation/intchain/core" 29 "github.com/intfoundation/intchain/core/rawdb" 30 "github.com/intfoundation/intchain/core/state" 31 "github.com/intfoundation/intchain/core/types" 32 "github.com/intfoundation/intchain/crypto" 33 "github.com/intfoundation/intchain/intprotocol/downloader" 34 "github.com/intfoundation/intchain/p2p" 35 "github.com/intfoundation/intchain/params" 36 ) 37 38 // Tests that protocol versions and modes of operations are matched up properly. 39 func TestProtocolCompatibility(t *testing.T) { 40 // Define the compatibility chart 41 tests := []struct { 42 version uint 43 mode downloader.SyncMode 44 compatible bool 45 }{ 46 {61, downloader.FullSync, true}, {62, downloader.FullSync, true}, {63, downloader.FullSync, true}, 47 {61, downloader.FastSync, false}, {62, downloader.FastSync, false}, {63, downloader.FastSync, true}, 48 } 49 // Make sure anything we screw up is restored 50 backup := consensus.EthProtocol.Versions 51 defer func() { consensus.EthProtocol.Versions = backup }() 52 53 // Try all available compatibility configs and check for errors 54 for i, tt := range tests { 55 consensus.EthProtocol.Versions = []uint{tt.version} 56 57 pm, _, err := newTestProtocolManager(tt.mode, 0, nil, nil) 58 if pm != nil { 59 defer pm.Stop() 60 } 61 if (err == nil && !tt.compatible) || (err != nil && tt.compatible) { 62 t.Errorf("test %d: compatibility mismatch: have error %v, want compatibility %v", i, err, tt.compatible) 63 } 64 } 65 } 66 67 // Tests that block headers can be retrieved from a remote chain based on user queries. 68 func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) } 69 func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) } 70 71 func testGetBlockHeaders(t *testing.T, protocol int) { 72 pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil) 73 peer, _ := newTestPeer("peer", protocol, pm, true) 74 defer peer.close() 75 76 // Create a "random" unknown hash for testing 77 var unknown common.Hash 78 for i := range unknown { 79 unknown[i] = byte(i) 80 } 81 // Create a batch of tests for various scenarios 82 limit := uint64(downloader.MaxHeaderFetch) 83 tests := []struct { 84 query *getBlockHeadersData // The query to execute for header retrieval 85 expect []common.Hash // The hashes of the block whose headers are expected 86 }{ 87 // A single random block should be retrievable by hash and number too 88 { 89 &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, 90 []common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, 91 }, { 92 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1}, 93 []common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, 94 }, 95 // Multiple headers should be retrievable in both directions 96 { 97 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3}, 98 []common.Hash{ 99 pm.blockchain.GetBlockByNumber(limit / 2).Hash(), 100 pm.blockchain.GetBlockByNumber(limit/2 + 1).Hash(), 101 pm.blockchain.GetBlockByNumber(limit/2 + 2).Hash(), 102 }, 103 }, { 104 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, 105 []common.Hash{ 106 pm.blockchain.GetBlockByNumber(limit / 2).Hash(), 107 pm.blockchain.GetBlockByNumber(limit/2 - 1).Hash(), 108 pm.blockchain.GetBlockByNumber(limit/2 - 2).Hash(), 109 }, 110 }, 111 // Multiple headers with skip lists should be retrievable 112 { 113 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, 114 []common.Hash{ 115 pm.blockchain.GetBlockByNumber(limit / 2).Hash(), 116 pm.blockchain.GetBlockByNumber(limit/2 + 4).Hash(), 117 pm.blockchain.GetBlockByNumber(limit/2 + 8).Hash(), 118 }, 119 }, { 120 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, 121 []common.Hash{ 122 pm.blockchain.GetBlockByNumber(limit / 2).Hash(), 123 pm.blockchain.GetBlockByNumber(limit/2 - 4).Hash(), 124 pm.blockchain.GetBlockByNumber(limit/2 - 8).Hash(), 125 }, 126 }, 127 // The chain endpoints should be retrievable 128 { 129 &getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1}, 130 []common.Hash{pm.blockchain.GetBlockByNumber(0).Hash()}, 131 }, { 132 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64()}, Amount: 1}, 133 []common.Hash{pm.blockchain.CurrentBlock().Hash()}, 134 }, 135 // Ensure protocol limits are honored 136 { 137 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, 138 pm.blockchain.GetBlockHashesFromHash(pm.blockchain.CurrentBlock().Hash(), limit), 139 }, 140 // Check that requesting more than available is handled gracefully 141 { 142 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, 143 []common.Hash{ 144 pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), 145 pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()).Hash(), 146 }, 147 }, { 148 &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, 149 []common.Hash{ 150 pm.blockchain.GetBlockByNumber(4).Hash(), 151 pm.blockchain.GetBlockByNumber(0).Hash(), 152 }, 153 }, 154 // Check that requesting more than available is handled gracefully, even if mid skip 155 { 156 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, 157 []common.Hash{ 158 pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), 159 pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 1).Hash(), 160 }, 161 }, { 162 &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, 163 []common.Hash{ 164 pm.blockchain.GetBlockByNumber(4).Hash(), 165 pm.blockchain.GetBlockByNumber(1).Hash(), 166 }, 167 }, 168 // Check a corner case where requesting more can iterate past the endpoints 169 { 170 &getBlockHeadersData{Origin: hashOrNumber{Number: 2}, Amount: 5, Reverse: true}, 171 []common.Hash{ 172 pm.blockchain.GetBlockByNumber(2).Hash(), 173 pm.blockchain.GetBlockByNumber(1).Hash(), 174 pm.blockchain.GetBlockByNumber(0).Hash(), 175 }, 176 }, 177 // Check a corner case where skipping overflow loops back into the chain start 178 { 179 &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(3).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1}, 180 []common.Hash{ 181 pm.blockchain.GetBlockByNumber(3).Hash(), 182 }, 183 }, 184 // Check a corner case where skipping overflow loops back to the same header 185 { 186 &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(1).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64}, 187 []common.Hash{ 188 pm.blockchain.GetBlockByNumber(1).Hash(), 189 }, 190 }, 191 // Check that non existing headers aren't returned 192 { 193 &getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1}, 194 []common.Hash{}, 195 }, { 196 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() + 1}, Amount: 1}, 197 []common.Hash{}, 198 }, 199 } 200 // Run each of the tests and verify the results against the chain 201 for i, tt := range tests { 202 // Collect the headers to expect in the response 203 headers := []*types.Header{} 204 for _, hash := range tt.expect { 205 headers = append(headers, pm.blockchain.GetBlockByHash(hash).Header()) 206 } 207 // Send the hash request and verify the response 208 p2p.Send(peer.app, 0x03, tt.query) 209 if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil { 210 t.Errorf("test %d: headers mismatch: %v", i, err) 211 } 212 // If the test used number origins, repeat with hashes as the too 213 if tt.query.Origin.Hash == (common.Hash{}) { 214 if origin := pm.blockchain.GetBlockByNumber(tt.query.Origin.Number); origin != nil { 215 tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0 216 217 p2p.Send(peer.app, 0x03, tt.query) 218 if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil { 219 t.Errorf("test %d: headers mismatch: %v", i, err) 220 } 221 } 222 } 223 } 224 } 225 226 // Tests that block contents can be retrieved from a remote chain based on their hashes. 227 func TestGetBlockBodies62(t *testing.T) { testGetBlockBodies(t, 62) } 228 func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) } 229 230 func testGetBlockBodies(t *testing.T, protocol int) { 231 pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil) 232 peer, _ := newTestPeer("peer", protocol, pm, true) 233 defer peer.close() 234 235 // Create a batch of tests for various scenarios 236 limit := downloader.MaxBlockFetch 237 tests := []struct { 238 random int // Number of blocks to fetch randomly from the chain 239 explicit []common.Hash // Explicitly requested blocks 240 available []bool // Availability of explicitly requested blocks 241 expected int // Total number of existing blocks to expect 242 }{ 243 {1, nil, nil, 1}, // A single random block should be retrievable 244 {10, nil, nil, 10}, // Multiple random blocks should be retrievable 245 {limit, nil, nil, limit}, // The maximum possible blocks should be retrievable 246 {limit + 1, nil, nil, limit}, // No more than the possible block count should be returned 247 {0, []common.Hash{pm.blockchain.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable 248 {0, []common.Hash{pm.blockchain.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable 249 {0, []common.Hash{{}}, []bool{false}, 0}, // A non existent block should not be returned 250 251 // Existing and non-existing blocks interleaved should not cause problems 252 {0, []common.Hash{ 253 {}, 254 pm.blockchain.GetBlockByNumber(1).Hash(), 255 {}, 256 pm.blockchain.GetBlockByNumber(10).Hash(), 257 {}, 258 pm.blockchain.GetBlockByNumber(100).Hash(), 259 {}, 260 }, []bool{false, true, false, true, false, true, false}, 3}, 261 } 262 // Run each of the tests and verify the results against the chain 263 for i, tt := range tests { 264 // Collect the hashes to request, and the response to expect 265 hashes, seen := []common.Hash{}, make(map[int64]bool) 266 bodies := []*blockBody{} 267 268 for j := 0; j < tt.random; j++ { 269 for { 270 num := rand.Int63n(int64(pm.blockchain.CurrentBlock().NumberU64())) 271 if !seen[num] { 272 seen[num] = true 273 274 block := pm.blockchain.GetBlockByNumber(uint64(num)) 275 hashes = append(hashes, block.Hash()) 276 if len(bodies) < tt.expected { 277 bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) 278 } 279 break 280 } 281 } 282 } 283 for j, hash := range tt.explicit { 284 hashes = append(hashes, hash) 285 if tt.available[j] && len(bodies) < tt.expected { 286 block := pm.blockchain.GetBlockByHash(hash) 287 bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) 288 } 289 } 290 // Send the hash request and verify the response 291 p2p.Send(peer.app, 0x05, hashes) 292 if err := p2p.ExpectMsg(peer.app, 0x06, bodies); err != nil { 293 t.Errorf("test %d: bodies mismatch: %v", i, err) 294 } 295 } 296 } 297 298 // Tests that the node state database can be retrieved based on hashes. 299 func TestGetNodeData63(t *testing.T) { testGetNodeData(t, 63) } 300 301 func testGetNodeData(t *testing.T, protocol int) { 302 // Define three accounts to simulate transactions with 303 acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") 304 acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") 305 acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) 306 acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) 307 308 signer := types.HomesteadSigner{} 309 // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) 310 generator := func(i int, block *core.BlockGen) { 311 switch i { 312 case 0: 313 // In block 1, the test bank sends account #1 some ether. 314 tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) 315 block.AddTx(tx) 316 case 1: 317 // In block 2, the test bank sends some more ether to account #1. 318 // acc1Addr passes it on to account #2. 319 tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) 320 tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key) 321 block.AddTx(tx1) 322 block.AddTx(tx2) 323 case 2: 324 // Block 3 is empty but was mined by account #2. 325 block.SetCoinbase(acc2Addr) 326 block.SetExtra([]byte("yeehaw")) 327 case 3: 328 // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). 329 b2 := block.PrevBlock(1).Header() 330 b2.Extra = []byte("foo") 331 block.AddUncle(b2) 332 b3 := block.PrevBlock(2).Header() 333 b3.Extra = []byte("foo") 334 block.AddUncle(b3) 335 } 336 } 337 // Assemble the test environment 338 pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil) 339 peer, _ := newTestPeer("peer", protocol, pm, true) 340 defer peer.close() 341 342 // Fetch for now the entire chain db 343 hashes := []common.Hash{} 344 //for _, key := range db.Keys() { 345 // if len(key) == len(common.Hash{}) { 346 // hashes = append(hashes, common.BytesToHash(key)) 347 // } 348 //} 349 p2p.Send(peer.app, 0x0d, hashes) 350 msg, err := peer.app.ReadMsg() 351 if err != nil { 352 t.Fatalf("failed to read node data response: %v", err) 353 } 354 if msg.Code != 0x0e { 355 t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, 0x0c) 356 } 357 var data [][]byte 358 if err := msg.Decode(&data); err != nil { 359 t.Fatalf("failed to decode response node data: %v", err) 360 } 361 // Verify that all hashes correspond to the requested data, and reconstruct a state tree 362 for i, want := range hashes { 363 if hash := crypto.Keccak256Hash(data[i]); hash != want { 364 t.Errorf("data hash mismatch: have %x, want %x", hash, want) 365 } 366 } 367 statedb := rawdb.NewMemoryDatabase() 368 for i := 0; i < len(data); i++ { 369 statedb.Put(hashes[i].Bytes(), data[i]) 370 } 371 accounts := []common.Address{testBank, acc1Addr, acc2Addr} 372 for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { 373 trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), state.NewDatabase(statedb)) 374 375 for j, acc := range accounts { 376 state, _ := pm.blockchain.State() 377 bw := state.GetBalance(acc) 378 bh := trie.GetBalance(acc) 379 380 if (bw != nil && bh == nil) || (bw == nil && bh != nil) { 381 t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) 382 } 383 if bw != nil && bh != nil && bw.Cmp(bw) != 0 { 384 t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) 385 } 386 } 387 } 388 } 389 390 // Tests that the transaction receipts can be retrieved based on hashes. 391 func TestGetReceipt63(t *testing.T) { testGetReceipt(t, 63) } 392 393 func testGetReceipt(t *testing.T, protocol int) { 394 // Define three accounts to simulate transactions with 395 acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") 396 acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") 397 acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) 398 acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) 399 400 signer := types.HomesteadSigner{} 401 // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) 402 generator := func(i int, block *core.BlockGen) { 403 switch i { 404 case 0: 405 // In block 1, the test bank sends account #1 some ether. 406 tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) 407 block.AddTx(tx) 408 case 1: 409 // In block 2, the test bank sends some more ether to account #1. 410 // acc1Addr passes it on to account #2. 411 tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) 412 tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key) 413 block.AddTx(tx1) 414 block.AddTx(tx2) 415 case 2: 416 // Block 3 is empty but was mined by account #2. 417 block.SetCoinbase(acc2Addr) 418 block.SetExtra([]byte("yeehaw")) 419 case 3: 420 // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). 421 b2 := block.PrevBlock(1).Header() 422 b2.Extra = []byte("foo") 423 block.AddUncle(b2) 424 b3 := block.PrevBlock(2).Header() 425 b3.Extra = []byte("foo") 426 block.AddUncle(b3) 427 } 428 } 429 // Assemble the test environment 430 pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil) 431 peer, _ := newTestPeer("peer", protocol, pm, true) 432 defer peer.close() 433 434 // Collect the hashes to request, and the response to expect 435 hashes, receipts := []common.Hash{}, []types.Receipts{} 436 for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { 437 block := pm.blockchain.GetBlockByNumber(i) 438 439 hashes = append(hashes, block.Hash()) 440 receipts = append(receipts, pm.blockchain.GetReceiptsByHash(block.Hash())) 441 } 442 // Send the hash request and verify the response 443 p2p.Send(peer.app, 0x0f, hashes) 444 if err := p2p.ExpectMsg(peer.app, 0x10, receipts); err != nil { 445 t.Errorf("receipts mismatch: %v", err) 446 } 447 } 448 449 // Tests that post intprotocol protocol handshake, DAO fork-enabled clients also execute 450 // a DAO "challenge" verifying each others' DAO fork headers to ensure they're on 451 // compatible chains. 452 func TestDAOChallengeNoVsNo(t *testing.T) { testDAOChallenge(t, false, false, false) } 453 func TestDAOChallengeNoVsPro(t *testing.T) { testDAOChallenge(t, false, true, false) } 454 func TestDAOChallengeProVsNo(t *testing.T) { testDAOChallenge(t, true, false, false) } 455 func TestDAOChallengeProVsPro(t *testing.T) { testDAOChallenge(t, true, true, false) } 456 func TestDAOChallengeNoVsTimeout(t *testing.T) { testDAOChallenge(t, false, false, true) } 457 func TestDAOChallengeProVsTimeout(t *testing.T) { testDAOChallenge(t, true, true, true) } 458 459 func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool) { 460 // Reduce the DAO handshake challenge timeout 461 if timeout { 462 defer func(old time.Duration) { daoChallengeTimeout = old }(daoChallengeTimeout) 463 daoChallengeTimeout = 500 * time.Millisecond 464 } 465 // Create a DAO aware protocol manager 466 467 // Connect a new peer and check that we receive the DAO challenge 468 }