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