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