Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

les: introduce forkID #21974

Merged
merged 2 commits into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 16 additions & 12 deletions les/client_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/light"
Expand All @@ -36,6 +37,7 @@ import (
// responses.
type clientHandler struct {
ulc *ulc
forkFilter forkid.Filter
checkpoint *params.TrustedCheckpoint
fetcher *lightFetcher
downloader *downloader.Downloader
Expand All @@ -48,6 +50,7 @@ type clientHandler struct {

func newClientHandler(ulcServers []string, ulcFraction int, checkpoint *params.TrustedCheckpoint, backend *LightEthereum) *clientHandler {
handler := &clientHandler{
forkFilter: forkid.NewFilter(backend.blockchain),
checkpoint: checkpoint,
backend: backend,
closeCh: make(chan struct{}),
Expand Down Expand Up @@ -102,7 +105,8 @@ func (h *clientHandler) handle(p *serverPeer) error {
p.Log().Debug("Light Ethereum peer connected", "name", p.Name())

// Execute the LES handshake
if err := p.Handshake(h.backend.blockchain.Genesis().Hash()); err != nil {
forkid := forkid.NewID(h.backend.blockchain.Config(), h.backend.genesis, h.backend.blockchain.CurrentHeader().Number.Uint64())
if err := p.Handshake(h.backend.blockchain.Genesis().Hash(), forkid, h.forkFilter); err != nil {
p.Log().Debug("Light Ethereum handshake failed", "err", err)
return err
}
Expand Down Expand Up @@ -153,8 +157,8 @@ func (h *clientHandler) handleMsg(p *serverPeer) error {
var deliverMsg *Msg

// Handle the message depending on its contents
switch msg.Code {
case AnnounceMsg:
switch {
case msg.Code == AnnounceMsg:
p.Log().Trace("Received announce message")
var req announceData
if err := msg.Decode(&req); err != nil {
Expand Down Expand Up @@ -187,7 +191,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error {
p.updateHead(req.Hash, req.Number, req.Td)
h.fetcher.announce(p, &req)
}
case BlockHeadersMsg:
case msg.Code == BlockHeadersMsg:
p.Log().Trace("Received block header response message")
var resp struct {
ReqID, BV uint64
Expand All @@ -210,7 +214,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error {
log.Debug("Failed to deliver headers", "err", err)
}
}
case BlockBodiesMsg:
case msg.Code == BlockBodiesMsg:
p.Log().Trace("Received block bodies response")
var resp struct {
ReqID, BV uint64
Expand All @@ -226,7 +230,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error {
ReqID: resp.ReqID,
Obj: resp.Data,
}
case CodeMsg:
case msg.Code == CodeMsg:
p.Log().Trace("Received code response")
var resp struct {
ReqID, BV uint64
Expand All @@ -242,7 +246,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error {
ReqID: resp.ReqID,
Obj: resp.Data,
}
case ReceiptsMsg:
case msg.Code == ReceiptsMsg:
p.Log().Trace("Received receipts response")
var resp struct {
ReqID, BV uint64
Expand All @@ -258,7 +262,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error {
ReqID: resp.ReqID,
Obj: resp.Receipts,
}
case ProofsV2Msg:
case msg.Code == ProofsV2Msg:
p.Log().Trace("Received les/2 proofs response")
var resp struct {
ReqID, BV uint64
Expand All @@ -274,7 +278,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error {
ReqID: resp.ReqID,
Obj: resp.Data,
}
case HelperTrieProofsMsg:
case msg.Code == HelperTrieProofsMsg:
p.Log().Trace("Received helper trie proof response")
var resp struct {
ReqID, BV uint64
Expand All @@ -290,7 +294,7 @@ func (h *clientHandler) handleMsg(p *serverPeer) error {
ReqID: resp.ReqID,
Obj: resp.Data,
}
case TxStatusMsg:
case msg.Code == TxStatusMsg:
p.Log().Trace("Received tx status response")
var resp struct {
ReqID, BV uint64
Expand All @@ -306,11 +310,11 @@ func (h *clientHandler) handleMsg(p *serverPeer) error {
ReqID: resp.ReqID,
Obj: resp.Status,
}
case StopMsg:
case msg.Code == StopMsg && p.version >= lpv3:
p.freeze()
h.backend.retriever.frozen(p)
p.Log().Debug("Service stopped")
case ResumeMsg:
case msg.Code == ResumeMsg && p.version >= lpv3:
var bv uint64
if err := msg.Decode(&bv); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
Expand Down
27 changes: 22 additions & 5 deletions les/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/les/flowcontrol"
Expand Down Expand Up @@ -246,7 +247,7 @@ func (p *peerCommons) sendReceiveHandshake(sendList keyValueList) (keyValueList,
// network IDs, difficulties, head and genesis blocks. Besides the basic handshake
// fields, server and client can exchange and resolve some specified fields through
// two callback functions.
func (p *peerCommons) handshake(td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, sendCallback func(*keyValueList), recvCallback func(keyValueMap) error) error {
func (p *peerCommons) handshake(td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter, sendCallback func(*keyValueList), recvCallback func(keyValueMap) error) error {
p.lock.Lock()
defer p.lock.Unlock()

Expand All @@ -262,6 +263,12 @@ func (p *peerCommons) handshake(td *big.Int, head common.Hash, headNum uint64, g
send = send.add("headNum", headNum)
send = send.add("genesisHash", genesis)

// If the protocol version is beyond les4, then pass the forkID
// as well. Check http://eips.ethereum.org/EIPS/eip-2124 for more
// spec detail.
if p.version >= lpv4 {
send = send.add("forkID", forkID)
}
// Add client-specified or server-specified fields
if sendCallback != nil {
sendCallback(&send)
Expand Down Expand Up @@ -295,6 +302,16 @@ func (p *peerCommons) handshake(td *big.Int, head common.Hash, headNum uint64, g
if int(rVersion) != p.version {
return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", rVersion, p.version)
}
// Check forkID if the protocol version is beyond the les4
if p.version >= lpv4 {
var forkID forkid.ID
if err := recv.get("forkID", &forkID); err != nil {
return err
}
if err := forkFilter(forkID); err != nil {
return errResp(ErrForkIDRejected, "%v", err)
}
}
if recvCallback != nil {
return recvCallback(recv)
}
Expand Down Expand Up @@ -561,10 +578,10 @@ func (p *serverPeer) updateHead(hash common.Hash, number uint64, td *big.Int) {

// Handshake executes the les protocol handshake, negotiating version number,
// network IDs and genesis blocks.
func (p *serverPeer) Handshake(genesis common.Hash) error {
func (p *serverPeer) Handshake(genesis common.Hash, forkid forkid.ID, forkFilter forkid.Filter) error {
// Note: there is no need to share local head with a server but older servers still
// require these fields so we announce zero values.
return p.handshake(common.Big0, common.Hash{}, 0, genesis, func(lists *keyValueList) {
return p.handshake(common.Big0, common.Hash{}, 0, genesis, forkid, forkFilter, func(lists *keyValueList) {
// Add some client-specific handshake fields
//
// Enable signed announcement randomly even the server is not trusted.
Expand Down Expand Up @@ -944,11 +961,11 @@ func (p *clientPeer) freezeClient() {

// Handshake executes the les protocol handshake, negotiating version number,
// network IDs, difficulties, head and genesis blocks.
func (p *clientPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, server *LesServer) error {
func (p *clientPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter, server *LesServer) error {
// Note: clientPeer.headInfo should contain the last head announced to the client by us.
// The values announced in the handshake are dummy values for compatibility reasons and should be ignored.
p.headInfo = blockInfo{Hash: head, Number: headNum, Td: td}
return p.handshake(td, head, headNum, genesis, func(lists *keyValueList) {
return p.handshake(td, head, headNum, genesis, forkID, forkFilter, func(lists *keyValueList) {
// Add some information which services server can offer.
if !server.config.UltraLightOnlyAnnounce {
*lists = (*lists).add("serveHeaders", nil)
Expand Down
23 changes: 21 additions & 2 deletions les/peer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ import (
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params"
)

type testServerPeerSub struct {
Expand Down Expand Up @@ -91,6 +96,14 @@ func TestPeerSubscription(t *testing.T) {
checkPeers(sub.unregCh)
}

type fakeChain struct{}

func (f *fakeChain) Config() *params.ChainConfig { return params.MainnetChainConfig }
func (f *fakeChain) Genesis() *types.Block {
return core.DefaultGenesisBlock().ToBlock(rawdb.NewMemoryDatabase())
}
func (f *fakeChain) CurrentHeader() *types.Header { return &types.Header{Number: big.NewInt(10000000)} }

func TestHandshake(t *testing.T) {
// Create a message pipe to communicate through
app, net := p2p.MsgPipe()
Expand All @@ -110,15 +123,21 @@ func TestHandshake(t *testing.T) {
head = common.HexToHash("deadbeef")
headNum = uint64(10)
genesis = common.HexToHash("cafebabe")

chain1, chain2 = &fakeChain{}, &fakeChain{}
forkID1 = forkid.NewID(chain1.Config(), chain1.Genesis().Hash(), chain1.CurrentHeader().Number.Uint64())
forkID2 = forkid.NewID(chain2.Config(), chain2.Genesis().Hash(), chain2.CurrentHeader().Number.Uint64())
filter1, filter2 = forkid.NewFilter(chain1), forkid.NewFilter(chain2)
)

go func() {
errCh1 <- peer1.handshake(td, head, headNum, genesis, func(list *keyValueList) {
errCh1 <- peer1.handshake(td, head, headNum, genesis, forkID1, filter1, func(list *keyValueList) {
var announceType uint64 = announceTypeSigned
*list = (*list).add("announceType", announceType)
}, nil)
}()
go func() {
errCh2 <- peer2.handshake(td, head, headNum, genesis, nil, func(recv keyValueMap) error {
errCh2 <- peer2.handshake(td, head, headNum, genesis, forkID2, filter2, nil, func(recv keyValueMap) error {
var reqType uint64
err := recv.get("announceType", &reqType)
if err != nil {
Expand Down
5 changes: 4 additions & 1 deletion les/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
const (
lpv2 = 2
lpv3 = 3
lpv4 = 4
)

// Supported versions of the les protocol (first is primary)
Expand All @@ -44,7 +45,7 @@ var (
)

// Number of implemented message corresponding to different protocol versions.
var ProtocolLengths = map[uint]uint64{lpv2: 22, lpv3: 24}
var ProtocolLengths = map[uint]uint64{lpv2: 22, lpv3: 24, lpv4: 24}

const (
NetworkId = 1
Expand Down Expand Up @@ -150,6 +151,7 @@ const (
ErrInvalidResponse
ErrTooManyTimeouts
ErrMissingKey
ErrForkIDRejected
)

func (e errCode) String() string {
Expand All @@ -172,6 +174,7 @@ var errorToString = map[int]string{
ErrInvalidResponse: "Invalid response",
ErrTooManyTimeouts: "Too many request timeouts",
ErrMissingKey: "Key missing from list",
ErrForkIDRejected: "ForkID rejected",
}

// announceData is the network packet for the block announcements.
Expand Down
6 changes: 5 additions & 1 deletion les/server_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
Expand Down Expand Up @@ -66,6 +67,7 @@ var (
// serverHandler is responsible for serving light client and process
// all incoming light requests.
type serverHandler struct {
forkFilter forkid.Filter
blockchain *core.BlockChain
chainDb ethdb.Database
txpool *core.TxPool
Expand All @@ -81,6 +83,7 @@ type serverHandler struct {

func newServerHandler(server *LesServer, blockchain *core.BlockChain, chainDb ethdb.Database, txpool *core.TxPool, synced func() bool) *serverHandler {
handler := &serverHandler{
forkFilter: forkid.NewFilter(blockchain),
server: server,
blockchain: blockchain,
chainDb: chainDb,
Expand Down Expand Up @@ -121,8 +124,9 @@ func (h *serverHandler) handle(p *clientPeer) error {
hash = head.Hash()
number = head.Number.Uint64()
td = h.blockchain.GetTd(hash, number)
forkID = forkid.NewID(h.blockchain.Config(), h.blockchain.Genesis().Hash(), h.blockchain.CurrentBlock().NumberU64())
)
if err := p.Handshake(td, hash, number, h.blockchain.Genesis().Hash(), h.server); err != nil {
if err := p.Handshake(td, hash, number, h.blockchain.Genesis().Hash(), forkID, h.forkFilter, h.server); err != nil {
p.Log().Debug("Light Ethereum handshake failed", "err", err)
return err
}
Expand Down