* les: implement request distributor, fix blocking issues * core: moved header validation before chain mutex lock
		
			
				
	
	
		
			357 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			357 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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"
 | |
| 	"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")
 | |
| 	errMultipleEntries     = errors.New("multiple 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")
 | |
| )
 | |
| 
 | |
| 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)
 | |
| 	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 errMultipleEntries
 | |
| 	}
 | |
| 	body := bodies[0]
 | |
| 
 | |
| 	// Retrieve our stored header and validate block content against it
 | |
| 	header := core.GetHeader(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 errMultipleEntries
 | |
| 	}
 | |
| 	receipt := receipts[0]
 | |
| 
 | |
| 	// Retrieve our stored header and validate receipt content against it
 | |
| 	header := core.GetHeader(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 {
 | |
| 	return peer.GetRequestCost(GetProofsMsg, 1)
 | |
| }
 | |
| 
 | |
| // 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)
 | |
| 
 | |
| 	// Ensure we have a correct message with a single proof
 | |
| 	if msg.MsgType != MsgProofs {
 | |
| 		return errInvalidMessageType
 | |
| 	}
 | |
| 	proofs := msg.Obj.([][]rlp.RawValue)
 | |
| 	if len(proofs) != 1 {
 | |
| 		return errMultipleEntries
 | |
| 	}
 | |
| 	// Verify the proof and store if checks out
 | |
| 	if _, err := trie.VerifyProof(r.Id.Root, r.Key, proofs[0]); err != nil {
 | |
| 		return fmt.Errorf("merkle proof verification failed: %v", err)
 | |
| 	}
 | |
| 	r.Proof = proofs[0]
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| 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 errMultipleEntries
 | |
| 	}
 | |
| 	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
 | |
| }
 | |
| 
 | |
| type ChtReq struct {
 | |
| 	ChtNum, BlockNum, FromLevel uint64
 | |
| }
 | |
| 
 | |
| 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 {
 | |
| 	return peer.GetRequestCost(GetHeaderProofsMsg, 1)
 | |
| }
 | |
| 
 | |
| // 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 r.ChtNum <= (peer.headInfo.Number-light.ChtConfirmations)/light.ChtFrequency
 | |
| }
 | |
| 
 | |
| // 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)
 | |
| 	req := &ChtReq{
 | |
| 		ChtNum:   r.ChtNum,
 | |
| 		BlockNum: r.BlockNum,
 | |
| 	}
 | |
| 	return peer.RequestHeaderProofs(reqID, r.GetCost(peer), []*ChtReq{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)
 | |
| 
 | |
| 	// Ensure we have a correct message with a single proof element
 | |
| 	if msg.MsgType != MsgHeaderProofs {
 | |
| 		return errInvalidMessageType
 | |
| 	}
 | |
| 	proofs := msg.Obj.([]ChtResp)
 | |
| 	if len(proofs) != 1 {
 | |
| 		return errMultipleEntries
 | |
| 	}
 | |
| 	proof := proofs[0]
 | |
| 
 | |
| 	// Verify the CHT
 | |
| 	var encNumber [8]byte
 | |
| 	binary.BigEndian.PutUint64(encNumber[:], r.BlockNum)
 | |
| 
 | |
| 	value, err := trie.VerifyProof(r.ChtRoot, encNumber[:], proof.Proof)
 | |
| 	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 = proof.Proof
 | |
| 	r.Td = node.Td
 | |
| 
 | |
| 	return nil
 | |
| }
 |