github.com/bloxroute-labs/bor@v0.1.4/les/odr_requests.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 "encoding/binary" 21 "errors" 22 "fmt" 23 24 "github.com/maticnetwork/bor/common" 25 "github.com/maticnetwork/bor/core/rawdb" 26 "github.com/maticnetwork/bor/core/types" 27 "github.com/maticnetwork/bor/crypto" 28 "github.com/maticnetwork/bor/ethdb" 29 "github.com/maticnetwork/bor/light" 30 "github.com/maticnetwork/bor/log" 31 "github.com/maticnetwork/bor/rlp" 32 "github.com/maticnetwork/bor/trie" 33 ) 34 35 var ( 36 errInvalidMessageType = errors.New("invalid message type") 37 errInvalidEntryCount = errors.New("invalid number of response entries") 38 errHeaderUnavailable = errors.New("header unavailable") 39 errTxHashMismatch = errors.New("transaction hash mismatch") 40 errUncleHashMismatch = errors.New("uncle hash mismatch") 41 errReceiptHashMismatch = errors.New("receipt hash mismatch") 42 errDataHashMismatch = errors.New("data hash mismatch") 43 errCHTHashMismatch = errors.New("cht hash mismatch") 44 errCHTNumberMismatch = errors.New("cht number mismatch") 45 errUselessNodes = errors.New("useless nodes in merkle proof nodeset") 46 ) 47 48 type LesOdrRequest interface { 49 GetCost(*peer) uint64 50 CanSend(*peer) bool 51 Request(uint64, *peer) error 52 Validate(ethdb.Database, *Msg) error 53 } 54 55 func LesRequest(req light.OdrRequest) LesOdrRequest { 56 switch r := req.(type) { 57 case *light.BlockRequest: 58 return (*BlockRequest)(r) 59 case *light.ReceiptsRequest: 60 return (*ReceiptsRequest)(r) 61 case *light.TrieRequest: 62 return (*TrieRequest)(r) 63 case *light.CodeRequest: 64 return (*CodeRequest)(r) 65 case *light.ChtRequest: 66 return (*ChtRequest)(r) 67 case *light.BloomRequest: 68 return (*BloomRequest)(r) 69 case *light.TxStatusRequest: 70 return (*TxStatusRequest)(r) 71 default: 72 return nil 73 } 74 } 75 76 // BlockRequest is the ODR request type for block bodies 77 type BlockRequest light.BlockRequest 78 79 // GetCost returns the cost of the given ODR request according to the serving 80 // peer's cost table (implementation of LesOdrRequest) 81 func (r *BlockRequest) GetCost(peer *peer) uint64 { 82 return peer.GetRequestCost(GetBlockBodiesMsg, 1) 83 } 84 85 // CanSend tells if a certain peer is suitable for serving the given request 86 func (r *BlockRequest) CanSend(peer *peer) bool { 87 return peer.HasBlock(r.Hash, r.Number, false) 88 } 89 90 // Request sends an ODR request to the LES network (implementation of LesOdrRequest) 91 func (r *BlockRequest) Request(reqID uint64, peer *peer) error { 92 peer.Log().Debug("Requesting block body", "hash", r.Hash) 93 return peer.RequestBodies(reqID, r.GetCost(peer), []common.Hash{r.Hash}) 94 } 95 96 // Valid processes an ODR request reply message from the LES network 97 // returns true and stores results in memory if the message was a valid reply 98 // to the request (implementation of LesOdrRequest) 99 func (r *BlockRequest) Validate(db ethdb.Database, msg *Msg) error { 100 log.Debug("Validating block body", "hash", r.Hash) 101 102 // Ensure we have a correct message with a single block body 103 if msg.MsgType != MsgBlockBodies { 104 return errInvalidMessageType 105 } 106 bodies := msg.Obj.([]*types.Body) 107 if len(bodies) != 1 { 108 return errInvalidEntryCount 109 } 110 body := bodies[0] 111 112 // Retrieve our stored header and validate block content against it 113 header := rawdb.ReadHeader(db, r.Hash, r.Number) 114 if header == nil { 115 return errHeaderUnavailable 116 } 117 if header.TxHash != types.DeriveSha(types.Transactions(body.Transactions)) { 118 return errTxHashMismatch 119 } 120 if header.UncleHash != types.CalcUncleHash(body.Uncles) { 121 return errUncleHashMismatch 122 } 123 // Validations passed, encode and store RLP 124 data, err := rlp.EncodeToBytes(body) 125 if err != nil { 126 return err 127 } 128 r.Rlp = data 129 return nil 130 } 131 132 // ReceiptsRequest is the ODR request type for block receipts by block hash 133 type ReceiptsRequest light.ReceiptsRequest 134 135 // GetCost returns the cost of the given ODR request according to the serving 136 // peer's cost table (implementation of LesOdrRequest) 137 func (r *ReceiptsRequest) GetCost(peer *peer) uint64 { 138 return peer.GetRequestCost(GetReceiptsMsg, 1) 139 } 140 141 // CanSend tells if a certain peer is suitable for serving the given request 142 func (r *ReceiptsRequest) CanSend(peer *peer) bool { 143 return peer.HasBlock(r.Hash, r.Number, false) 144 } 145 146 // Request sends an ODR request to the LES network (implementation of LesOdrRequest) 147 func (r *ReceiptsRequest) Request(reqID uint64, peer *peer) error { 148 peer.Log().Debug("Requesting block receipts", "hash", r.Hash) 149 return peer.RequestReceipts(reqID, r.GetCost(peer), []common.Hash{r.Hash}) 150 } 151 152 // Valid processes an ODR request reply message from the LES network 153 // returns true and stores results in memory if the message was a valid reply 154 // to the request (implementation of LesOdrRequest) 155 func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error { 156 log.Debug("Validating block receipts", "hash", r.Hash) 157 158 // Ensure we have a correct message with a single block receipt 159 if msg.MsgType != MsgReceipts { 160 return errInvalidMessageType 161 } 162 receipts := msg.Obj.([]types.Receipts) 163 if len(receipts) != 1 { 164 return errInvalidEntryCount 165 } 166 receipt := receipts[0] 167 168 // Retrieve our stored header and validate receipt content against it 169 if r.Header == nil { 170 r.Header = rawdb.ReadHeader(db, r.Hash, r.Number) 171 } 172 if r.Header == nil { 173 return errHeaderUnavailable 174 } 175 if r.Header.ReceiptHash != types.DeriveSha(receipt) { 176 return errReceiptHashMismatch 177 } 178 // Validations passed, store and return 179 r.Receipts = receipt 180 return nil 181 } 182 183 type ProofReq struct { 184 BHash common.Hash 185 AccKey, Key []byte 186 FromLevel uint 187 } 188 189 // ODR request type for state/storage trie entries, see LesOdrRequest interface 190 type TrieRequest light.TrieRequest 191 192 // GetCost returns the cost of the given ODR request according to the serving 193 // peer's cost table (implementation of LesOdrRequest) 194 func (r *TrieRequest) GetCost(peer *peer) uint64 { 195 return peer.GetRequestCost(GetProofsV2Msg, 1) 196 } 197 198 // CanSend tells if a certain peer is suitable for serving the given request 199 func (r *TrieRequest) CanSend(peer *peer) bool { 200 return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber, true) 201 } 202 203 // Request sends an ODR request to the LES network (implementation of LesOdrRequest) 204 func (r *TrieRequest) Request(reqID uint64, peer *peer) error { 205 peer.Log().Debug("Requesting trie proof", "root", r.Id.Root, "key", r.Key) 206 req := ProofReq{ 207 BHash: r.Id.BlockHash, 208 AccKey: r.Id.AccKey, 209 Key: r.Key, 210 } 211 return peer.RequestProofs(reqID, r.GetCost(peer), []ProofReq{req}) 212 } 213 214 // Valid processes an ODR request reply message from the LES network 215 // returns true and stores results in memory if the message was a valid reply 216 // to the request (implementation of LesOdrRequest) 217 func (r *TrieRequest) Validate(db ethdb.Database, msg *Msg) error { 218 log.Debug("Validating trie proof", "root", r.Id.Root, "key", r.Key) 219 220 if msg.MsgType != MsgProofsV2 { 221 return errInvalidMessageType 222 } 223 proofs := msg.Obj.(light.NodeList) 224 // Verify the proof and store if checks out 225 nodeSet := proofs.NodeSet() 226 reads := &readTraceDB{db: nodeSet} 227 if _, _, err := trie.VerifyProof(r.Id.Root, r.Key, reads); err != nil { 228 return fmt.Errorf("merkle proof verification failed: %v", err) 229 } 230 // check if all nodes have been read by VerifyProof 231 if len(reads.reads) != nodeSet.KeyCount() { 232 return errUselessNodes 233 } 234 r.Proof = nodeSet 235 return nil 236 } 237 238 type CodeReq struct { 239 BHash common.Hash 240 AccKey []byte 241 } 242 243 // ODR request type for node data (used for retrieving contract code), see LesOdrRequest interface 244 type CodeRequest light.CodeRequest 245 246 // GetCost returns the cost of the given ODR request according to the serving 247 // peer's cost table (implementation of LesOdrRequest) 248 func (r *CodeRequest) GetCost(peer *peer) uint64 { 249 return peer.GetRequestCost(GetCodeMsg, 1) 250 } 251 252 // CanSend tells if a certain peer is suitable for serving the given request 253 func (r *CodeRequest) CanSend(peer *peer) bool { 254 return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber, true) 255 } 256 257 // Request sends an ODR request to the LES network (implementation of LesOdrRequest) 258 func (r *CodeRequest) Request(reqID uint64, peer *peer) error { 259 peer.Log().Debug("Requesting code data", "hash", r.Hash) 260 req := CodeReq{ 261 BHash: r.Id.BlockHash, 262 AccKey: r.Id.AccKey, 263 } 264 return peer.RequestCode(reqID, r.GetCost(peer), []CodeReq{req}) 265 } 266 267 // Valid processes an ODR request reply message from the LES network 268 // returns true and stores results in memory if the message was a valid reply 269 // to the request (implementation of LesOdrRequest) 270 func (r *CodeRequest) Validate(db ethdb.Database, msg *Msg) error { 271 log.Debug("Validating code data", "hash", r.Hash) 272 273 // Ensure we have a correct message with a single code element 274 if msg.MsgType != MsgCode { 275 return errInvalidMessageType 276 } 277 reply := msg.Obj.([][]byte) 278 if len(reply) != 1 { 279 return errInvalidEntryCount 280 } 281 data := reply[0] 282 283 // Verify the data and store if checks out 284 if hash := crypto.Keccak256Hash(data); r.Hash != hash { 285 return errDataHashMismatch 286 } 287 r.Data = data 288 return nil 289 } 290 291 const ( 292 // helper trie type constants 293 htCanonical = iota // Canonical hash trie 294 htBloomBits // BloomBits trie 295 296 // applicable for all helper trie requests 297 auxRoot = 1 298 // applicable for htCanonical 299 auxHeader = 2 300 ) 301 302 type HelperTrieReq struct { 303 Type uint 304 TrieIdx uint64 305 Key []byte 306 FromLevel, AuxReq uint 307 } 308 309 type HelperTrieResps struct { // describes all responses, not just a single one 310 Proofs light.NodeList 311 AuxData [][]byte 312 } 313 314 // ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface 315 type ChtRequest light.ChtRequest 316 317 // GetCost returns the cost of the given ODR request according to the serving 318 // peer's cost table (implementation of LesOdrRequest) 319 func (r *ChtRequest) GetCost(peer *peer) uint64 { 320 return peer.GetRequestCost(GetHelperTrieProofsMsg, 1) 321 } 322 323 // CanSend tells if a certain peer is suitable for serving the given request 324 func (r *ChtRequest) CanSend(peer *peer) bool { 325 peer.lock.RLock() 326 defer peer.lock.RUnlock() 327 328 if r.Untrusted { 329 return peer.headInfo.Number >= r.BlockNum && peer.id == r.PeerId 330 } else { 331 return peer.headInfo.Number >= r.Config.ChtConfirms && r.ChtNum <= (peer.headInfo.Number-r.Config.ChtConfirms)/r.Config.ChtSize 332 } 333 } 334 335 // Request sends an ODR request to the LES network (implementation of LesOdrRequest) 336 func (r *ChtRequest) Request(reqID uint64, peer *peer) error { 337 peer.Log().Debug("Requesting CHT", "cht", r.ChtNum, "block", r.BlockNum) 338 var encNum [8]byte 339 binary.BigEndian.PutUint64(encNum[:], r.BlockNum) 340 req := HelperTrieReq{ 341 Type: htCanonical, 342 TrieIdx: r.ChtNum, 343 Key: encNum[:], 344 AuxReq: auxHeader, 345 } 346 return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), []HelperTrieReq{req}) 347 } 348 349 // Valid processes an ODR request reply message from the LES network 350 // returns true and stores results in memory if the message was a valid reply 351 // to the request (implementation of LesOdrRequest) 352 func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error { 353 log.Debug("Validating CHT", "cht", r.ChtNum, "block", r.BlockNum) 354 355 if msg.MsgType != MsgHelperTrieProofs { 356 return errInvalidMessageType 357 } 358 resp := msg.Obj.(HelperTrieResps) 359 if len(resp.AuxData) != 1 { 360 return errInvalidEntryCount 361 } 362 nodeSet := resp.Proofs.NodeSet() 363 headerEnc := resp.AuxData[0] 364 if len(headerEnc) == 0 { 365 return errHeaderUnavailable 366 } 367 header := new(types.Header) 368 if err := rlp.DecodeBytes(headerEnc, header); err != nil { 369 return errHeaderUnavailable 370 } 371 372 // Verify the CHT 373 // Note: For untrusted CHT request, there is no proof response but 374 // header data. 375 var node light.ChtNode 376 if !r.Untrusted { 377 var encNumber [8]byte 378 binary.BigEndian.PutUint64(encNumber[:], r.BlockNum) 379 380 reads := &readTraceDB{db: nodeSet} 381 value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], reads) 382 if err != nil { 383 return fmt.Errorf("merkle proof verification failed: %v", err) 384 } 385 if len(reads.reads) != nodeSet.KeyCount() { 386 return errUselessNodes 387 } 388 389 if err := rlp.DecodeBytes(value, &node); err != nil { 390 return err 391 } 392 if node.Hash != header.Hash() { 393 return errCHTHashMismatch 394 } 395 if r.BlockNum != header.Number.Uint64() { 396 return errCHTNumberMismatch 397 } 398 } 399 // Verifications passed, store and return 400 r.Header = header 401 r.Proof = nodeSet 402 r.Td = node.Td // For untrusted request, td here is nil, todo improve the les/2 protocol 403 404 return nil 405 } 406 407 type BloomReq struct { 408 BloomTrieNum, BitIdx, SectionIndex, FromLevel uint64 409 } 410 411 // ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface 412 type BloomRequest light.BloomRequest 413 414 // GetCost returns the cost of the given ODR request according to the serving 415 // peer's cost table (implementation of LesOdrRequest) 416 func (r *BloomRequest) GetCost(peer *peer) uint64 { 417 return peer.GetRequestCost(GetHelperTrieProofsMsg, len(r.SectionIndexList)) 418 } 419 420 // CanSend tells if a certain peer is suitable for serving the given request 421 func (r *BloomRequest) CanSend(peer *peer) bool { 422 peer.lock.RLock() 423 defer peer.lock.RUnlock() 424 425 if peer.version < lpv2 { 426 return false 427 } 428 return peer.headInfo.Number >= r.Config.BloomTrieConfirms && r.BloomTrieNum <= (peer.headInfo.Number-r.Config.BloomTrieConfirms)/r.Config.BloomTrieSize 429 } 430 431 // Request sends an ODR request to the LES network (implementation of LesOdrRequest) 432 func (r *BloomRequest) Request(reqID uint64, peer *peer) error { 433 peer.Log().Debug("Requesting BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIndexList) 434 reqs := make([]HelperTrieReq, len(r.SectionIndexList)) 435 436 var encNumber [10]byte 437 binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx)) 438 439 for i, sectionIdx := range r.SectionIndexList { 440 binary.BigEndian.PutUint64(encNumber[2:], sectionIdx) 441 reqs[i] = HelperTrieReq{ 442 Type: htBloomBits, 443 TrieIdx: r.BloomTrieNum, 444 Key: common.CopyBytes(encNumber[:]), 445 } 446 } 447 return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), reqs) 448 } 449 450 // Valid processes an ODR request reply message from the LES network 451 // returns true and stores results in memory if the message was a valid reply 452 // to the request (implementation of LesOdrRequest) 453 func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error { 454 log.Debug("Validating BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIndexList) 455 456 // Ensure we have a correct message with a single proof element 457 if msg.MsgType != MsgHelperTrieProofs { 458 return errInvalidMessageType 459 } 460 resps := msg.Obj.(HelperTrieResps) 461 proofs := resps.Proofs 462 nodeSet := proofs.NodeSet() 463 reads := &readTraceDB{db: nodeSet} 464 465 r.BloomBits = make([][]byte, len(r.SectionIndexList)) 466 467 // Verify the proofs 468 var encNumber [10]byte 469 binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx)) 470 471 for i, idx := range r.SectionIndexList { 472 binary.BigEndian.PutUint64(encNumber[2:], idx) 473 value, _, err := trie.VerifyProof(r.BloomTrieRoot, encNumber[:], reads) 474 if err != nil { 475 return err 476 } 477 r.BloomBits[i] = value 478 } 479 480 if len(reads.reads) != nodeSet.KeyCount() { 481 return errUselessNodes 482 } 483 r.Proofs = nodeSet 484 return nil 485 } 486 487 // TxStatusRequest is the ODR request type for transaction status 488 type TxStatusRequest light.TxStatusRequest 489 490 // GetCost returns the cost of the given ODR request according to the serving 491 // peer's cost table (implementation of LesOdrRequest) 492 func (r *TxStatusRequest) GetCost(peer *peer) uint64 { 493 return peer.GetRequestCost(GetTxStatusMsg, len(r.Hashes)) 494 } 495 496 // CanSend tells if a certain peer is suitable for serving the given request 497 func (r *TxStatusRequest) CanSend(peer *peer) bool { 498 return peer.version >= lpv2 499 } 500 501 // Request sends an ODR request to the LES network (implementation of LesOdrRequest) 502 func (r *TxStatusRequest) Request(reqID uint64, peer *peer) error { 503 peer.Log().Debug("Requesting transaction status", "count", len(r.Hashes)) 504 return peer.RequestTxStatus(reqID, r.GetCost(peer), r.Hashes) 505 } 506 507 // Valid processes an ODR request reply message from the LES network 508 // returns true and stores results in memory if the message was a valid reply 509 // to the request (implementation of LesOdrRequest) 510 func (r *TxStatusRequest) Validate(db ethdb.Database, msg *Msg) error { 511 log.Debug("Validating transaction status", "count", len(r.Hashes)) 512 513 // Ensure we have a correct message with a single block body 514 if msg.MsgType != MsgTxStatus { 515 return errInvalidMessageType 516 } 517 status := msg.Obj.([]light.TxStatus) 518 if len(status) != len(r.Hashes) { 519 return errInvalidEntryCount 520 } 521 r.Status = status 522 return nil 523 } 524 525 // readTraceDB stores the keys of database reads. We use this to check that received node 526 // sets contain only the trie nodes necessary to make proofs pass. 527 type readTraceDB struct { 528 db ethdb.KeyValueReader 529 reads map[string]struct{} 530 } 531 532 // Get returns a stored node 533 func (db *readTraceDB) Get(k []byte) ([]byte, error) { 534 if db.reads == nil { 535 db.reads = make(map[string]struct{}) 536 } 537 db.reads[string(k)] = struct{}{} 538 return db.db.Get(k) 539 } 540 541 // Has returns true if the node set contains the given key 542 func (db *readTraceDB) Has(key []byte) (bool, error) { 543 _, err := db.Get(key) 544 return err == nil, nil 545 }