123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567 |
- // Copyright 2016 The go-ethereum Authors
- // This file is part of the go-ethereum library.
- //
- // The go-ethereum library is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Lesser General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // The go-ethereum library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
- // Package light implements on-demand retrieval capable state and chain objects
- // for the Ethereum Light Client.
- package les
- import (
- "encoding/binary"
- "errors"
- "fmt"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/light"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/trie"
- )
- var (
- errInvalidMessageType = errors.New("invalid message type")
- errInvalidEntryCount = errors.New("invalid number of response entries")
- errHeaderUnavailable = errors.New("header unavailable")
- errTxHashMismatch = errors.New("transaction hash mismatch")
- errUncleHashMismatch = errors.New("uncle hash mismatch")
- errReceiptHashMismatch = errors.New("receipt hash mismatch")
- errDataHashMismatch = errors.New("data hash mismatch")
- errCHTHashMismatch = errors.New("cht hash mismatch")
- errCHTNumberMismatch = errors.New("cht number mismatch")
- errUselessNodes = errors.New("useless nodes in merkle proof nodeset")
- )
- type LesOdrRequest interface {
- GetCost(*peer) uint64
- CanSend(*peer) bool
- Request(uint64, *peer) error
- Validate(ethdb.Database, *Msg) error
- }
- func LesRequest(req light.OdrRequest) LesOdrRequest {
- switch r := req.(type) {
- case *light.BlockRequest:
- return (*BlockRequest)(r)
- case *light.ReceiptsRequest:
- return (*ReceiptsRequest)(r)
- case *light.TrieRequest:
- return (*TrieRequest)(r)
- case *light.CodeRequest:
- return (*CodeRequest)(r)
- case *light.ChtRequest:
- return (*ChtRequest)(r)
- case *light.BloomRequest:
- return (*BloomRequest)(r)
- default:
- return nil
- }
- }
- // BlockRequest is the ODR request type for block bodies
- type BlockRequest light.BlockRequest
- // GetCost returns the cost of the given ODR request according to the serving
- // peer's cost table (implementation of LesOdrRequest)
- func (r *BlockRequest) GetCost(peer *peer) uint64 {
- return peer.GetRequestCost(GetBlockBodiesMsg, 1)
- }
- // CanSend tells if a certain peer is suitable for serving the given request
- func (r *BlockRequest) CanSend(peer *peer) bool {
- return peer.HasBlock(r.Hash, r.Number)
- }
- // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
- func (r *BlockRequest) Request(reqID uint64, peer *peer) error {
- peer.Log().Debug("Requesting block body", "hash", r.Hash)
- return peer.RequestBodies(reqID, r.GetCost(peer), []common.Hash{r.Hash})
- }
- // Valid processes an ODR request reply message from the LES network
- // returns true and stores results in memory if the message was a valid reply
- // to the request (implementation of LesOdrRequest)
- func (r *BlockRequest) Validate(db ethdb.Database, msg *Msg) error {
- log.Debug("Validating block body", "hash", r.Hash)
- // Ensure we have a correct message with a single block body
- if msg.MsgType != MsgBlockBodies {
- return errInvalidMessageType
- }
- bodies := msg.Obj.([]*types.Body)
- if len(bodies) != 1 {
- return errInvalidEntryCount
- }
- body := bodies[0]
- // Retrieve our stored header and validate block content against it
- header := rawdb.ReadHeader(db, r.Hash, r.Number)
- if header == nil {
- return errHeaderUnavailable
- }
- if header.TxHash != types.DeriveSha(types.Transactions(body.Transactions)) {
- return errTxHashMismatch
- }
- if header.UncleHash != types.CalcUncleHash(body.Uncles) {
- return errUncleHashMismatch
- }
- // Validations passed, encode and store RLP
- data, err := rlp.EncodeToBytes(body)
- if err != nil {
- return err
- }
- r.Rlp = data
- return nil
- }
- // ReceiptsRequest is the ODR request type for block receipts by block hash
- type ReceiptsRequest light.ReceiptsRequest
- // GetCost returns the cost of the given ODR request according to the serving
- // peer's cost table (implementation of LesOdrRequest)
- func (r *ReceiptsRequest) GetCost(peer *peer) uint64 {
- return peer.GetRequestCost(GetReceiptsMsg, 1)
- }
- // CanSend tells if a certain peer is suitable for serving the given request
- func (r *ReceiptsRequest) CanSend(peer *peer) bool {
- return peer.HasBlock(r.Hash, r.Number)
- }
- // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
- func (r *ReceiptsRequest) Request(reqID uint64, peer *peer) error {
- peer.Log().Debug("Requesting block receipts", "hash", r.Hash)
- return peer.RequestReceipts(reqID, r.GetCost(peer), []common.Hash{r.Hash})
- }
- // Valid processes an ODR request reply message from the LES network
- // returns true and stores results in memory if the message was a valid reply
- // to the request (implementation of LesOdrRequest)
- func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error {
- log.Debug("Validating block receipts", "hash", r.Hash)
- // Ensure we have a correct message with a single block receipt
- if msg.MsgType != MsgReceipts {
- return errInvalidMessageType
- }
- receipts := msg.Obj.([]types.Receipts)
- if len(receipts) != 1 {
- return errInvalidEntryCount
- }
- receipt := receipts[0]
- // Retrieve our stored header and validate receipt content against it
- header := rawdb.ReadHeader(db, r.Hash, r.Number)
- if header == nil {
- return errHeaderUnavailable
- }
- if header.ReceiptHash != types.DeriveSha(receipt) {
- return errReceiptHashMismatch
- }
- // Validations passed, store and return
- r.Receipts = receipt
- return nil
- }
- type ProofReq struct {
- BHash common.Hash
- AccKey, Key []byte
- FromLevel uint
- }
- // ODR request type for state/storage trie entries, see LesOdrRequest interface
- type TrieRequest light.TrieRequest
- // GetCost returns the cost of the given ODR request according to the serving
- // peer's cost table (implementation of LesOdrRequest)
- func (r *TrieRequest) GetCost(peer *peer) uint64 {
- switch peer.version {
- case lpv1:
- return peer.GetRequestCost(GetProofsV1Msg, 1)
- case lpv2:
- return peer.GetRequestCost(GetProofsV2Msg, 1)
- default:
- panic(nil)
- }
- }
- // CanSend tells if a certain peer is suitable for serving the given request
- func (r *TrieRequest) CanSend(peer *peer) bool {
- return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber)
- }
- // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
- func (r *TrieRequest) Request(reqID uint64, peer *peer) error {
- peer.Log().Debug("Requesting trie proof", "root", r.Id.Root, "key", r.Key)
- req := ProofReq{
- BHash: r.Id.BlockHash,
- AccKey: r.Id.AccKey,
- Key: r.Key,
- }
- return peer.RequestProofs(reqID, r.GetCost(peer), []ProofReq{req})
- }
- // Valid processes an ODR request reply message from the LES network
- // returns true and stores results in memory if the message was a valid reply
- // to the request (implementation of LesOdrRequest)
- func (r *TrieRequest) Validate(db ethdb.Database, msg *Msg) error {
- log.Debug("Validating trie proof", "root", r.Id.Root, "key", r.Key)
- switch msg.MsgType {
- case MsgProofsV1:
- proofs := msg.Obj.([]light.NodeList)
- if len(proofs) != 1 {
- return errInvalidEntryCount
- }
- nodeSet := proofs[0].NodeSet()
- // Verify the proof and store if checks out
- if _, _, err := trie.VerifyProof(r.Id.Root, r.Key, nodeSet); err != nil {
- return fmt.Errorf("merkle proof verification failed: %v", err)
- }
- r.Proof = nodeSet
- return nil
- case MsgProofsV2:
- proofs := msg.Obj.(light.NodeList)
- // Verify the proof and store if checks out
- nodeSet := proofs.NodeSet()
- reads := &readTraceDB{db: nodeSet}
- if _, _, err := trie.VerifyProof(r.Id.Root, r.Key, reads); err != nil {
- return fmt.Errorf("merkle proof verification failed: %v", err)
- }
- // check if all nodes have been read by VerifyProof
- if len(reads.reads) != nodeSet.KeyCount() {
- return errUselessNodes
- }
- r.Proof = nodeSet
- return nil
- default:
- return errInvalidMessageType
- }
- }
- type CodeReq struct {
- BHash common.Hash
- AccKey []byte
- }
- // ODR request type for node data (used for retrieving contract code), see LesOdrRequest interface
- type CodeRequest light.CodeRequest
- // GetCost returns the cost of the given ODR request according to the serving
- // peer's cost table (implementation of LesOdrRequest)
- func (r *CodeRequest) GetCost(peer *peer) uint64 {
- return peer.GetRequestCost(GetCodeMsg, 1)
- }
- // CanSend tells if a certain peer is suitable for serving the given request
- func (r *CodeRequest) CanSend(peer *peer) bool {
- return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber)
- }
- // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
- func (r *CodeRequest) Request(reqID uint64, peer *peer) error {
- peer.Log().Debug("Requesting code data", "hash", r.Hash)
- req := CodeReq{
- BHash: r.Id.BlockHash,
- AccKey: r.Id.AccKey,
- }
- return peer.RequestCode(reqID, r.GetCost(peer), []CodeReq{req})
- }
- // Valid processes an ODR request reply message from the LES network
- // returns true and stores results in memory if the message was a valid reply
- // to the request (implementation of LesOdrRequest)
- func (r *CodeRequest) Validate(db ethdb.Database, msg *Msg) error {
- log.Debug("Validating code data", "hash", r.Hash)
- // Ensure we have a correct message with a single code element
- if msg.MsgType != MsgCode {
- return errInvalidMessageType
- }
- reply := msg.Obj.([][]byte)
- if len(reply) != 1 {
- return errInvalidEntryCount
- }
- data := reply[0]
- // Verify the data and store if checks out
- if hash := crypto.Keccak256Hash(data); r.Hash != hash {
- return errDataHashMismatch
- }
- r.Data = data
- return nil
- }
- const (
- // helper trie type constants
- htCanonical = iota // Canonical hash trie
- htBloomBits // BloomBits trie
- // applicable for all helper trie requests
- auxRoot = 1
- // applicable for htCanonical
- auxHeader = 2
- )
- type HelperTrieReq struct {
- Type uint
- TrieIdx uint64
- Key []byte
- FromLevel, AuxReq uint
- }
- type HelperTrieResps struct { // describes all responses, not just a single one
- Proofs light.NodeList
- AuxData [][]byte
- }
- // legacy LES/1
- type ChtReq struct {
- ChtNum, BlockNum uint64
- FromLevel uint
- }
- // legacy LES/1
- type ChtResp struct {
- Header *types.Header
- Proof []rlp.RawValue
- }
- // ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface
- type ChtRequest light.ChtRequest
- // GetCost returns the cost of the given ODR request according to the serving
- // peer's cost table (implementation of LesOdrRequest)
- func (r *ChtRequest) GetCost(peer *peer) uint64 {
- switch peer.version {
- case lpv1:
- return peer.GetRequestCost(GetHeaderProofsMsg, 1)
- case lpv2:
- return peer.GetRequestCost(GetHelperTrieProofsMsg, 1)
- default:
- panic(nil)
- }
- }
- // CanSend tells if a certain peer is suitable for serving the given request
- func (r *ChtRequest) CanSend(peer *peer) bool {
- peer.lock.RLock()
- defer peer.lock.RUnlock()
- return peer.headInfo.Number >= light.HelperTrieConfirmations && r.ChtNum <= (peer.headInfo.Number-light.HelperTrieConfirmations)/light.CHTFrequencyClient
- }
- // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
- func (r *ChtRequest) Request(reqID uint64, peer *peer) error {
- peer.Log().Debug("Requesting CHT", "cht", r.ChtNum, "block", r.BlockNum)
- var encNum [8]byte
- binary.BigEndian.PutUint64(encNum[:], r.BlockNum)
- req := HelperTrieReq{
- Type: htCanonical,
- TrieIdx: r.ChtNum,
- Key: encNum[:],
- AuxReq: auxHeader,
- }
- return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), []HelperTrieReq{req})
- }
- // Valid processes an ODR request reply message from the LES network
- // returns true and stores results in memory if the message was a valid reply
- // to the request (implementation of LesOdrRequest)
- func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error {
- log.Debug("Validating CHT", "cht", r.ChtNum, "block", r.BlockNum)
- switch msg.MsgType {
- case MsgHeaderProofs: // LES/1 backwards compatibility
- proofs := msg.Obj.([]ChtResp)
- if len(proofs) != 1 {
- return errInvalidEntryCount
- }
- proof := proofs[0]
- // Verify the CHT
- var encNumber [8]byte
- binary.BigEndian.PutUint64(encNumber[:], r.BlockNum)
- value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], light.NodeList(proof.Proof).NodeSet())
- if err != nil {
- return err
- }
- var node light.ChtNode
- if err := rlp.DecodeBytes(value, &node); err != nil {
- return err
- }
- if node.Hash != proof.Header.Hash() {
- return errCHTHashMismatch
- }
- // Verifications passed, store and return
- r.Header = proof.Header
- r.Proof = light.NodeList(proof.Proof).NodeSet()
- r.Td = node.Td
- case MsgHelperTrieProofs:
- resp := msg.Obj.(HelperTrieResps)
- if len(resp.AuxData) != 1 {
- return errInvalidEntryCount
- }
- nodeSet := resp.Proofs.NodeSet()
- headerEnc := resp.AuxData[0]
- if len(headerEnc) == 0 {
- return errHeaderUnavailable
- }
- header := new(types.Header)
- if err := rlp.DecodeBytes(headerEnc, header); err != nil {
- return errHeaderUnavailable
- }
- // Verify the CHT
- var encNumber [8]byte
- binary.BigEndian.PutUint64(encNumber[:], r.BlockNum)
- reads := &readTraceDB{db: nodeSet}
- value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], reads)
- if err != nil {
- return fmt.Errorf("merkle proof verification failed: %v", err)
- }
- if len(reads.reads) != nodeSet.KeyCount() {
- return errUselessNodes
- }
- var node light.ChtNode
- if err := rlp.DecodeBytes(value, &node); err != nil {
- return err
- }
- if node.Hash != header.Hash() {
- return errCHTHashMismatch
- }
- if r.BlockNum != header.Number.Uint64() {
- return errCHTNumberMismatch
- }
- // Verifications passed, store and return
- r.Header = header
- r.Proof = nodeSet
- r.Td = node.Td
- default:
- return errInvalidMessageType
- }
- return nil
- }
- type BloomReq struct {
- BloomTrieNum, BitIdx, SectionIdx, FromLevel uint64
- }
- // ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface
- type BloomRequest light.BloomRequest
- // GetCost returns the cost of the given ODR request according to the serving
- // peer's cost table (implementation of LesOdrRequest)
- func (r *BloomRequest) GetCost(peer *peer) uint64 {
- return peer.GetRequestCost(GetHelperTrieProofsMsg, len(r.SectionIdxList))
- }
- // CanSend tells if a certain peer is suitable for serving the given request
- func (r *BloomRequest) CanSend(peer *peer) bool {
- peer.lock.RLock()
- defer peer.lock.RUnlock()
- if peer.version < lpv2 {
- return false
- }
- return peer.headInfo.Number >= light.HelperTrieConfirmations && r.BloomTrieNum <= (peer.headInfo.Number-light.HelperTrieConfirmations)/light.BloomTrieFrequency
- }
- // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
- func (r *BloomRequest) Request(reqID uint64, peer *peer) error {
- peer.Log().Debug("Requesting BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIdxList)
- reqs := make([]HelperTrieReq, len(r.SectionIdxList))
- var encNumber [10]byte
- binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx))
- for i, sectionIdx := range r.SectionIdxList {
- binary.BigEndian.PutUint64(encNumber[2:], sectionIdx)
- reqs[i] = HelperTrieReq{
- Type: htBloomBits,
- TrieIdx: r.BloomTrieNum,
- Key: common.CopyBytes(encNumber[:]),
- }
- }
- return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), reqs)
- }
- // Valid processes an ODR request reply message from the LES network
- // returns true and stores results in memory if the message was a valid reply
- // to the request (implementation of LesOdrRequest)
- func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error {
- log.Debug("Validating BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIdxList)
- // Ensure we have a correct message with a single proof element
- if msg.MsgType != MsgHelperTrieProofs {
- return errInvalidMessageType
- }
- resps := msg.Obj.(HelperTrieResps)
- proofs := resps.Proofs
- nodeSet := proofs.NodeSet()
- reads := &readTraceDB{db: nodeSet}
- r.BloomBits = make([][]byte, len(r.SectionIdxList))
- // Verify the proofs
- var encNumber [10]byte
- binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx))
- for i, idx := range r.SectionIdxList {
- binary.BigEndian.PutUint64(encNumber[2:], idx)
- value, _, err := trie.VerifyProof(r.BloomTrieRoot, encNumber[:], reads)
- if err != nil {
- return err
- }
- r.BloomBits[i] = value
- }
- if len(reads.reads) != nodeSet.KeyCount() {
- return errUselessNodes
- }
- r.Proofs = nodeSet
- return nil
- }
- // readTraceDB stores the keys of database reads. We use this to check that received node
- // sets contain only the trie nodes necessary to make proofs pass.
- type readTraceDB struct {
- db trie.DatabaseReader
- reads map[string]struct{}
- }
- // Get returns a stored node
- func (db *readTraceDB) Get(k []byte) ([]byte, error) {
- if db.reads == nil {
- db.reads = make(map[string]struct{})
- }
- db.reads[string(k)] = struct{}{}
- return db.db.Get(k)
- }
- // Has returns true if the node set contains the given key
- func (db *readTraceDB) Has(key []byte) (bool, error) {
- _, err := db.Get(key)
- return err == nil, nil
- }
|