forked from cerc-io/plugeth
all: remove notion of trusted checkpoints in the post-merge world (#27147)
* all: remove notion of trusted checkpoints in the post-merge world * light: remove unused function * eth/ethconfig, les: remove unused config option * les: make linter happy --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com>
This commit is contained in:
co-authored by
Gary Rong
parent
d3ece3a07c
commit
1e556d220c
@@ -19,8 +19,6 @@ package eth
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -38,7 +36,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// testEthHandler is a mock event handler to listen for inbound network requests
|
||||
@@ -459,148 +456,6 @@ func testTransactionPropagation(t *testing.T, protocol uint) {
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that post eth protocol handshake, clients perform a mutual checkpoint
|
||||
// challenge to validate each other's chains. Hash mismatches, or missing ones
|
||||
// during a fast sync should lead to the peer getting dropped.
|
||||
func TestCheckpointChallenge(t *testing.T) {
|
||||
tests := []struct {
|
||||
syncmode downloader.SyncMode
|
||||
checkpoint bool
|
||||
timeout bool
|
||||
empty bool
|
||||
match bool
|
||||
drop bool
|
||||
}{
|
||||
// If checkpointing is not enabled locally, don't challenge and don't drop
|
||||
{downloader.FullSync, false, false, false, false, false},
|
||||
{downloader.SnapSync, false, false, false, false, false},
|
||||
|
||||
// If checkpointing is enabled locally and remote response is empty, only drop during fast sync
|
||||
{downloader.FullSync, true, false, true, false, false},
|
||||
{downloader.SnapSync, true, false, true, false, true}, // Special case, fast sync, unsynced peer
|
||||
|
||||
// If checkpointing is enabled locally and remote response mismatches, always drop
|
||||
{downloader.FullSync, true, false, false, false, true},
|
||||
{downloader.SnapSync, true, false, false, false, true},
|
||||
|
||||
// If checkpointing is enabled locally and remote response matches, never drop
|
||||
{downloader.FullSync, true, false, false, true, false},
|
||||
{downloader.SnapSync, true, false, false, true, false},
|
||||
|
||||
// If checkpointing is enabled locally and remote times out, always drop
|
||||
{downloader.FullSync, true, true, false, true, true},
|
||||
{downloader.SnapSync, true, true, false, true, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(fmt.Sprintf("sync %v checkpoint %v timeout %v empty %v match %v", tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match), func(t *testing.T) {
|
||||
testCheckpointChallenge(t, tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match, tt.drop)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpoint bool, timeout bool, empty bool, match bool, drop bool) {
|
||||
// Reduce the checkpoint handshake challenge timeout
|
||||
defer func(old time.Duration) { syncChallengeTimeout = old }(syncChallengeTimeout)
|
||||
syncChallengeTimeout = 250 * time.Millisecond
|
||||
|
||||
// Create a test handler and inject a CHT into it. The injection is a bit
|
||||
// ugly, but it beats creating everything manually just to avoid reaching
|
||||
// into the internals a bit.
|
||||
handler := newTestHandler()
|
||||
defer handler.close()
|
||||
|
||||
if syncmode == downloader.SnapSync {
|
||||
atomic.StoreUint32(&handler.handler.snapSync, 1)
|
||||
} else {
|
||||
atomic.StoreUint32(&handler.handler.snapSync, 0)
|
||||
}
|
||||
var response *types.Header
|
||||
if checkpoint {
|
||||
number := (uint64(rand.Intn(500))+1)*params.CHTFrequency - 1
|
||||
response = &types.Header{Number: big.NewInt(int64(number)), Extra: []byte("valid")}
|
||||
|
||||
handler.handler.checkpointNumber = number
|
||||
handler.handler.checkpointHash = response.Hash()
|
||||
}
|
||||
|
||||
// Create a challenger peer and a challenged one.
|
||||
p2pLocal, p2pRemote := p2p.MsgPipe()
|
||||
defer p2pLocal.Close()
|
||||
defer p2pRemote.Close()
|
||||
|
||||
local := eth.NewPeer(eth.ETH66, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pLocal), p2pLocal, handler.txpool)
|
||||
remote := eth.NewPeer(eth.ETH66, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pRemote), p2pRemote, handler.txpool)
|
||||
defer local.Close()
|
||||
defer remote.Close()
|
||||
|
||||
handlerDone := make(chan struct{})
|
||||
go func() {
|
||||
defer close(handlerDone)
|
||||
handler.handler.runEthPeer(local, func(peer *eth.Peer) error {
|
||||
return eth.Handle((*ethHandler)(handler.handler), peer)
|
||||
})
|
||||
}()
|
||||
|
||||
// Run the handshake locally to avoid spinning up a remote handler.
|
||||
var (
|
||||
genesis = handler.chain.Genesis()
|
||||
head = handler.chain.CurrentBlock()
|
||||
td = handler.chain.GetTd(head.Hash(), head.Number.Uint64())
|
||||
)
|
||||
if err := remote.Handshake(1, td, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil {
|
||||
t.Fatalf("failed to run protocol handshake")
|
||||
}
|
||||
// Connect a new peer and check that we receive the checkpoint challenge.
|
||||
if checkpoint {
|
||||
msg, err := p2pRemote.ReadMsg()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read checkpoint challenge: %v", err)
|
||||
}
|
||||
request := new(eth.GetBlockHeadersPacket66)
|
||||
if err := msg.Decode(request); err != nil {
|
||||
t.Fatalf("failed to decode checkpoint challenge: %v", err)
|
||||
}
|
||||
query := request.GetBlockHeadersPacket
|
||||
if query.Origin.Number != response.Number.Uint64() || query.Amount != 1 || query.Skip != 0 || query.Reverse {
|
||||
t.Fatalf("challenge mismatch: have [%d, %d, %d, %v] want [%d, %d, %d, %v]",
|
||||
query.Origin.Number, query.Amount, query.Skip, query.Reverse,
|
||||
response.Number.Uint64(), 1, 0, false)
|
||||
}
|
||||
// Create a block to reply to the challenge if no timeout is simulated.
|
||||
if !timeout {
|
||||
if empty {
|
||||
if err := remote.ReplyBlockHeadersRLP(request.RequestId, []rlp.RawValue{}); err != nil {
|
||||
t.Fatalf("failed to answer challenge: %v", err)
|
||||
}
|
||||
} else if match {
|
||||
responseRlp, _ := rlp.EncodeToBytes(response)
|
||||
if err := remote.ReplyBlockHeadersRLP(request.RequestId, []rlp.RawValue{responseRlp}); err != nil {
|
||||
t.Fatalf("failed to answer challenge: %v", err)
|
||||
}
|
||||
} else {
|
||||
responseRlp, _ := rlp.EncodeToBytes(&types.Header{Number: response.Number})
|
||||
if err := remote.ReplyBlockHeadersRLP(request.RequestId, []rlp.RawValue{responseRlp}); err != nil {
|
||||
t.Fatalf("failed to answer challenge: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Wait until the test timeout passes to ensure proper cleanup
|
||||
time.Sleep(syncChallengeTimeout + 300*time.Millisecond)
|
||||
|
||||
// Verify that the remote peer is maintained or dropped.
|
||||
if drop {
|
||||
<-handlerDone
|
||||
if peers := handler.handler.peers.len(); peers != 0 {
|
||||
t.Fatalf("peer count mismatch: have %d, want %d", peers, 0)
|
||||
}
|
||||
} else {
|
||||
if peers := handler.handler.peers.len(); peers != 1 {
|
||||
t.Fatalf("peer count mismatch: have %d, want %d", peers, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that blocks are broadcast to a sqrt number of peers only.
|
||||
func TestBroadcastBlock1Peer(t *testing.T) { testBroadcastBlock(t, 1, 1) }
|
||||
func TestBroadcastBlock2Peers(t *testing.T) { testBroadcastBlock(t, 2, 1) }
|
||||
|
||||
Reference in New Issue
Block a user