erigon-pulse/p2p/sentry/sentry_grpc_server_test.go
2023-11-02 08:35:13 +07:00

198 lines
5.9 KiB
Go

package sentry
import (
"context"
"math/big"
"testing"
"time"
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon-lib/chain"
"github.com/ledgerwatch/erigon-lib/common/datadir"
"github.com/ledgerwatch/erigon-lib/direct"
"github.com/ledgerwatch/erigon-lib/gointerfaces"
proto_sentry "github.com/ledgerwatch/erigon-lib/gointerfaces/sentry"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/stretchr/testify/require"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/core/forkid"
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/state/temporal"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/p2p"
)
func testSentryServer(db kv.Getter, genesis *types.Genesis, genesisHash libcommon.Hash) *GrpcServer {
s := &GrpcServer{
ctx: context.Background(),
}
head := rawdb.ReadCurrentHeader(db)
headTd, err := rawdb.ReadTd(db, head.Hash(), head.Number.Uint64())
if err != nil {
panic(err)
}
headTd256 := new(uint256.Int)
headTd256.SetFromBig(headTd)
heightForks, timeForks := forkid.GatherForks(genesis.Config, genesis.Timestamp)
s.statusData = &proto_sentry.StatusData{
NetworkId: 1,
TotalDifficulty: gointerfaces.ConvertUint256IntToH256(headTd256),
BestHash: gointerfaces.ConvertHashToH256(head.Hash()),
MaxBlockHeight: head.Number.Uint64(),
MaxBlockTime: head.Time,
ForkData: &proto_sentry.Forks{
Genesis: gointerfaces.ConvertHashToH256(genesisHash),
HeightForks: heightForks,
TimeForks: timeForks,
},
}
return s
}
func startHandshake(
ctx context.Context,
status *proto_sentry.StatusData,
pipe *p2p.MsgPipeRW,
protocolVersion uint,
errChan chan *p2p.PeerError,
) {
go func() {
_, err := handShake(ctx, status, pipe, protocolVersion, protocolVersion)
errChan <- err
}()
}
// Tests that peers are correctly accepted (or rejected) based on the advertised
// fork IDs in the protocol handshake.
func TestForkIDSplit66(t *testing.T) { testForkIDSplit(t, direct.ETH66) }
func testForkIDSplit(t *testing.T, protocol uint) {
var (
ctx = context.Background()
configNoFork = &chain.Config{HomesteadBlock: big.NewInt(1), ChainID: big.NewInt(1)}
configProFork = &chain.Config{
ChainID: big.NewInt(1),
HomesteadBlock: big.NewInt(1),
TangerineWhistleBlock: big.NewInt(2),
SpuriousDragonBlock: big.NewInt(2),
ByzantiumBlock: big.NewInt(3),
}
_, dbNoFork, _ = temporal.NewTestDB(t, datadir.New(t.TempDir()), nil)
_, dbProFork, _ = temporal.NewTestDB(t, datadir.New(t.TempDir()), nil)
gspecNoFork = &types.Genesis{Config: configNoFork}
gspecProFork = &types.Genesis{Config: configProFork}
genesisNoFork = core.MustCommitGenesis(gspecNoFork, dbNoFork, "")
genesisProFork = core.MustCommitGenesis(gspecProFork, dbProFork, "")
)
var s1, s2 *GrpcServer
err := dbNoFork.Update(context.Background(), func(tx kv.RwTx) error {
s1 = testSentryServer(tx, gspecNoFork, genesisNoFork.Hash())
return nil
})
require.NoError(t, err)
err = dbProFork.Update(context.Background(), func(tx kv.RwTx) error {
s2 = testSentryServer(tx, gspecProFork, genesisProFork.Hash())
return nil
})
require.NoError(t, err)
// Both nodes should allow the other to connect (same genesis, next fork is the same)
p2pNoFork, p2pProFork := p2p.MsgPipe()
defer p2pNoFork.Close()
defer p2pProFork.Close()
errc := make(chan *p2p.PeerError, 2)
startHandshake(ctx, s1.GetStatus(), p2pNoFork, protocol, errc)
startHandshake(ctx, s2.GetStatus(), p2pProFork, protocol, errc)
for i := 0; i < 2; i++ {
select {
case err := <-errc:
if err != nil {
t.Fatalf("frontier nofork <-> profork failed: %v", err)
}
case <-time.After(250 * time.Millisecond):
t.Fatalf("frontier nofork <-> profork handler timeout")
}
}
// Progress into Homestead. Fork's match, so we don't care what the future holds
s1.statusData.MaxBlockHeight = 1
s2.statusData.MaxBlockHeight = 1
startHandshake(ctx, s1.GetStatus(), p2pNoFork, protocol, errc)
startHandshake(ctx, s2.GetStatus(), p2pProFork, protocol, errc)
for i := 0; i < 2; i++ {
select {
case err := <-errc:
if err != nil {
t.Fatalf("homestead nofork <-> profork failed: %v", err)
}
case <-time.After(250 * time.Millisecond):
t.Fatalf("frontier nofork <-> profork handler timeout")
}
}
// Progress into Spurious. Forks mismatch, signalling differing chains, reject
s1.statusData.MaxBlockHeight = 2
s2.statusData.MaxBlockHeight = 2
// Both nodes should allow the other to connect (same genesis, next fork is the same)
startHandshake(ctx, s1.GetStatus(), p2pNoFork, protocol, errc)
startHandshake(ctx, s2.GetStatus(), p2pProFork, protocol, errc)
var successes int
for i := 0; i < 2; i++ {
select {
case err := <-errc:
if err == nil {
successes++
if successes == 2 { // Only one side disconnects
t.Fatalf("fork ID rejection didn't happen")
}
}
case <-time.After(250 * time.Millisecond):
t.Fatalf("split peers not rejected")
}
}
}
func TestSentryServerImpl_SetStatusInitPanic(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Fatalf("panic during server initialization")
}
}()
configNoFork := &chain.Config{HomesteadBlock: big.NewInt(1), ChainID: big.NewInt(1)}
_, dbNoFork, _ := temporal.NewTestDB(t, datadir.New(t.TempDir()), nil)
gspecNoFork := &types.Genesis{Config: configNoFork}
genesisNoFork := core.MustCommitGenesis(gspecNoFork, dbNoFork, "")
ss := &GrpcServer{p2p: &p2p.Config{}}
_, err := ss.SetStatus(context.Background(), &proto_sentry.StatusData{
ForkData: &proto_sentry.Forks{Genesis: gointerfaces.ConvertHashToH256(genesisNoFork.Hash())},
})
if err == nil {
t.Fatalf("error expected")
}
// Should not panic here.
_, err = ss.SetStatus(context.Background(), &proto_sentry.StatusData{
ForkData: &proto_sentry.Forks{Genesis: gointerfaces.ConvertHashToH256(genesisNoFork.Hash())},
})
if err == nil {
t.Fatalf("error expected")
}
}