mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 09:23:48 +00:00
les, light: add GetCheckpoint to LES (status.im issue 320)
This commit is contained in:
parent
43dd8e62fc
commit
2f607c2726
8 changed files with 190 additions and 2 deletions
|
|
@ -319,7 +319,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
|
|||
}
|
||||
}
|
||||
|
||||
var reqList = []uint64{GetBlockHeadersMsg, GetBlockBodiesMsg, GetCodeMsg, GetReceiptsMsg, GetProofsV1Msg, SendTxMsg, SendTxV2Msg, GetTxStatusMsg, GetHeaderProofsMsg, GetProofsV2Msg, GetHelperTrieProofsMsg}
|
||||
var reqList = []uint64{GetBlockHeadersMsg, GetBlockBodiesMsg, GetCodeMsg, GetReceiptsMsg, GetProofsV1Msg, SendTxMsg, SendTxV2Msg, GetTxStatusMsg, GetHeaderProofsMsg, GetProofsV2Msg, GetHelperTrieProofsMsg, GetCheckpointMsg}
|
||||
|
||||
// handleMsg is invoked whenever an inbound message is received from a remote
|
||||
// peer. The remote connection is torn down upon returning any error.
|
||||
|
|
@ -1063,6 +1063,63 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
|
||||
p.fcServer.GotReply(resp.ReqID, resp.BV)
|
||||
|
||||
case GetCheckpointMsg:
|
||||
p.Log().Debug("Received GetCheckpointMsg", "msg", msg)
|
||||
// Decode the retrieval message
|
||||
var req struct {
|
||||
ReqID uint64
|
||||
Req CheckpointReq
|
||||
}
|
||||
if err := msg.Decode(&req); err != nil {
|
||||
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||
}
|
||||
p.Log().Debug("Got CheckpointReq", "req", req)
|
||||
|
||||
sectionHead := pm.server.chtIndexer.SectionHead(req.Req.SectionIdx)
|
||||
|
||||
var chtRoot common.Hash
|
||||
var prefix string
|
||||
chtRoot, prefix = pm.getHelperTrie(0, req.Req.SectionIdx)
|
||||
p.Log().Debug("Got chtRoot, prefix=", "chtRoot", chtRoot, "prefix", prefix)
|
||||
|
||||
var bloomRoot common.Hash
|
||||
var prefix2 string
|
||||
bloomRoot, prefix2 = pm.getHelperTrie(1, req.Req.SectionIdx)
|
||||
p.Log().Debug("Got bloomRoot, prefix=", "bloomRoot", bloomRoot, "prefix2", prefix2)
|
||||
|
||||
var roots [3]common.Hash
|
||||
roots[0] = sectionHead
|
||||
roots[1] = chtRoot
|
||||
roots[2] = bloomRoot
|
||||
|
||||
reqCnt := 1
|
||||
bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost)
|
||||
pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost)
|
||||
result := p.SendCheckpoint(req.ReqID, bv, roots)
|
||||
p.Log().Debug("Called SendCheckpoint ok with ", "result", result)
|
||||
return result
|
||||
|
||||
case CheckpointMsg:
|
||||
if pm.odr == nil {
|
||||
return errResp(ErrUnexpectedResponse, "")
|
||||
}
|
||||
|
||||
p.Log().Debug("Received CheckpointMsg response", "msg", msg)
|
||||
var resp struct {
|
||||
ReqID, BV uint64
|
||||
Roots [3]common.Hash
|
||||
}
|
||||
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: MsgCheckpoint,
|
||||
ReqID: resp.ReqID,
|
||||
Obj: resp.Roots,
|
||||
}
|
||||
|
||||
default:
|
||||
p.Log().Trace("Received unknown message", "code", msg.Code)
|
||||
return errResp(ErrInvalidMsgCode, "%v", msg.Code)
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ const (
|
|||
MsgProofsV2
|
||||
MsgHeaderProofs
|
||||
MsgHelperTrieProofs
|
||||
MsgCheckpoint
|
||||
)
|
||||
|
||||
// Msg encodes a LES message that delivers reply data for a request
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ func LesRequest(req light.OdrRequest) LesOdrRequest {
|
|||
return (*ChtRequest)(r)
|
||||
case *light.BloomRequest:
|
||||
return (*BloomRequest)(r)
|
||||
case *light.CheckpointRequest:
|
||||
return (*CheckpointRequest)(r)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
|
@ -543,6 +545,61 @@ func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// status.im request 320
|
||||
|
||||
type CheckpointReq struct {
|
||||
SectionIdx uint64
|
||||
}
|
||||
|
||||
// ODR request type for requesting a non-trusted checkpoint (CHT and Bloom hash values)
|
||||
type CheckpointRequest light.CheckpointRequest
|
||||
|
||||
// GetCost returns the cost of the given ODR request according to the serving
|
||||
// peer's cost table (implementation of LesOdrRequest)
|
||||
func (r *CheckpointRequest) GetCost(peer *peer) uint64 {
|
||||
return peer.GetRequestCost(GetCheckpointMsg, 1)
|
||||
}
|
||||
|
||||
// CanSend tells if a certain peer is suitable for serving the given request
|
||||
func (r *CheckpointRequest) CanSend(peer *peer) bool {
|
||||
log.Debug("CheckpointRequest.CanSend", "peer", peer)
|
||||
peer.lock.RLock()
|
||||
defer peer.lock.RUnlock()
|
||||
|
||||
if peer.version < lpv2 {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
||||
func (r *CheckpointRequest) Request(reqID uint64, peer *peer) error {
|
||||
peer.Log().Debug("Requesting Checkpoint", "SectionIdx", r.SectionIdx)
|
||||
req := CheckpointReq{SectionIdx: r.SectionIdx}
|
||||
return peer.RequestCheckpoint(reqID, r.GetCost(peer), req)
|
||||
}
|
||||
|
||||
// Validate 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 *CheckpointRequest) Validate(db ethdb.Database, msg *Msg) error {
|
||||
// Ensure we have a correct message
|
||||
if msg.MsgType != MsgCheckpoint {
|
||||
return errInvalidMessageType
|
||||
}
|
||||
|
||||
log.Debug("Got GetCheckpoint response: ", "msg", msg)
|
||||
hashes := msg.Obj.([3]common.Hash)
|
||||
r.SectionHead = hashes[0]
|
||||
r.ChtRoot = hashes[1]
|
||||
r.BloomRoot = hashes[2]
|
||||
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 {
|
||||
|
|
|
|||
19
les/peer.go
19
les/peer.go
|
|
@ -325,6 +325,25 @@ func (p *peer) SendTxs(reqID, cost uint64, txs types.Transactions) error {
|
|||
}
|
||||
}
|
||||
|
||||
// RequestCheckpoint a checkpoint at the specified section index from a remote node.
|
||||
func (p *peer) RequestCheckpoint(reqID, cost uint64, req CheckpointReq) error {
|
||||
p.Log().Debug("Fetching checkpoint", "req", req, "p.version", p.version, "lpv1", lpv1)
|
||||
switch p.version {
|
||||
case lpv1:
|
||||
p.Log().Info("RequestCheckpoint: lpv1 so doing nothing")
|
||||
return nil
|
||||
case lpv2:
|
||||
return sendRequest(p.rw, GetCheckpointMsg, reqID, cost, req)
|
||||
default:
|
||||
panic(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// SendCheckpoint sends a checkpoint, corresponding to the one requested.
|
||||
func (p *peer) SendCheckpoint(reqID, bv uint64, roots [3]common.Hash) error {
|
||||
return sendResponse(p.rw, CheckpointMsg, reqID, bv, roots)
|
||||
}
|
||||
|
||||
type keyValueEntry struct {
|
||||
Key string
|
||||
Value rlp.RawValue
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ var (
|
|||
)
|
||||
|
||||
// Number of implemented message corresponding to different protocol versions.
|
||||
var ProtocolLengths = map[uint]uint64{lpv1: 15, lpv2: 22}
|
||||
var ProtocolLengths = map[uint]uint64{lpv1: 15, lpv2: 24}
|
||||
|
||||
const (
|
||||
NetworkId = 1
|
||||
|
|
@ -79,6 +79,8 @@ const (
|
|||
SendTxV2Msg = 0x13
|
||||
GetTxStatusMsg = 0x14
|
||||
TxStatusMsg = 0x15
|
||||
GetCheckpointMsg = 0x16
|
||||
CheckpointMsg = 0x17
|
||||
)
|
||||
|
||||
type errCode int
|
||||
|
|
|
|||
31
les/sync.go
31
les/sync.go
|
|
@ -20,9 +20,11 @@ import (
|
|||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/light"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -79,6 +81,35 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
|
|||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
updateChtFromPeer(pm, peer, ctx)
|
||||
pm.blockchain.(*light.LightChain).SyncCht(ctx)
|
||||
pm.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync)
|
||||
}
|
||||
|
||||
// Status.im issue 320: Use GetHeaderProofs to download the latest CHT from a peer.
|
||||
func updateChtFromPeer(pm *ProtocolManager, peer *peer, ctx context.Context) {
|
||||
log.Info("Downloading latest CHT root from peer", "peer.headBlockInfo", peer.headBlockInfo())
|
||||
|
||||
// Formula from lightchain.go:SyncCht: num := cht.Number*ChtFrequency – 1
|
||||
var hbl = peer.headBlockInfo()
|
||||
var peerHeadBlockNum = hbl.Number
|
||||
log.Debug("UpdateChtFromPeer", "peerHeadBlockNum", peerHeadBlockNum)
|
||||
|
||||
var sectionIdx uint64 = ((peerHeadBlockNum + 1) / light.ChtFrequency) - 1
|
||||
log.Debug("Retrieving checkpoint with: ", "sectionIdx", sectionIdx)
|
||||
|
||||
req := &light.CheckpointRequest{SectionIdx: uint64(sectionIdx)}
|
||||
pm.odr.Retrieve(ctx, req)
|
||||
log.Info("Retrieved checkpoint from peer: ",
|
||||
"SectionHead=", common.ToHex(req.SectionHead.Bytes()),
|
||||
"ChtRoot", common.ToHex(req.ChtRoot.Bytes()),
|
||||
"BloomTrieRoot", common.ToHex(req.BloomRoot.Bytes()))
|
||||
|
||||
pm.blockchain.(*light.LightChain).AddTrustedCheckpoint("live", sectionIdx, req.SectionHead, req.ChtRoot, req.BloomRoot)
|
||||
log.Debug("Added checkpoint to LightChain")
|
||||
|
||||
log.Info("Sanity check: can download some very old header?")
|
||||
var sanityBlock uint64 = hbl.Number / 100
|
||||
sanityHeader, err := pm.blockchain.(*light.LightChain).GetHeaderByNumberOdr(ctx, sanityBlock)
|
||||
log.Info("Sanity check result:", "sanityHeader", sanityHeader, "err", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bc.genesisBlock, _ = bc.GetBlockByNumber(NoOdr, 0)
|
||||
if bc.genesisBlock == nil {
|
||||
return nil, core.ErrNoGenesis
|
||||
|
|
@ -113,6 +114,13 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.
|
|||
return bc, nil
|
||||
}
|
||||
|
||||
func (self *LightChain) AddTrustedCheckpoint(name string, sectionIdx uint64,
|
||||
sectionHead common.Hash,
|
||||
chtRoot common.Hash, bloomTrieRoot common.Hash) {
|
||||
cp := trustedCheckpoint{name, sectionIdx, sectionHead, chtRoot, bloomTrieRoot}
|
||||
self.addTrustedCheckpoint(cp)
|
||||
}
|
||||
|
||||
// addTrustedCheckpoint adds a trusted checkpoint to the blockchain
|
||||
func (self *LightChain) addTrustedCheckpoint(cp trustedCheckpoint) {
|
||||
if self.odr.ChtIndexer() != nil {
|
||||
|
|
|
|||
13
light/odr.go
13
light/odr.go
|
|
@ -169,3 +169,16 @@ func (req *BloomRequest) StoreResult(db ethdb.Database) {
|
|||
core.WriteBloomBits(db, req.BitIdx, sectionIdx, sectionHead, req.BloomBits[i])
|
||||
}
|
||||
}
|
||||
|
||||
// CheckpointRequest is the ODR request type for retrieving a CHT+BloomRoot checkpoint
|
||||
type CheckpointRequest struct {
|
||||
OdrRequest
|
||||
SectionIdx uint64
|
||||
SectionHead common.Hash
|
||||
ChtRoot common.Hash
|
||||
BloomRoot common.Hash
|
||||
}
|
||||
|
||||
// StoreResult stores the retrieved data in local database
|
||||
func (req *CheckpointRequest) StoreResult(db ethdb.Database) {
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue