mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
les, light: support multi headers fetching
This commit is contained in:
parent
8ff9a7613f
commit
dfe765f3f5
3 changed files with 116 additions and 86 deletions
|
|
@ -22,6 +22,7 @@ import (
|
|||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
|
|
@ -352,9 +353,9 @@ type ChtRequest light.ChtRequest
|
|||
func (r *ChtRequest) GetCost(peer *peer) uint64 {
|
||||
switch peer.version {
|
||||
case lpv1:
|
||||
return peer.GetRequestCost(GetHeaderProofsMsg, 1)
|
||||
return peer.GetRequestCost(GetHeaderProofsMsg, len(r.BlockNum))
|
||||
case lpv2:
|
||||
return peer.GetRequestCost(GetHelperTrieProofsMsg, 1)
|
||||
return peer.GetRequestCost(GetHelperTrieProofsMsg, len(r.BlockNum))
|
||||
default:
|
||||
panic(nil)
|
||||
}
|
||||
|
|
@ -371,26 +372,33 @@ func (r *ChtRequest) CanSend(peer *peer, config *light.IndexerConfig) bool {
|
|||
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
||||
func (r *ChtRequest) Request(reqID uint64, peer *peer, config *light.IndexerConfig) error {
|
||||
peer.Log().Debug("Requesting CHT", "cht", r.ChtNum, "block", r.BlockNum)
|
||||
var encNum [8]byte
|
||||
binary.BigEndian.PutUint64(encNum[:], r.BlockNum)
|
||||
req := HelperTrieReq{
|
||||
var (
|
||||
encNum [8]byte
|
||||
reqs []HelperTrieReq
|
||||
)
|
||||
for _, num := range r.BlockNum {
|
||||
binary.BigEndian.PutUint64(encNum[:], num)
|
||||
reqs = append(reqs, HelperTrieReq{
|
||||
Type: htCanonical,
|
||||
TrieIdx: r.ChtNum,
|
||||
Key: encNum[:],
|
||||
AuxReq: auxHeader,
|
||||
})
|
||||
}
|
||||
switch peer.version {
|
||||
case lpv1:
|
||||
var reqsV1 ChtReq
|
||||
if req.Type != htCanonical || req.AuxReq != auxHeader || len(req.Key) != 8 {
|
||||
var reqsV1 []ChtReq
|
||||
for _, r := range reqs {
|
||||
if r.Type != htCanonical || r.AuxReq != auxHeader || len(r.Key) != 8 {
|
||||
return fmt.Errorf("Request invalid in LES/1 mode")
|
||||
}
|
||||
blockNum := binary.BigEndian.Uint64(req.Key)
|
||||
blockNum := binary.BigEndian.Uint64(r.Key)
|
||||
// convert HelperTrie request to old CHT request
|
||||
reqsV1 = ChtReq{ChtNum: (req.TrieIdx + 1) * (config.ChtSize / config.PairChtSize), BlockNum: blockNum, FromLevel: req.FromLevel}
|
||||
return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), []ChtReq{reqsV1})
|
||||
reqsV1 = append(reqsV1, ChtReq{ChtNum: (r.TrieIdx+1)*(config.ChtSize/config.PairChtSize), BlockNum: blockNum, FromLevel: r.FromLevel})
|
||||
}
|
||||
return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), reqsV1)
|
||||
case lpv2:
|
||||
return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), []HelperTrieReq{req})
|
||||
return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), reqs)
|
||||
default:
|
||||
panic(nil)
|
||||
}
|
||||
|
|
@ -404,73 +412,88 @@ func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error {
|
|||
|
||||
switch msg.MsgType {
|
||||
case MsgHeaderProofs: // LES/1 backwards compatibility
|
||||
proofs := msg.Obj.([]ChtResp)
|
||||
if len(proofs) != 1 {
|
||||
resps := msg.Obj.([]ChtResp)
|
||||
if len(resps) != len(r.BlockNum) {
|
||||
return errInvalidEntryCount
|
||||
}
|
||||
proof := proofs[0]
|
||||
|
||||
var (
|
||||
headers []*types.Header
|
||||
tds []*big.Int
|
||||
encNumber [8]byte
|
||||
node light.ChtNode
|
||||
nodeset = light.NewNodeSet()
|
||||
)
|
||||
for i, num := range r.BlockNum {
|
||||
resp := resps[i]
|
||||
// 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())
|
||||
binary.BigEndian.PutUint64(encNumber[:], num)
|
||||
value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], light.NodeList(resp.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() {
|
||||
if node.Hash != resp.Header.Hash() {
|
||||
return errCHTHashMismatch
|
||||
}
|
||||
// Verifications passed, store and return
|
||||
r.Header = proof.Header
|
||||
r.Proof = light.NodeList(proof.Proof).NodeSet()
|
||||
r.Td = node.Td
|
||||
if num != resp.Header.Number.Uint64() {
|
||||
return errCHTNumberMismatch
|
||||
}
|
||||
// Verifications passed, store temporarily
|
||||
headers = append(headers, resp.Header)
|
||||
tds = append(tds, node.Td)
|
||||
light.NodeList(resp.Proof).Store(nodeset)
|
||||
}
|
||||
r.Header = headers
|
||||
r.Td = tds
|
||||
r.Proof = nodeset
|
||||
|
||||
case MsgHelperTrieProofs:
|
||||
// Check if the number of items in the response is the same as we requested.
|
||||
resp := msg.Obj.(HelperTrieResps)
|
||||
if len(resp.AuxData) != 1 {
|
||||
if len(resp.AuxData) != len(r.BlockNum) {
|
||||
return errInvalidEntryCount
|
||||
}
|
||||
nodeSet := resp.Proofs.NodeSet()
|
||||
headerEnc := resp.AuxData[0]
|
||||
if len(headerEnc) == 0 {
|
||||
var (
|
||||
headers []*types.Header
|
||||
tds []*big.Int
|
||||
encNumber [8]byte
|
||||
node light.ChtNode
|
||||
nodeSet = resp.Proofs.NodeSet()
|
||||
)
|
||||
for i, num := range r.BlockNum {
|
||||
enc := resp.AuxData[i]
|
||||
if len(enc) == 0 {
|
||||
return errHeaderUnavailable
|
||||
}
|
||||
header := new(types.Header)
|
||||
if err := rlp.DecodeBytes(headerEnc, header); err != nil {
|
||||
if err := rlp.DecodeBytes(enc, 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)
|
||||
binary.BigEndian.PutUint64(encNumber[:], num)
|
||||
value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], nodeSet)
|
||||
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() {
|
||||
if num != header.Number.Uint64() {
|
||||
return errCHTNumberMismatch
|
||||
}
|
||||
// Verifications passed, store and return
|
||||
r.Header = header
|
||||
// Verifications passed, store temporarily
|
||||
headers = append(headers, header)
|
||||
tds = append(tds, node.Td)
|
||||
}
|
||||
r.Header = headers
|
||||
r.Td = tds
|
||||
r.Proof = nodeSet
|
||||
r.Td = node.Td
|
||||
|
||||
default:
|
||||
return errInvalidMessageType
|
||||
}
|
||||
|
|
|
|||
18
light/odr.go
18
light/odr.go
|
|
@ -133,20 +133,26 @@ func (req *ReceiptsRequest) StoreResult(db ethdb.Database, config *IndexerConfig
|
|||
// ChtRequest is the ODR request type for state/storage trie entries
|
||||
type ChtRequest struct {
|
||||
OdrRequest
|
||||
ChtNum, BlockNum uint64
|
||||
ChtNum uint64
|
||||
ChtRoot common.Hash
|
||||
Header *types.Header
|
||||
Td *big.Int
|
||||
BlockNum []uint64
|
||||
Header []*types.Header
|
||||
Td []*big.Int
|
||||
Proof *NodeSet
|
||||
}
|
||||
|
||||
// StoreResult stores the retrieved data in local database
|
||||
func (req *ChtRequest) StoreResult(db ethdb.Database, config *IndexerConfig) {
|
||||
hash, num := req.Header.Hash(), req.Header.Number.Uint64()
|
||||
// The block number, header, td, proof length consistency has been verified
|
||||
// in the validation phase.
|
||||
for index := range req.BlockNum {
|
||||
header := req.Header[index]
|
||||
hash, num := header.Hash(), header.Number.Uint64()
|
||||
|
||||
rawdb.WriteHeader(db, req.Header)
|
||||
rawdb.WriteTd(db, hash, num, req.Td)
|
||||
rawdb.WriteHeader(db, header)
|
||||
rawdb.WriteTd(db, hash, num, req.Td[index])
|
||||
rawdb.WriteCanonicalHash(db, hash, num)
|
||||
}
|
||||
}
|
||||
|
||||
// BloomRequest is the ODR request type for retrieving bloom filters from a CHT structure
|
||||
|
|
|
|||
|
|
@ -62,11 +62,12 @@ func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*typ
|
|||
if number >= chtCount*odr.IndexerConfig().ChtSize {
|
||||
return nil, ErrNoTrustedCht
|
||||
}
|
||||
r := &ChtRequest{ChtRoot: GetChtRoot(db, chtCount-1, sectionHead), ChtNum: chtCount - 1, BlockNum: number}
|
||||
r := &ChtRequest{ChtRoot: GetChtRoot(db, chtCount-1, sectionHead), ChtNum: chtCount - 1, BlockNum: []uint64{number}}
|
||||
if err := odr.Retrieve(ctx, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.Header, nil
|
||||
// Response item number has been checked in validation stage, no extra checking needed.
|
||||
return r.Header[0], nil
|
||||
}
|
||||
|
||||
func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue