mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 01:13:45 +00:00
les: implement ProofsV2Msg
This commit is contained in:
parent
6016610f7d
commit
c76e52132f
8 changed files with 168 additions and 51 deletions
108
les/handler.go
108
les/handler.go
|
|
@ -18,6 +18,7 @@
|
|||
package les
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -51,13 +52,13 @@ const (
|
|||
|
||||
ethVersion = 63 // equivalent eth version for the downloader
|
||||
|
||||
MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request
|
||||
MaxBodyFetch = 32 // Amount of block bodies to be fetched per retrieval request
|
||||
MaxReceiptFetch = 128 // Amount of transaction receipts to allow fetching per request
|
||||
MaxCodeFetch = 64 // Amount of contract codes to allow fetching per request
|
||||
MaxProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request
|
||||
MaxHeaderProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request
|
||||
MaxTxSend = 64 // Amount of transactions to be send per request
|
||||
MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request
|
||||
MaxBodyFetch = 32 // Amount of block bodies to be fetched per retrieval request
|
||||
MaxReceiptFetch = 128 // Amount of transaction receipts to allow fetching per request
|
||||
MaxCodeFetch = 64 // Amount of contract codes to allow fetching per request
|
||||
MaxProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request
|
||||
MaxPPTProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request
|
||||
MaxTxSend = 64 // Amount of transactions to be send per request
|
||||
|
||||
disableClientRemovePeer = false
|
||||
)
|
||||
|
|
@ -316,7 +317,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
|
|||
}
|
||||
}
|
||||
|
||||
var reqList = []uint64{GetBlockHeadersMsg, GetBlockBodiesMsg, GetCodeMsg, GetReceiptsMsg, GetProofsMsg, SendTxMsg, GetHeaderProofsMsg}
|
||||
var reqList = []uint64{GetBlockHeadersMsg, GetBlockBodiesMsg, GetCodeMsg, GetReceiptsMsg, GetProofsV1Msg, SendTxMsg, GetHeaderProofsMsg, GetProofsV2Msg, GetPPTProofsMsg}
|
||||
|
||||
// handleMsg is invoked whenever an inbound message is received from a remote
|
||||
// peer. The remote connection is torn down upon returning any error.
|
||||
|
|
@ -656,7 +657,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
Obj: resp.Receipts,
|
||||
}
|
||||
|
||||
case GetProofsMsg:
|
||||
case GetProofsV1Msg:
|
||||
p.Log().Trace("Received proofs request")
|
||||
// Decode the retrieval message
|
||||
var req struct {
|
||||
|
|
@ -703,7 +704,67 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost)
|
||||
return p.SendProofs(req.ReqID, bv, proofs)
|
||||
|
||||
case ProofsMsg:
|
||||
case GetProofsV2Msg:
|
||||
p.Log().Trace("Received les/2 proofs request")
|
||||
// Decode the retrieval message
|
||||
var req struct {
|
||||
ReqID uint64
|
||||
Reqs []ProofReq
|
||||
}
|
||||
if err := msg.Decode(&req); err != nil {
|
||||
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||
}
|
||||
// Gather state data until the fetch or network limits is reached
|
||||
var (
|
||||
lastBHash common.Hash
|
||||
lastAccKey []byte
|
||||
tr, str *trie.Trie
|
||||
)
|
||||
reqCnt := len(req.Reqs)
|
||||
if reject(uint64(reqCnt), MaxProofsFetch) {
|
||||
return errResp(ErrRequestRejected, "")
|
||||
}
|
||||
|
||||
nodes := light.NewNodeSet()
|
||||
|
||||
for _, req := range req.Reqs {
|
||||
if nodes.DataSize() >= softResponseLimit {
|
||||
break
|
||||
}
|
||||
if tr == nil || req.BHash != lastBHash {
|
||||
if header := core.GetHeader(pm.chainDb, req.BHash, core.GetBlockNumber(pm.chainDb, req.BHash)); header != nil {
|
||||
tr, _ = trie.New(header.Root, pm.chainDb)
|
||||
} else {
|
||||
tr = nil
|
||||
}
|
||||
lastBHash = req.BHash
|
||||
str = nil
|
||||
}
|
||||
if tr != nil {
|
||||
if len(req.AccKey) > 0 {
|
||||
if str == nil || !bytes.Equal(req.AccKey, lastAccKey) {
|
||||
sdata := tr.Get(req.AccKey)
|
||||
str = nil
|
||||
var acc state.Account
|
||||
if err := rlp.DecodeBytes(sdata, &acc); err == nil {
|
||||
str, _ = trie.New(acc.Root, pm.chainDb)
|
||||
}
|
||||
lastAccKey = common.CopyBytes(req.AccKey)
|
||||
}
|
||||
if str != nil {
|
||||
str.Prove(req.Key, req.FromLevel, nodes)
|
||||
}
|
||||
} else {
|
||||
tr.Prove(req.Key, req.FromLevel, nodes)
|
||||
}
|
||||
}
|
||||
}
|
||||
proofs := nodes.NodeList()
|
||||
bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost)
|
||||
pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost)
|
||||
return p.SendProofsV2(req.ReqID, bv, proofs)
|
||||
|
||||
case ProofsV1Msg:
|
||||
if pm.odr == nil {
|
||||
return errResp(ErrUnexpectedResponse, "")
|
||||
}
|
||||
|
|
@ -712,14 +773,35 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
// A batch of merkle proofs arrived to one of our previous requests
|
||||
var resp struct {
|
||||
ReqID, BV uint64
|
||||
Data [][]rlp.RawValue
|
||||
Data []light.NodeList
|
||||
}
|
||||
if err := msg.Decode(&resp); err != nil {
|
||||
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||
}
|
||||
p.fcServer.GotReply(resp.ReqID, resp.BV)
|
||||
deliverMsg = &Msg{
|
||||
MsgType: MsgProofs,
|
||||
MsgType: MsgProofsV1,
|
||||
ReqID: resp.ReqID,
|
||||
Obj: resp.Data,
|
||||
}
|
||||
|
||||
case ProofsV2Msg:
|
||||
if pm.odr == nil {
|
||||
return errResp(ErrUnexpectedResponse, "")
|
||||
}
|
||||
|
||||
p.Log().Trace("Received les/2 proofs response")
|
||||
// A batch of merkle proofs arrived to one of our previous requests
|
||||
var resp struct {
|
||||
ReqID, BV uint64
|
||||
Data light.NodeList
|
||||
}
|
||||
if err := msg.Decode(&resp); err != nil {
|
||||
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||
}
|
||||
p.fcServer.GotReply(resp.ReqID, resp.BV)
|
||||
deliverMsg = &Msg{
|
||||
MsgType: MsgProofsV2,
|
||||
ReqID: resp.ReqID,
|
||||
Obj: resp.Data,
|
||||
}
|
||||
|
|
@ -740,7 +822,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
proofs []ChtResp
|
||||
)
|
||||
reqCnt := len(req.Reqs)
|
||||
if reject(uint64(reqCnt), MaxHeaderProofsFetch) {
|
||||
if reject(uint64(reqCnt), MaxPPTProofsFetch) {
|
||||
return errResp(ErrRequestRejected, "")
|
||||
}
|
||||
for _, req := range req.Reqs {
|
||||
|
|
|
|||
|
|
@ -338,9 +338,9 @@ func testGetProofs(t *testing.T, protocol int) {
|
|||
}
|
||||
}
|
||||
// Send the proof request and verify the response
|
||||
cost := peer.GetRequestCost(GetProofsMsg, len(proofreqs))
|
||||
sendRequest(peer.app, GetProofsMsg, 42, cost, proofreqs)
|
||||
if err := expectResponse(peer.app, ProofsMsg, 42, testBufLimit, proofs); err != nil {
|
||||
cost := peer.GetRequestCost(GetProofsV1Msg, len(proofreqs))
|
||||
sendRequest(peer.app, GetProofsV1Msg, 42, cost, proofreqs)
|
||||
if err := expectResponse(peer.app, ProofsV1Msg, 42, testBufLimit, proofs); err != nil {
|
||||
t.Errorf("proofs mismatch: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@ const (
|
|||
MsgBlockBodies = iota
|
||||
MsgCode
|
||||
MsgReceipts
|
||||
MsgProofs
|
||||
MsgProofsV1
|
||||
MsgProofsV2
|
||||
MsgHeaderProofs
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ var (
|
|||
errReceiptHashMismatch = errors.New("receipt hash mismatch")
|
||||
errDataHashMismatch = errors.New("data hash mismatch")
|
||||
errCHTHashMismatch = errors.New("cht hash mismatch")
|
||||
errUselessNodes = errors.New("useless nodes in merkle proof nodeset")
|
||||
)
|
||||
|
||||
type LesOdrRequest interface {
|
||||
|
|
@ -186,7 +187,14 @@ 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)
|
||||
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
|
||||
|
|
@ -211,20 +219,38 @@ func (r *TrieRequest) Request(reqID uint64, peer *peer) error {
|
|||
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 {
|
||||
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
|
||||
pdb := proofs.NodeSet()
|
||||
cdb := pdb.ReadCache()
|
||||
if _, err, _ := trie.VerifyProof(r.Id.Root, r.Key, cdb); err != nil {
|
||||
return fmt.Errorf("merkle proof verification failed: %v", err)
|
||||
}
|
||||
// check if all nodes have been read by VerifyProof
|
||||
if pdb.KeyCount() != cdb.KeyCount() {
|
||||
return errUselessNodes
|
||||
}
|
||||
r.Proof = pdb
|
||||
return nil
|
||||
|
||||
default:
|
||||
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, light.NodeList(proofs[0]).NodeSet()); err != nil {
|
||||
return fmt.Errorf("merkle proof verification failed: %v", err)
|
||||
}
|
||||
r.Proof = proofs[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
type CodeReq struct {
|
||||
|
|
|
|||
18
les/peer.go
18
les/peer.go
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/les/flowcontrol"
|
||||
"github.com/ethereum/go-ethereum/light"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
|
@ -199,7 +200,12 @@ func (p *peer) SendReceiptsRLP(reqID, bv uint64, receipts []rlp.RawValue) error
|
|||
|
||||
// SendProofs sends a batch of merkle proofs, corresponding to the ones requested.
|
||||
func (p *peer) SendProofs(reqID, bv uint64, proofs proofsData) error {
|
||||
return sendResponse(p.rw, ProofsMsg, reqID, bv, proofs)
|
||||
return sendResponse(p.rw, ProofsV1Msg, reqID, bv, proofs)
|
||||
}
|
||||
|
||||
// SendProofsV2 sends a batch of merkle proofs, corresponding to the ones requested.
|
||||
func (p *peer) SendProofsV2(reqID, bv uint64, proofs light.NodeList) error {
|
||||
return sendResponse(p.rw, ProofsV2Msg, reqID, bv, proofs)
|
||||
}
|
||||
|
||||
// SendHeaderProofs sends a batch of header proofs, corresponding to the ones requested.
|
||||
|
|
@ -244,7 +250,15 @@ func (p *peer) RequestReceipts(reqID, cost uint64, hashes []common.Hash) error {
|
|||
// RequestProofs fetches a batch of merkle proofs from a remote node.
|
||||
func (p *peer) RequestProofs(reqID, cost uint64, reqs []*ProofReq) error {
|
||||
p.Log().Debug("Fetching batch of proofs", "count", len(reqs))
|
||||
return sendRequest(p.rw, GetProofsMsg, reqID, cost, reqs)
|
||||
switch p.version {
|
||||
case lpv1:
|
||||
return sendRequest(p.rw, GetProofsV1Msg, reqID, cost, reqs)
|
||||
case lpv2:
|
||||
return sendRequest(p.rw, GetProofsV2Msg, reqID, cost, reqs)
|
||||
default:
|
||||
panic(nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// RequestHeaderProofs fetches a batch of header merkle proofs from a remote node.
|
||||
|
|
|
|||
|
|
@ -29,13 +29,14 @@ import (
|
|||
// Constants to match up protocol versions and messages
|
||||
const (
|
||||
lpv1 = 1
|
||||
lpv2 = 2
|
||||
)
|
||||
|
||||
// Supported versions of the les protocol (first is primary).
|
||||
var ProtocolVersions = []uint{lpv1}
|
||||
var ProtocolVersions = []uint{lpv1, lpv2}
|
||||
|
||||
// Number of implemented message corresponding to different protocol versions.
|
||||
var ProtocolLengths = []uint64{15}
|
||||
var ProtocolLengths = []uint64{15, 19}
|
||||
|
||||
const (
|
||||
NetworkId = 1
|
||||
|
|
@ -53,13 +54,18 @@ const (
|
|||
BlockBodiesMsg = 0x05
|
||||
GetReceiptsMsg = 0x06
|
||||
ReceiptsMsg = 0x07
|
||||
GetProofsMsg = 0x08
|
||||
ProofsMsg = 0x09
|
||||
GetProofsV1Msg = 0x08
|
||||
ProofsV1Msg = 0x09
|
||||
GetCodeMsg = 0x0a
|
||||
CodeMsg = 0x0b
|
||||
SendTxMsg = 0x0c
|
||||
GetHeaderProofsMsg = 0x0d
|
||||
HeaderProofsMsg = 0x0e
|
||||
// Protocol messages belonging to LPV2
|
||||
GetProofsV2Msg = 0x0f
|
||||
ProofsV2Msg = 0x10
|
||||
GetPPTProofsMsg = 0x11
|
||||
PPTProofsMsg = 0x12
|
||||
)
|
||||
|
||||
type errCode int
|
||||
|
|
|
|||
16
light/odr.go
16
light/odr.go
|
|
@ -25,7 +25,6 @@ import (
|
|||
"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/rlp"
|
||||
)
|
||||
|
|
@ -80,23 +79,12 @@ type TrieRequest struct {
|
|||
OdrRequest
|
||||
Id *TrieID
|
||||
Key []byte
|
||||
Proof []rlp.RawValue
|
||||
Proof *NodeSet
|
||||
}
|
||||
|
||||
// StoreResult stores the retrieved data in local database
|
||||
func (req *TrieRequest) StoreResult(db ethdb.Database) {
|
||||
storeProof(db, req.Proof)
|
||||
}
|
||||
|
||||
// storeProof stores the new trie nodes obtained from a merkle proof in the database
|
||||
func storeProof(db ethdb.Database, proof []rlp.RawValue) {
|
||||
for _, buf := range proof {
|
||||
hash := crypto.Keccak256(buf)
|
||||
val, _ := db.Get(hash)
|
||||
if val == nil {
|
||||
db.Put(hash, buf)
|
||||
}
|
||||
}
|
||||
req.Proof.Store(db)
|
||||
}
|
||||
|
||||
// CodeRequest is the ODR request type for retrieving contract code
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error {
|
|||
t, _ := trie.New(req.Id.Root, odr.sdb)
|
||||
nodes := NewNodeSet()
|
||||
t.Prove(req.Key, 0, nodes)
|
||||
req.Proof = nodes.NodeList()
|
||||
req.Proof = nodes
|
||||
case *CodeRequest:
|
||||
req.Data, _ = odr.sdb.Get(req.Hash[:])
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue