github.com/ethw3/go-ethereuma@v0.0.0-20221013053120-c14602a4c23c/les/server_requests.go (about) 1 // Copyright 2021 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 "encoding/binary" 21 "encoding/json" 22 23 "github.com/ethw3/go-ethereuma/common" 24 "github.com/ethw3/go-ethereuma/core" 25 "github.com/ethw3/go-ethereuma/core/state" 26 "github.com/ethw3/go-ethereuma/core/types" 27 "github.com/ethw3/go-ethereuma/light" 28 "github.com/ethw3/go-ethereuma/log" 29 "github.com/ethw3/go-ethereuma/metrics" 30 "github.com/ethw3/go-ethereuma/rlp" 31 "github.com/ethw3/go-ethereuma/trie" 32 ) 33 34 // serverBackend defines the backend functions needed for serving LES requests 35 type serverBackend interface { 36 ArchiveMode() bool 37 AddTxsSync() bool 38 BlockChain() *core.BlockChain 39 TxPool() *core.TxPool 40 GetHelperTrie(typ uint, index uint64) *trie.Trie 41 } 42 43 // Decoder is implemented by the messages passed to the handler functions 44 type Decoder interface { 45 Decode(val interface{}) error 46 } 47 48 // RequestType is a static struct that describes an LES request type and references 49 // its handler function. 50 type RequestType struct { 51 Name string 52 MaxCount uint64 53 InPacketsMeter, InTrafficMeter, OutPacketsMeter, OutTrafficMeter metrics.Meter 54 ServingTimeMeter metrics.Timer 55 Handle func(msg Decoder) (serve serveRequestFn, reqID, amount uint64, err error) 56 } 57 58 // serveRequestFn is returned by the request handler functions after decoding the request. 59 // This function does the actual request serving using the supplied backend. waitOrStop is 60 // called between serving individual request items and may block if the serving process 61 // needs to be throttled. If it returns false then the process is terminated. 62 // The reply is not sent by this function yet. The flow control feedback value is supplied 63 // by the protocol handler when calling the send function of the returned reply struct. 64 type serveRequestFn func(backend serverBackend, peer *clientPeer, waitOrStop func() bool) *reply 65 66 // Les3 contains the request types supported by les/2 and les/3 67 var Les3 = map[uint64]RequestType{ 68 GetBlockHeadersMsg: { 69 Name: "block header request", 70 MaxCount: MaxHeaderFetch, 71 InPacketsMeter: miscInHeaderPacketsMeter, 72 InTrafficMeter: miscInHeaderTrafficMeter, 73 OutPacketsMeter: miscOutHeaderPacketsMeter, 74 OutTrafficMeter: miscOutHeaderTrafficMeter, 75 ServingTimeMeter: miscServingTimeHeaderTimer, 76 Handle: handleGetBlockHeaders, 77 }, 78 GetBlockBodiesMsg: { 79 Name: "block bodies request", 80 MaxCount: MaxBodyFetch, 81 InPacketsMeter: miscInBodyPacketsMeter, 82 InTrafficMeter: miscInBodyTrafficMeter, 83 OutPacketsMeter: miscOutBodyPacketsMeter, 84 OutTrafficMeter: miscOutBodyTrafficMeter, 85 ServingTimeMeter: miscServingTimeBodyTimer, 86 Handle: handleGetBlockBodies, 87 }, 88 GetCodeMsg: { 89 Name: "code request", 90 MaxCount: MaxCodeFetch, 91 InPacketsMeter: miscInCodePacketsMeter, 92 InTrafficMeter: miscInCodeTrafficMeter, 93 OutPacketsMeter: miscOutCodePacketsMeter, 94 OutTrafficMeter: miscOutCodeTrafficMeter, 95 ServingTimeMeter: miscServingTimeCodeTimer, 96 Handle: handleGetCode, 97 }, 98 GetReceiptsMsg: { 99 Name: "receipts request", 100 MaxCount: MaxReceiptFetch, 101 InPacketsMeter: miscInReceiptPacketsMeter, 102 InTrafficMeter: miscInReceiptTrafficMeter, 103 OutPacketsMeter: miscOutReceiptPacketsMeter, 104 OutTrafficMeter: miscOutReceiptTrafficMeter, 105 ServingTimeMeter: miscServingTimeReceiptTimer, 106 Handle: handleGetReceipts, 107 }, 108 GetProofsV2Msg: { 109 Name: "les/2 proofs request", 110 MaxCount: MaxProofsFetch, 111 InPacketsMeter: miscInTrieProofPacketsMeter, 112 InTrafficMeter: miscInTrieProofTrafficMeter, 113 OutPacketsMeter: miscOutTrieProofPacketsMeter, 114 OutTrafficMeter: miscOutTrieProofTrafficMeter, 115 ServingTimeMeter: miscServingTimeTrieProofTimer, 116 Handle: handleGetProofs, 117 }, 118 GetHelperTrieProofsMsg: { 119 Name: "helper trie proof request", 120 MaxCount: MaxHelperTrieProofsFetch, 121 InPacketsMeter: miscInHelperTriePacketsMeter, 122 InTrafficMeter: miscInHelperTrieTrafficMeter, 123 OutPacketsMeter: miscOutHelperTriePacketsMeter, 124 OutTrafficMeter: miscOutHelperTrieTrafficMeter, 125 ServingTimeMeter: miscServingTimeHelperTrieTimer, 126 Handle: handleGetHelperTrieProofs, 127 }, 128 SendTxV2Msg: { 129 Name: "new transactions", 130 MaxCount: MaxTxSend, 131 InPacketsMeter: miscInTxsPacketsMeter, 132 InTrafficMeter: miscInTxsTrafficMeter, 133 OutPacketsMeter: miscOutTxsPacketsMeter, 134 OutTrafficMeter: miscOutTxsTrafficMeter, 135 ServingTimeMeter: miscServingTimeTxTimer, 136 Handle: handleSendTx, 137 }, 138 GetTxStatusMsg: { 139 Name: "transaction status query request", 140 MaxCount: MaxTxStatus, 141 InPacketsMeter: miscInTxStatusPacketsMeter, 142 InTrafficMeter: miscInTxStatusTrafficMeter, 143 OutPacketsMeter: miscOutTxStatusPacketsMeter, 144 OutTrafficMeter: miscOutTxStatusTrafficMeter, 145 ServingTimeMeter: miscServingTimeTxStatusTimer, 146 Handle: handleGetTxStatus, 147 }, 148 } 149 150 // handleGetBlockHeaders handles a block header request 151 func handleGetBlockHeaders(msg Decoder) (serveRequestFn, uint64, uint64, error) { 152 var r GetBlockHeadersPacket 153 if err := msg.Decode(&r); err != nil { 154 return nil, 0, 0, err 155 } 156 return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { 157 // Gather headers until the fetch or network limits is reached 158 var ( 159 bc = backend.BlockChain() 160 hashMode = r.Query.Origin.Hash != (common.Hash{}) 161 first = true 162 maxNonCanonical = uint64(100) 163 bytes common.StorageSize 164 headers []*types.Header 165 unknown bool 166 ) 167 for !unknown && len(headers) < int(r.Query.Amount) && bytes < softResponseLimit { 168 if !first && !waitOrStop() { 169 return nil 170 } 171 // Retrieve the next header satisfying the r 172 var origin *types.Header 173 if hashMode { 174 if first { 175 origin = bc.GetHeaderByHash(r.Query.Origin.Hash) 176 if origin != nil { 177 r.Query.Origin.Number = origin.Number.Uint64() 178 } 179 } else { 180 origin = bc.GetHeader(r.Query.Origin.Hash, r.Query.Origin.Number) 181 } 182 } else { 183 origin = bc.GetHeaderByNumber(r.Query.Origin.Number) 184 } 185 if origin == nil { 186 break 187 } 188 headers = append(headers, origin) 189 bytes += estHeaderRlpSize 190 191 // Advance to the next header of the r 192 switch { 193 case hashMode && r.Query.Reverse: 194 // Hash based traversal towards the genesis block 195 ancestor := r.Query.Skip + 1 196 if ancestor == 0 { 197 unknown = true 198 } else { 199 r.Query.Origin.Hash, r.Query.Origin.Number = bc.GetAncestor(r.Query.Origin.Hash, r.Query.Origin.Number, ancestor, &maxNonCanonical) 200 unknown = r.Query.Origin.Hash == common.Hash{} 201 } 202 case hashMode && !r.Query.Reverse: 203 // Hash based traversal towards the leaf block 204 var ( 205 current = origin.Number.Uint64() 206 next = current + r.Query.Skip + 1 207 ) 208 if next <= current { 209 infos, _ := json.Marshal(p.Peer.Info()) 210 p.Log().Warn("GetBlockHeaders skip overflow attack", "current", current, "skip", r.Query.Skip, "next", next, "attacker", string(infos)) 211 unknown = true 212 } else { 213 if header := bc.GetHeaderByNumber(next); header != nil { 214 nextHash := header.Hash() 215 expOldHash, _ := bc.GetAncestor(nextHash, next, r.Query.Skip+1, &maxNonCanonical) 216 if expOldHash == r.Query.Origin.Hash { 217 r.Query.Origin.Hash, r.Query.Origin.Number = nextHash, next 218 } else { 219 unknown = true 220 } 221 } else { 222 unknown = true 223 } 224 } 225 case r.Query.Reverse: 226 // Number based traversal towards the genesis block 227 if r.Query.Origin.Number >= r.Query.Skip+1 { 228 r.Query.Origin.Number -= r.Query.Skip + 1 229 } else { 230 unknown = true 231 } 232 233 case !r.Query.Reverse: 234 // Number based traversal towards the leaf block 235 r.Query.Origin.Number += r.Query.Skip + 1 236 } 237 first = false 238 } 239 return p.replyBlockHeaders(r.ReqID, headers) 240 }, r.ReqID, r.Query.Amount, nil 241 } 242 243 // handleGetBlockBodies handles a block body request 244 func handleGetBlockBodies(msg Decoder) (serveRequestFn, uint64, uint64, error) { 245 var r GetBlockBodiesPacket 246 if err := msg.Decode(&r); err != nil { 247 return nil, 0, 0, err 248 } 249 return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { 250 var ( 251 bytes int 252 bodies []rlp.RawValue 253 ) 254 bc := backend.BlockChain() 255 for i, hash := range r.Hashes { 256 if i != 0 && !waitOrStop() { 257 return nil 258 } 259 if bytes >= softResponseLimit { 260 break 261 } 262 body := bc.GetBodyRLP(hash) 263 if body == nil { 264 p.bumpInvalid() 265 continue 266 } 267 bodies = append(bodies, body) 268 bytes += len(body) 269 } 270 return p.replyBlockBodiesRLP(r.ReqID, bodies) 271 }, r.ReqID, uint64(len(r.Hashes)), nil 272 } 273 274 // handleGetCode handles a contract code request 275 func handleGetCode(msg Decoder) (serveRequestFn, uint64, uint64, error) { 276 var r GetCodePacket 277 if err := msg.Decode(&r); err != nil { 278 return nil, 0, 0, err 279 } 280 return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { 281 var ( 282 bytes int 283 data [][]byte 284 ) 285 bc := backend.BlockChain() 286 for i, request := range r.Reqs { 287 if i != 0 && !waitOrStop() { 288 return nil 289 } 290 // Look up the root hash belonging to the request 291 header := bc.GetHeaderByHash(request.BHash) 292 if header == nil { 293 p.Log().Warn("Failed to retrieve associate header for code", "hash", request.BHash) 294 p.bumpInvalid() 295 continue 296 } 297 // Refuse to search stale state data in the database since looking for 298 // a non-exist key is kind of expensive. 299 local := bc.CurrentHeader().Number.Uint64() 300 if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local { 301 p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local) 302 p.bumpInvalid() 303 continue 304 } 305 triedb := bc.StateCache().TrieDB() 306 307 account, err := getAccount(triedb, header.Root, common.BytesToHash(request.AccKey)) 308 if err != nil { 309 p.Log().Warn("Failed to retrieve account for code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err) 310 p.bumpInvalid() 311 continue 312 } 313 code, err := bc.StateCache().ContractCode(common.BytesToHash(request.AccKey), common.BytesToHash(account.CodeHash)) 314 if err != nil { 315 p.Log().Warn("Failed to retrieve account code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "codehash", common.BytesToHash(account.CodeHash), "err", err) 316 continue 317 } 318 // Accumulate the code and abort if enough data was retrieved 319 data = append(data, code) 320 if bytes += len(code); bytes >= softResponseLimit { 321 break 322 } 323 } 324 return p.replyCode(r.ReqID, data) 325 }, r.ReqID, uint64(len(r.Reqs)), nil 326 } 327 328 // handleGetReceipts handles a block receipts request 329 func handleGetReceipts(msg Decoder) (serveRequestFn, uint64, uint64, error) { 330 var r GetReceiptsPacket 331 if err := msg.Decode(&r); err != nil { 332 return nil, 0, 0, err 333 } 334 return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { 335 var ( 336 bytes int 337 receipts []rlp.RawValue 338 ) 339 bc := backend.BlockChain() 340 for i, hash := range r.Hashes { 341 if i != 0 && !waitOrStop() { 342 return nil 343 } 344 if bytes >= softResponseLimit { 345 break 346 } 347 // Retrieve the requested block's receipts, skipping if unknown to us 348 results := bc.GetReceiptsByHash(hash) 349 if results == nil { 350 if header := bc.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { 351 p.bumpInvalid() 352 continue 353 } 354 } 355 // If known, encode and queue for response packet 356 if encoded, err := rlp.EncodeToBytes(results); err != nil { 357 log.Error("Failed to encode receipt", "err", err) 358 } else { 359 receipts = append(receipts, encoded) 360 bytes += len(encoded) 361 } 362 } 363 return p.replyReceiptsRLP(r.ReqID, receipts) 364 }, r.ReqID, uint64(len(r.Hashes)), nil 365 } 366 367 // handleGetProofs handles a proof request 368 func handleGetProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) { 369 var r GetProofsPacket 370 if err := msg.Decode(&r); err != nil { 371 return nil, 0, 0, err 372 } 373 return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { 374 var ( 375 lastBHash common.Hash 376 root common.Hash 377 header *types.Header 378 err error 379 ) 380 bc := backend.BlockChain() 381 nodes := light.NewNodeSet() 382 383 for i, request := range r.Reqs { 384 if i != 0 && !waitOrStop() { 385 return nil 386 } 387 // Look up the root hash belonging to the request 388 if request.BHash != lastBHash { 389 root, lastBHash = common.Hash{}, request.BHash 390 391 if header = bc.GetHeaderByHash(request.BHash); header == nil { 392 p.Log().Warn("Failed to retrieve header for proof", "hash", request.BHash) 393 p.bumpInvalid() 394 continue 395 } 396 // Refuse to search stale state data in the database since looking for 397 // a non-exist key is kind of expensive. 398 local := bc.CurrentHeader().Number.Uint64() 399 if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local { 400 p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local) 401 p.bumpInvalid() 402 continue 403 } 404 root = header.Root 405 } 406 // If a header lookup failed (non existent), ignore subsequent requests for the same header 407 if root == (common.Hash{}) { 408 p.bumpInvalid() 409 continue 410 } 411 // Open the account or storage trie for the request 412 statedb := bc.StateCache() 413 414 var trie state.Trie 415 switch len(request.AccKey) { 416 case 0: 417 // No account key specified, open an account trie 418 trie, err = statedb.OpenTrie(root) 419 if trie == nil || err != nil { 420 p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "root", root, "err", err) 421 continue 422 } 423 default: 424 // Account key specified, open a storage trie 425 account, err := getAccount(statedb.TrieDB(), root, common.BytesToHash(request.AccKey)) 426 if err != nil { 427 p.Log().Warn("Failed to retrieve account for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err) 428 p.bumpInvalid() 429 continue 430 } 431 trie, err = statedb.OpenStorageTrie(common.BytesToHash(request.AccKey), account.Root) 432 if trie == nil || err != nil { 433 p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "root", account.Root, "err", err) 434 continue 435 } 436 } 437 // Prove the user's request from the account or stroage trie 438 if err := trie.Prove(request.Key, request.FromLevel, nodes); err != nil { 439 p.Log().Warn("Failed to prove state request", "block", header.Number, "hash", header.Hash(), "err", err) 440 continue 441 } 442 if nodes.DataSize() >= softResponseLimit { 443 break 444 } 445 } 446 return p.replyProofsV2(r.ReqID, nodes.NodeList()) 447 }, r.ReqID, uint64(len(r.Reqs)), nil 448 } 449 450 // handleGetHelperTrieProofs handles a helper trie proof request 451 func handleGetHelperTrieProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) { 452 var r GetHelperTrieProofsPacket 453 if err := msg.Decode(&r); err != nil { 454 return nil, 0, 0, err 455 } 456 return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { 457 var ( 458 lastIdx uint64 459 lastType uint 460 auxTrie *trie.Trie 461 auxBytes int 462 auxData [][]byte 463 ) 464 bc := backend.BlockChain() 465 nodes := light.NewNodeSet() 466 for i, request := range r.Reqs { 467 if i != 0 && !waitOrStop() { 468 return nil 469 } 470 if auxTrie == nil || request.Type != lastType || request.TrieIdx != lastIdx { 471 lastType, lastIdx = request.Type, request.TrieIdx 472 auxTrie = backend.GetHelperTrie(request.Type, request.TrieIdx) 473 } 474 if auxTrie == nil { 475 return nil 476 } 477 // TODO(rjl493456442) short circuit if the proving is failed. 478 // The original client side code has a dirty hack to retrieve 479 // the headers with no valid proof. Keep the compatibility for 480 // legacy les protocol and drop this hack when the les2/3 are 481 // not supported. 482 err := auxTrie.Prove(request.Key, request.FromLevel, nodes) 483 if p.version >= lpv4 && err != nil { 484 return nil 485 } 486 if request.Type == htCanonical && request.AuxReq == htAuxHeader && len(request.Key) == 8 { 487 header := bc.GetHeaderByNumber(binary.BigEndian.Uint64(request.Key)) 488 data, err := rlp.EncodeToBytes(header) 489 if err != nil { 490 log.Error("Failed to encode header", "err", err) 491 return nil 492 } 493 auxData = append(auxData, data) 494 auxBytes += len(data) 495 } 496 if nodes.DataSize()+auxBytes >= softResponseLimit { 497 break 498 } 499 } 500 return p.replyHelperTrieProofs(r.ReqID, HelperTrieResps{Proofs: nodes.NodeList(), AuxData: auxData}) 501 }, r.ReqID, uint64(len(r.Reqs)), nil 502 } 503 504 // handleSendTx handles a transaction propagation request 505 func handleSendTx(msg Decoder) (serveRequestFn, uint64, uint64, error) { 506 var r SendTxPacket 507 if err := msg.Decode(&r); err != nil { 508 return nil, 0, 0, err 509 } 510 amount := uint64(len(r.Txs)) 511 return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { 512 stats := make([]light.TxStatus, len(r.Txs)) 513 for i, tx := range r.Txs { 514 if i != 0 && !waitOrStop() { 515 return nil 516 } 517 hash := tx.Hash() 518 stats[i] = txStatus(backend, hash) 519 if stats[i].Status == core.TxStatusUnknown { 520 addFn := backend.TxPool().AddRemotes 521 // Add txs synchronously for testing purpose 522 if backend.AddTxsSync() { 523 addFn = backend.TxPool().AddRemotesSync 524 } 525 if errs := addFn([]*types.Transaction{tx}); errs[0] != nil { 526 stats[i].Error = errs[0].Error() 527 continue 528 } 529 stats[i] = txStatus(backend, hash) 530 } 531 } 532 return p.replyTxStatus(r.ReqID, stats) 533 }, r.ReqID, amount, nil 534 } 535 536 // handleGetTxStatus handles a transaction status query 537 func handleGetTxStatus(msg Decoder) (serveRequestFn, uint64, uint64, error) { 538 var r GetTxStatusPacket 539 if err := msg.Decode(&r); err != nil { 540 return nil, 0, 0, err 541 } 542 return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { 543 stats := make([]light.TxStatus, len(r.Hashes)) 544 for i, hash := range r.Hashes { 545 if i != 0 && !waitOrStop() { 546 return nil 547 } 548 stats[i] = txStatus(backend, hash) 549 } 550 return p.replyTxStatus(r.ReqID, stats) 551 }, r.ReqID, uint64(len(r.Hashes)), nil 552 } 553 554 // txStatus returns the status of a specified transaction. 555 func txStatus(b serverBackend, hash common.Hash) light.TxStatus { 556 var stat light.TxStatus 557 // Looking the transaction in txpool first. 558 stat.Status = b.TxPool().Status([]common.Hash{hash})[0] 559 560 // If the transaction is unknown to the pool, try looking it up locally. 561 if stat.Status == core.TxStatusUnknown { 562 lookup := b.BlockChain().GetTransactionLookup(hash) 563 if lookup != nil { 564 stat.Status = core.TxStatusIncluded 565 stat.Lookup = lookup 566 } 567 } 568 return stat 569 }