mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 01:13:45 +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
|
// handleMsg is invoked whenever an inbound message is received from a remote
|
||||||
// peer. The remote connection is torn down upon returning any error.
|
// 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)
|
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:
|
default:
|
||||||
p.Log().Trace("Received unknown message", "code", msg.Code)
|
p.Log().Trace("Received unknown message", "code", msg.Code)
|
||||||
return errResp(ErrInvalidMsgCode, "%v", msg.Code)
|
return errResp(ErrInvalidMsgCode, "%v", msg.Code)
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ const (
|
||||||
MsgProofsV2
|
MsgProofsV2
|
||||||
MsgHeaderProofs
|
MsgHeaderProofs
|
||||||
MsgHelperTrieProofs
|
MsgHelperTrieProofs
|
||||||
|
MsgCheckpoint
|
||||||
)
|
)
|
||||||
|
|
||||||
// Msg encodes a LES message that delivers reply data for a request
|
// 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)
|
return (*ChtRequest)(r)
|
||||||
case *light.BloomRequest:
|
case *light.BloomRequest:
|
||||||
return (*BloomRequest)(r)
|
return (*BloomRequest)(r)
|
||||||
|
case *light.CheckpointRequest:
|
||||||
|
return (*CheckpointRequest)(r)
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -543,6 +545,61 @@ func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error {
|
||||||
return nil
|
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
|
// 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.
|
// sets contain only the trie nodes necessary to make proofs pass.
|
||||||
type readTraceDB struct {
|
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 {
|
type keyValueEntry struct {
|
||||||
Key string
|
Key string
|
||||||
Value rlp.RawValue
|
Value rlp.RawValue
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Number of implemented message corresponding to different protocol versions.
|
// 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 (
|
const (
|
||||||
NetworkId = 1
|
NetworkId = 1
|
||||||
|
|
@ -79,6 +79,8 @@ const (
|
||||||
SendTxV2Msg = 0x13
|
SendTxV2Msg = 0x13
|
||||||
GetTxStatusMsg = 0x14
|
GetTxStatusMsg = 0x14
|
||||||
TxStatusMsg = 0x15
|
TxStatusMsg = 0x15
|
||||||
|
GetCheckpointMsg = 0x16
|
||||||
|
CheckpointMsg = 0x17
|
||||||
)
|
)
|
||||||
|
|
||||||
type errCode int
|
type errCode int
|
||||||
|
|
|
||||||
31
les/sync.go
31
les/sync.go
|
|
@ -20,9 +20,11 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/light"
|
"github.com/ethereum/go-ethereum/light"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -79,6 +81,35 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
updateChtFromPeer(pm, peer, ctx)
|
||||||
pm.blockchain.(*light.LightChain).SyncCht(ctx)
|
pm.blockchain.(*light.LightChain).SyncCht(ctx)
|
||||||
pm.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
bc.genesisBlock, _ = bc.GetBlockByNumber(NoOdr, 0)
|
bc.genesisBlock, _ = bc.GetBlockByNumber(NoOdr, 0)
|
||||||
if bc.genesisBlock == nil {
|
if bc.genesisBlock == nil {
|
||||||
return nil, core.ErrNoGenesis
|
return nil, core.ErrNoGenesis
|
||||||
|
|
@ -113,6 +114,13 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.
|
||||||
return bc, nil
|
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
|
// addTrustedCheckpoint adds a trusted checkpoint to the blockchain
|
||||||
func (self *LightChain) addTrustedCheckpoint(cp trustedCheckpoint) {
|
func (self *LightChain) addTrustedCheckpoint(cp trustedCheckpoint) {
|
||||||
if self.odr.ChtIndexer() != nil {
|
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])
|
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