les, light: support multi headers fetching

This commit is contained in:
rjl493456442 2018-07-12 17:08:31 +08:00
parent 8ff9a7613f
commit dfe765f3f5
3 changed files with 116 additions and 86 deletions

View file

@ -22,6 +22,7 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
@ -352,9 +353,9 @@ type ChtRequest light.ChtRequest
func (r *ChtRequest) GetCost(peer *peer) uint64 { func (r *ChtRequest) GetCost(peer *peer) uint64 {
switch peer.version { switch peer.version {
case lpv1: case lpv1:
return peer.GetRequestCost(GetHeaderProofsMsg, 1) return peer.GetRequestCost(GetHeaderProofsMsg, len(r.BlockNum))
case lpv2: case lpv2:
return peer.GetRequestCost(GetHelperTrieProofsMsg, 1) return peer.GetRequestCost(GetHelperTrieProofsMsg, len(r.BlockNum))
default: default:
panic(nil) 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) // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
func (r *ChtRequest) Request(reqID uint64, peer *peer, config *light.IndexerConfig) error { func (r *ChtRequest) Request(reqID uint64, peer *peer, config *light.IndexerConfig) error {
peer.Log().Debug("Requesting CHT", "cht", r.ChtNum, "block", r.BlockNum) peer.Log().Debug("Requesting CHT", "cht", r.ChtNum, "block", r.BlockNum)
var encNum [8]byte var (
binary.BigEndian.PutUint64(encNum[:], r.BlockNum) encNum [8]byte
req := HelperTrieReq{ reqs []HelperTrieReq
)
for _, num := range r.BlockNum {
binary.BigEndian.PutUint64(encNum[:], num)
reqs = append(reqs, HelperTrieReq{
Type: htCanonical, Type: htCanonical,
TrieIdx: r.ChtNum, TrieIdx: r.ChtNum,
Key: encNum[:], Key: encNum[:],
AuxReq: auxHeader, AuxReq: auxHeader,
})
} }
switch peer.version { switch peer.version {
case lpv1: case lpv1:
var reqsV1 ChtReq var reqsV1 []ChtReq
if req.Type != htCanonical || req.AuxReq != auxHeader || len(req.Key) != 8 { for _, r := range reqs {
if r.Type != htCanonical || r.AuxReq != auxHeader || len(r.Key) != 8 {
return fmt.Errorf("Request invalid in LES/1 mode") 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 // convert HelperTrie request to old CHT request
reqsV1 = ChtReq{ChtNum: (req.TrieIdx + 1) * (config.ChtSize / config.PairChtSize), BlockNum: blockNum, FromLevel: req.FromLevel} reqsV1 = append(reqsV1, ChtReq{ChtNum: (r.TrieIdx+1)*(config.ChtSize/config.PairChtSize), BlockNum: blockNum, FromLevel: r.FromLevel})
return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), []ChtReq{reqsV1}) }
return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), reqsV1)
case lpv2: case lpv2:
return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), []HelperTrieReq{req}) return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), reqs)
default: default:
panic(nil) panic(nil)
} }
@ -404,73 +412,88 @@ func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error {
switch msg.MsgType { switch msg.MsgType {
case MsgHeaderProofs: // LES/1 backwards compatibility case MsgHeaderProofs: // LES/1 backwards compatibility
proofs := msg.Obj.([]ChtResp) resps := msg.Obj.([]ChtResp)
if len(proofs) != 1 { if len(resps) != len(r.BlockNum) {
return errInvalidEntryCount 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 // Verify the CHT
var encNumber [8]byte binary.BigEndian.PutUint64(encNumber[:], num)
binary.BigEndian.PutUint64(encNumber[:], r.BlockNum) value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], light.NodeList(resp.Proof).NodeSet())
value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], light.NodeList(proof.Proof).NodeSet())
if err != nil { if err != nil {
return err return err
} }
var node light.ChtNode
if err := rlp.DecodeBytes(value, &node); err != nil { if err := rlp.DecodeBytes(value, &node); err != nil {
return err return err
} }
if node.Hash != proof.Header.Hash() { if node.Hash != resp.Header.Hash() {
return errCHTHashMismatch return errCHTHashMismatch
} }
// Verifications passed, store and return if num != resp.Header.Number.Uint64() {
r.Header = proof.Header return errCHTNumberMismatch
r.Proof = light.NodeList(proof.Proof).NodeSet() }
r.Td = node.Td // 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: case MsgHelperTrieProofs:
// Check if the number of items in the response is the same as we requested.
resp := msg.Obj.(HelperTrieResps) resp := msg.Obj.(HelperTrieResps)
if len(resp.AuxData) != 1 { if len(resp.AuxData) != len(r.BlockNum) {
return errInvalidEntryCount return errInvalidEntryCount
} }
nodeSet := resp.Proofs.NodeSet() var (
headerEnc := resp.AuxData[0] headers []*types.Header
if len(headerEnc) == 0 { 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 return errHeaderUnavailable
} }
header := new(types.Header) header := new(types.Header)
if err := rlp.DecodeBytes(headerEnc, header); err != nil { if err := rlp.DecodeBytes(enc, header); err != nil {
return errHeaderUnavailable return errHeaderUnavailable
} }
// Verify the CHT // Verify the CHT
var encNumber [8]byte binary.BigEndian.PutUint64(encNumber[:], num)
binary.BigEndian.PutUint64(encNumber[:], r.BlockNum) value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], nodeSet)
reads := &readTraceDB{db: nodeSet}
value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], reads)
if err != nil { if err != nil {
return fmt.Errorf("merkle proof verification failed: %v", err) 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 { if err := rlp.DecodeBytes(value, &node); err != nil {
return err return err
} }
if node.Hash != header.Hash() { if node.Hash != header.Hash() {
return errCHTHashMismatch return errCHTHashMismatch
} }
if r.BlockNum != header.Number.Uint64() { if num != header.Number.Uint64() {
return errCHTNumberMismatch return errCHTNumberMismatch
} }
// Verifications passed, store and return // Verifications passed, store temporarily
r.Header = header headers = append(headers, header)
tds = append(tds, node.Td)
}
r.Header = headers
r.Td = tds
r.Proof = nodeSet r.Proof = nodeSet
r.Td = node.Td
default: default:
return errInvalidMessageType return errInvalidMessageType
} }

View file

@ -133,21 +133,27 @@ func (req *ReceiptsRequest) StoreResult(db ethdb.Database, config *IndexerConfig
// ChtRequest is the ODR request type for state/storage trie entries // ChtRequest is the ODR request type for state/storage trie entries
type ChtRequest struct { type ChtRequest struct {
OdrRequest OdrRequest
ChtNum, BlockNum uint64 ChtNum uint64
ChtRoot common.Hash ChtRoot common.Hash
Header *types.Header BlockNum []uint64
Td *big.Int Header []*types.Header
Td []*big.Int
Proof *NodeSet Proof *NodeSet
} }
// StoreResult stores the retrieved data in local database // StoreResult stores the retrieved data in local database
func (req *ChtRequest) StoreResult(db ethdb.Database, config *IndexerConfig) { 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.WriteHeader(db, header)
rawdb.WriteTd(db, hash, num, req.Td) rawdb.WriteTd(db, hash, num, req.Td[index])
rawdb.WriteCanonicalHash(db, hash, num) rawdb.WriteCanonicalHash(db, hash, num)
} }
}
// BloomRequest is the ODR request type for retrieving bloom filters from a CHT structure // BloomRequest is the ODR request type for retrieving bloom filters from a CHT structure
type BloomRequest struct { type BloomRequest struct {

View file

@ -62,11 +62,12 @@ func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*typ
if number >= chtCount*odr.IndexerConfig().ChtSize { if number >= chtCount*odr.IndexerConfig().ChtSize {
return nil, ErrNoTrustedCht 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 { if err := odr.Retrieve(ctx, r); err != nil {
return nil, err 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) { func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) {