From 737164a4d62e46cbf6f7bfe7b320dd9e62b42e6b Mon Sep 17 00:00:00 2001 From: battlmonstr Date: Mon, 27 May 2024 10:43:13 +0200 Subject: [PATCH 1/2] p2p/sentry: sentry doesn't start with ErrNoHead (#10454) ReadCurrentHeaderHavingBody must never return nil, but IRL sometimes it does. In this case the node gets stuck, because it is not possible to start sentry and make progress. Use genesis data when rawdb.ReadCurrentHeaderHavingBody returns nil. --- cmd/integration/commands/stages.go | 3 ++ eth/backend.go | 1 + .../sentry_multi_client.go | 4 +- p2p/sentry/status_data_provider.go | 53 ++++++++++++++----- turbo/stages/mock/mock_sentry.go | 1 + 5 files changed, 47 insertions(+), 15 deletions(-) diff --git a/cmd/integration/commands/stages.go b/cmd/integration/commands/stages.go index d2a4b54d493..d0f06667049 100644 --- a/cmd/integration/commands/stages.go +++ b/cmd/integration/commands/stages.go @@ -18,6 +18,8 @@ import ( "github.com/ledgerwatch/secp256k1" "github.com/spf13/cobra" + "golang.org/x/sync/errgroup" + chain2 "github.com/ledgerwatch/erigon-lib/chain" common2 "github.com/ledgerwatch/erigon-lib/common" libcommon "github.com/ledgerwatch/erigon-lib/common" @@ -1621,6 +1623,7 @@ func newSync(ctx context.Context, db kv.RwDB, miningConfig *params.MiningConfig, chainConfig, genesisBlock, chainConfig.ChainID.Uint64(), + logger, ) maxBlockBroadcastPeers := func(header *types.Header) uint { return 0 } diff --git a/eth/backend.go b/eth/backend.go index 4816822d42d..ea632176776 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -573,6 +573,7 @@ func New(ctx context.Context, stack *node.Node, config *ethconfig.Config, logger chainConfig, genesis, backend.config.NetworkID, + logger, ) // limit "new block" broadcasts to at most 10 random peers at time diff --git a/p2p/sentry/sentry_multi_client/sentry_multi_client.go b/p2p/sentry/sentry_multi_client/sentry_multi_client.go index ef62bfaa84a..0f32615b949 100644 --- a/p2p/sentry/sentry_multi_client/sentry_multi_client.go +++ b/p2p/sentry/sentry_multi_client/sentry_multi_client.go @@ -156,9 +156,7 @@ func SentryReconnectAndPumpStreamLoop[TMessage interface{}]( statusData, err := statusDataFactory(ctx) if err != nil { - if !errors.Is(err, sentry.ErrNoHead) { - logger.Error("SentryReconnectAndPumpStreamLoop: statusDataFactory error", "stream", streamName, "err", err) - } + logger.Error("SentryReconnectAndPumpStreamLoop: statusDataFactory error", "stream", streamName, "err", err) time.Sleep(time.Second) continue } diff --git a/p2p/sentry/status_data_provider.go b/p2p/sentry/status_data_provider.go index 91137e5c321..83da0d3bad3 100644 --- a/p2p/sentry/status_data_provider.go +++ b/p2p/sentry/status_data_provider.go @@ -7,6 +7,7 @@ import ( "math/big" "github.com/holiman/uint256" + "github.com/ledgerwatch/log/v3" "github.com/ledgerwatch/erigon-lib/chain" libcommon "github.com/ledgerwatch/erigon-lib/common" @@ -32,8 +33,11 @@ type StatusDataProvider struct { networkId uint64 genesisHash libcommon.Hash + genesisHead ChainHead heightForks []uint64 timeForks []uint64 + + logger log.Logger } func NewStatusDataProvider( @@ -41,11 +45,14 @@ func NewStatusDataProvider( chainConfig *chain.Config, genesis *types.Block, networkId uint64, + logger log.Logger, ) *StatusDataProvider { s := &StatusDataProvider{ db: db, networkId: networkId, genesisHash: genesis.Hash(), + genesisHead: makeGenesisChainHead(genesis), + logger: logger, } s.heightForks, s.timeForks = forkid.GatherForks(chainConfig, genesis.Time()) @@ -53,6 +60,32 @@ func NewStatusDataProvider( return s } +func uint256FromBigInt(num *big.Int) (*uint256.Int, error) { + if num == nil { + num = new(big.Int) + } + num256 := new(uint256.Int) + overflow := num256.SetFromBig(num) + if overflow { + return nil, fmt.Errorf("uint256FromBigInt: big.Int greater than 2^256-1") + } + return num256, nil +} + +func makeGenesisChainHead(genesis *types.Block) ChainHead { + genesisDifficulty, err := uint256FromBigInt(genesis.Difficulty()) + if err != nil { + panic(fmt.Errorf("makeGenesisChainHead: difficulty conversion error: %w", err)) + } + + return ChainHead{ + HeadHeight: genesis.NumberU64(), + HeadTime: genesis.Time(), + HeadHash: genesis.Hash(), + HeadTd: genesisDifficulty, + } +} + func (s *StatusDataProvider) makeStatusData(head ChainHead) *proto_sentry.StatusData { return &proto_sentry.StatusData{ NetworkId: s.networkId, @@ -71,6 +104,10 @@ func (s *StatusDataProvider) makeStatusData(head ChainHead) *proto_sentry.Status func (s *StatusDataProvider) GetStatusData(ctx context.Context) (*proto_sentry.StatusData, error) { chainHead, err := ReadChainHead(ctx, s.db) if err != nil { + if errors.Is(err, ErrNoHead) { + s.logger.Warn("sentry.StatusDataProvider: The canonical chain current header not found in the database. Check the database consistency. Using genesis as a fallback.") + return s.makeStatusData(s.genesisHead), nil + } return nil, err } return s.makeStatusData(chainHead), err @@ -84,23 +121,15 @@ func ReadChainHeadWithTx(tx kv.Tx) (ChainHead, error) { height := header.Number.Uint64() hash := header.Hash() - - var time uint64 - if header != nil { - time = header.Time - } + time := header.Time td, err := rawdb.ReadTd(tx, hash, height) if err != nil { return ChainHead{}, fmt.Errorf("ReadChainHead: ReadTd error at height %d and hash %s: %w", height, hash, err) } - if td == nil { - td = new(big.Int) - } - td256 := new(uint256.Int) - overflow := td256.SetFromBig(td) - if overflow { - return ChainHead{}, fmt.Errorf("ReadChainHead: total difficulty higher than 2^256-1") + td256, err := uint256FromBigInt(td) + if err != nil { + return ChainHead{}, fmt.Errorf("ReadChainHead: total difficulty conversion error: %w", err) } return ChainHead{height, time, hash, td256}, nil diff --git a/turbo/stages/mock/mock_sentry.go b/turbo/stages/mock/mock_sentry.go index 3994c6508b6..8eab77cdf6e 100644 --- a/turbo/stages/mock/mock_sentry.go +++ b/turbo/stages/mock/mock_sentry.go @@ -371,6 +371,7 @@ func MockWithEverything(tb testing.TB, gspec *types.Genesis, key *ecdsa.PrivateK mock.ChainConfig, mock.Genesis, mock.ChainConfig.ChainID.Uint64(), + logger, ) maxBlockBroadcastPeers := func(header *types.Header) uint { return 0 } From 15c3325c8fe1edfc3326183d0710ba4be8eb69bf Mon Sep 17 00:00:00 2001 From: battlmonstr Date: Tue, 28 May 2024 10:18:12 +0200 Subject: [PATCH 2/2] fix --- cmd/integration/commands/stages.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/integration/commands/stages.go b/cmd/integration/commands/stages.go index d0f06667049..428903d3fab 100644 --- a/cmd/integration/commands/stages.go +++ b/cmd/integration/commands/stages.go @@ -18,8 +18,6 @@ import ( "github.com/ledgerwatch/secp256k1" "github.com/spf13/cobra" - "golang.org/x/sync/errgroup" - chain2 "github.com/ledgerwatch/erigon-lib/chain" common2 "github.com/ledgerwatch/erigon-lib/common" libcommon "github.com/ledgerwatch/erigon-lib/common"