github.com/bamzi/go-ethereum@v1.6.7-0.20170704111104-138f26c93af1/les/handler_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 "math/rand" 21 "testing" 22 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/core" 25 "github.com/ethereum/go-ethereum/core/types" 26 "github.com/ethereum/go-ethereum/crypto" 27 "github.com/ethereum/go-ethereum/eth/downloader" 28 "github.com/ethereum/go-ethereum/ethdb" 29 "github.com/ethereum/go-ethereum/p2p" 30 "github.com/ethereum/go-ethereum/rlp" 31 "github.com/ethereum/go-ethereum/trie" 32 ) 33 34 func expectResponse(r p2p.MsgReader, msgcode, reqID, bv uint64, data interface{}) error { 35 type resp struct { 36 ReqID, BV uint64 37 Data interface{} 38 } 39 return p2p.ExpectMsg(r, msgcode, resp{reqID, bv, data}) 40 } 41 42 // Tests that block headers can be retrieved from a remote chain based on user queries. 43 func TestGetBlockHeadersLes1(t *testing.T) { testGetBlockHeaders(t, 1) } 44 45 func testGetBlockHeaders(t *testing.T, protocol int) { 46 db, _ := ethdb.NewMemDatabase() 47 pm := newTestProtocolManagerMust(t, false, downloader.MaxHashFetch+15, nil, nil, nil, db) 48 bc := pm.blockchain.(*core.BlockChain) 49 peer, _ := newTestPeer(t, "peer", protocol, pm, true) 50 defer peer.close() 51 52 // Create a "random" unknown hash for testing 53 var unknown common.Hash 54 for i := range unknown { 55 unknown[i] = byte(i) 56 } 57 // Create a batch of tests for various scenarios 58 limit := uint64(MaxHeaderFetch) 59 tests := []struct { 60 query *getBlockHeadersData // The query to execute for header retrieval 61 expect []common.Hash // The hashes of the block whose headers are expected 62 }{ 63 // A single random block should be retrievable by hash and number too 64 { 65 &getBlockHeadersData{Origin: hashOrNumber{Hash: bc.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, 66 []common.Hash{bc.GetBlockByNumber(limit / 2).Hash()}, 67 }, { 68 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1}, 69 []common.Hash{bc.GetBlockByNumber(limit / 2).Hash()}, 70 }, 71 // Multiple headers should be retrievable in both directions 72 { 73 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3}, 74 []common.Hash{ 75 bc.GetBlockByNumber(limit / 2).Hash(), 76 bc.GetBlockByNumber(limit/2 + 1).Hash(), 77 bc.GetBlockByNumber(limit/2 + 2).Hash(), 78 }, 79 }, { 80 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, 81 []common.Hash{ 82 bc.GetBlockByNumber(limit / 2).Hash(), 83 bc.GetBlockByNumber(limit/2 - 1).Hash(), 84 bc.GetBlockByNumber(limit/2 - 2).Hash(), 85 }, 86 }, 87 // Multiple headers with skip lists should be retrievable 88 { 89 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, 90 []common.Hash{ 91 bc.GetBlockByNumber(limit / 2).Hash(), 92 bc.GetBlockByNumber(limit/2 + 4).Hash(), 93 bc.GetBlockByNumber(limit/2 + 8).Hash(), 94 }, 95 }, { 96 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, 97 []common.Hash{ 98 bc.GetBlockByNumber(limit / 2).Hash(), 99 bc.GetBlockByNumber(limit/2 - 4).Hash(), 100 bc.GetBlockByNumber(limit/2 - 8).Hash(), 101 }, 102 }, 103 // The chain endpoints should be retrievable 104 { 105 &getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1}, 106 []common.Hash{bc.GetBlockByNumber(0).Hash()}, 107 }, { 108 &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64()}, Amount: 1}, 109 []common.Hash{bc.CurrentBlock().Hash()}, 110 }, 111 // Ensure protocol limits are honored 112 /*{ 113 &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, 114 bc.GetBlockHashesFromHash(bc.CurrentBlock().Hash(), limit), 115 },*/ 116 // Check that requesting more than available is handled gracefully 117 { 118 &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, 119 []common.Hash{ 120 bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 4).Hash(), 121 bc.GetBlockByNumber(bc.CurrentBlock().NumberU64()).Hash(), 122 }, 123 }, { 124 &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, 125 []common.Hash{ 126 bc.GetBlockByNumber(4).Hash(), 127 bc.GetBlockByNumber(0).Hash(), 128 }, 129 }, 130 // Check that requesting more than available is handled gracefully, even if mid skip 131 { 132 &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, 133 []common.Hash{ 134 bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 4).Hash(), 135 bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1).Hash(), 136 }, 137 }, { 138 &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, 139 []common.Hash{ 140 bc.GetBlockByNumber(4).Hash(), 141 bc.GetBlockByNumber(1).Hash(), 142 }, 143 }, 144 // Check that non existing headers aren't returned 145 { 146 &getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1}, 147 []common.Hash{}, 148 }, { 149 &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() + 1}, Amount: 1}, 150 []common.Hash{}, 151 }, 152 } 153 // Run each of the tests and verify the results against the chain 154 var reqID uint64 155 for i, tt := range tests { 156 // Collect the headers to expect in the response 157 headers := []*types.Header{} 158 for _, hash := range tt.expect { 159 headers = append(headers, bc.GetHeaderByHash(hash)) 160 } 161 // Send the hash request and verify the response 162 reqID++ 163 cost := peer.GetRequestCost(GetBlockHeadersMsg, int(tt.query.Amount)) 164 sendRequest(peer.app, GetBlockHeadersMsg, reqID, cost, tt.query) 165 if err := expectResponse(peer.app, BlockHeadersMsg, reqID, testBufLimit, headers); err != nil { 166 t.Errorf("test %d: headers mismatch: %v", i, err) 167 } 168 } 169 } 170 171 // Tests that block contents can be retrieved from a remote chain based on their hashes. 172 func TestGetBlockBodiesLes1(t *testing.T) { testGetBlockBodies(t, 1) } 173 174 func testGetBlockBodies(t *testing.T, protocol int) { 175 db, _ := ethdb.NewMemDatabase() 176 pm := newTestProtocolManagerMust(t, false, downloader.MaxBlockFetch+15, nil, nil, nil, db) 177 bc := pm.blockchain.(*core.BlockChain) 178 peer, _ := newTestPeer(t, "peer", protocol, pm, true) 179 defer peer.close() 180 181 // Create a batch of tests for various scenarios 182 limit := MaxBodyFetch 183 tests := []struct { 184 random int // Number of blocks to fetch randomly from the chain 185 explicit []common.Hash // Explicitly requested blocks 186 available []bool // Availability of explicitly requested blocks 187 expected int // Total number of existing blocks to expect 188 }{ 189 {1, nil, nil, 1}, // A single random block should be retrievable 190 {10, nil, nil, 10}, // Multiple random blocks should be retrievable 191 {limit, nil, nil, limit}, // The maximum possible blocks should be retrievable 192 //{limit + 1, nil, nil, limit}, // No more than the possible block count should be returned 193 {0, []common.Hash{bc.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable 194 {0, []common.Hash{bc.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable 195 {0, []common.Hash{{}}, []bool{false}, 0}, // A non existent block should not be returned 196 197 // Existing and non-existing blocks interleaved should not cause problems 198 {0, []common.Hash{ 199 {}, 200 bc.GetBlockByNumber(1).Hash(), 201 {}, 202 bc.GetBlockByNumber(10).Hash(), 203 {}, 204 bc.GetBlockByNumber(100).Hash(), 205 {}, 206 }, []bool{false, true, false, true, false, true, false}, 3}, 207 } 208 // Run each of the tests and verify the results against the chain 209 var reqID uint64 210 for i, tt := range tests { 211 // Collect the hashes to request, and the response to expect 212 hashes, seen := []common.Hash{}, make(map[int64]bool) 213 bodies := []*types.Body{} 214 215 for j := 0; j < tt.random; j++ { 216 for { 217 num := rand.Int63n(int64(bc.CurrentBlock().NumberU64())) 218 if !seen[num] { 219 seen[num] = true 220 221 block := bc.GetBlockByNumber(uint64(num)) 222 hashes = append(hashes, block.Hash()) 223 if len(bodies) < tt.expected { 224 bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()}) 225 } 226 break 227 } 228 } 229 } 230 for j, hash := range tt.explicit { 231 hashes = append(hashes, hash) 232 if tt.available[j] && len(bodies) < tt.expected { 233 block := bc.GetBlockByHash(hash) 234 bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()}) 235 } 236 } 237 reqID++ 238 // Send the hash request and verify the response 239 cost := peer.GetRequestCost(GetBlockBodiesMsg, len(hashes)) 240 sendRequest(peer.app, GetBlockBodiesMsg, reqID, cost, hashes) 241 if err := expectResponse(peer.app, BlockBodiesMsg, reqID, testBufLimit, bodies); err != nil { 242 t.Errorf("test %d: bodies mismatch: %v", i, err) 243 } 244 } 245 } 246 247 // Tests that the contract codes can be retrieved based on account addresses. 248 func TestGetCodeLes1(t *testing.T) { testGetCode(t, 1) } 249 250 func testGetCode(t *testing.T, protocol int) { 251 // Assemble the test environment 252 db, _ := ethdb.NewMemDatabase() 253 pm := newTestProtocolManagerMust(t, false, 4, testChainGen, nil, nil, db) 254 bc := pm.blockchain.(*core.BlockChain) 255 peer, _ := newTestPeer(t, "peer", protocol, pm, true) 256 defer peer.close() 257 258 var codereqs []*CodeReq 259 var codes [][]byte 260 261 for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ { 262 header := bc.GetHeaderByNumber(i) 263 req := &CodeReq{ 264 BHash: header.Hash(), 265 AccKey: crypto.Keccak256(testContractAddr[:]), 266 } 267 codereqs = append(codereqs, req) 268 if i >= testContractDeployed { 269 codes = append(codes, testContractCodeDeployed) 270 } 271 } 272 273 cost := peer.GetRequestCost(GetCodeMsg, len(codereqs)) 274 sendRequest(peer.app, GetCodeMsg, 42, cost, codereqs) 275 if err := expectResponse(peer.app, CodeMsg, 42, testBufLimit, codes); err != nil { 276 t.Errorf("codes mismatch: %v", err) 277 } 278 } 279 280 // Tests that the transaction receipts can be retrieved based on hashes. 281 func TestGetReceiptLes1(t *testing.T) { testGetReceipt(t, 1) } 282 283 func testGetReceipt(t *testing.T, protocol int) { 284 // Assemble the test environment 285 db, _ := ethdb.NewMemDatabase() 286 pm := newTestProtocolManagerMust(t, false, 4, testChainGen, nil, nil, db) 287 bc := pm.blockchain.(*core.BlockChain) 288 peer, _ := newTestPeer(t, "peer", protocol, pm, true) 289 defer peer.close() 290 291 // Collect the hashes to request, and the response to expect 292 hashes, receipts := []common.Hash{}, []types.Receipts{} 293 for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ { 294 block := bc.GetBlockByNumber(i) 295 296 hashes = append(hashes, block.Hash()) 297 receipts = append(receipts, core.GetBlockReceipts(db, block.Hash(), block.NumberU64())) 298 } 299 // Send the hash request and verify the response 300 cost := peer.GetRequestCost(GetReceiptsMsg, len(hashes)) 301 sendRequest(peer.app, GetReceiptsMsg, 42, cost, hashes) 302 if err := expectResponse(peer.app, ReceiptsMsg, 42, testBufLimit, receipts); err != nil { 303 t.Errorf("receipts mismatch: %v", err) 304 } 305 } 306 307 // Tests that trie merkle proofs can be retrieved 308 func TestGetProofsLes1(t *testing.T) { testGetReceipt(t, 1) } 309 310 func testGetProofs(t *testing.T, protocol int) { 311 // Assemble the test environment 312 db, _ := ethdb.NewMemDatabase() 313 pm := newTestProtocolManagerMust(t, false, 4, testChainGen, nil, nil, db) 314 bc := pm.blockchain.(*core.BlockChain) 315 peer, _ := newTestPeer(t, "peer", protocol, pm, true) 316 defer peer.close() 317 318 var proofreqs []ProofReq 319 var proofs [][]rlp.RawValue 320 321 accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr, {}} 322 for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ { 323 header := bc.GetHeaderByNumber(i) 324 root := header.Root 325 trie, _ := trie.New(root, db) 326 327 for _, acc := range accounts { 328 req := ProofReq{ 329 BHash: header.Hash(), 330 Key: acc[:], 331 } 332 proofreqs = append(proofreqs, req) 333 334 proof := trie.Prove(crypto.Keccak256(acc[:])) 335 proofs = append(proofs, proof) 336 } 337 } 338 // Send the proof request and verify the response 339 cost := peer.GetRequestCost(GetProofsMsg, len(proofreqs)) 340 sendRequest(peer.app, GetProofsMsg, 42, cost, proofreqs) 341 if err := expectResponse(peer.app, ProofsMsg, 42, testBufLimit, proofs); err != nil { 342 t.Errorf("proofs mismatch: %v", err) 343 } 344 }