les, light: add GetCheckpoint to LES (status.im issue 320)

This commit is contained in:
pacamara 2017-12-06 15:30:24 +00:00
parent 43dd8e62fc
commit 2f607c2726
8 changed files with 190 additions and 2 deletions

View file

@ -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)

View file

@ -77,6 +77,7 @@ const (
MsgProofsV2
MsgHeaderProofs
MsgHelperTrieProofs
MsgCheckpoint
)
// Msg encodes a LES message that delivers reply data for a request

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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)
}

View file

@ -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 {

View file

@ -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) {
}