Commit 68dcaf41 authored by Tei Im's avatar Tei Im

op-node: Fix reorg depth check, Add skip-sanity-check flag

parent 0b3052c7
...@@ -221,6 +221,13 @@ var ( ...@@ -221,6 +221,13 @@ var (
Required: false, Required: false,
Value: false, Value: false,
} }
SkipSanityCheck = &cli.BoolFlag{
Name: "skip-sanity-check",
Usage: "Skip chain sanity check on pipeline reset",
EnvVars: prefixEnvVars("SKIP_SANITY_CHECK"),
Required: false,
Value: false,
}
) )
var requiredFlags = []cli.Flag{ var requiredFlags = []cli.Flag{
...@@ -260,6 +267,7 @@ var optionalFlags = []cli.Flag{ ...@@ -260,6 +267,7 @@ var optionalFlags = []cli.Flag{
BackupL2UnsafeSyncRPC, BackupL2UnsafeSyncRPC,
BackupL2UnsafeSyncRPCTrustRPC, BackupL2UnsafeSyncRPCTrustRPC,
L2EngineP2PEnabled, L2EngineP2PEnabled,
SkipSanityCheck,
} }
// Flags contains the list of configuration options available to the binary. // Flags contains the list of configuration options available to the binary.
......
...@@ -749,7 +749,7 @@ func (eq *EngineQueue) resetBuildingState() { ...@@ -749,7 +749,7 @@ func (eq *EngineQueue) resetBuildingState() {
// Reset walks the L2 chain backwards until it finds an L2 block whose L1 origin is canonical. // Reset walks the L2 chain backwards until it finds an L2 block whose L1 origin is canonical.
// The unsafe head is set to the head of the L2 chain, unless the existing safe head is not canonical. // The unsafe head is set to the head of the L2 chain, unless the existing safe head is not canonical.
func (eq *EngineQueue) Reset(ctx context.Context, _ eth.L1BlockRef, _ eth.SystemConfig) error { func (eq *EngineQueue) Reset(ctx context.Context, _ eth.L1BlockRef, _ eth.SystemConfig) error {
result, err := sync.FindL2Heads(ctx, eq.cfg, eq.l1Fetcher, eq.engine, eq.log) result, err := sync.FindL2Heads(ctx, eq.cfg, eq.l1Fetcher, eq.engine, eq.log, eq.syncCfg)
if err != nil { if err != nil {
return NewTemporaryError(fmt.Errorf("failed to find the L2 Heads to start from: %w", err)) return NewTemporaryError(fmt.Errorf("failed to find the L2 Heads to start from: %w", err))
} }
......
...@@ -3,4 +3,6 @@ package sync ...@@ -3,4 +3,6 @@ package sync
type Config struct { type Config struct {
// EngineP2PEnabled is true when the EngineQueue can trigger execution engine P2P sync. // EngineP2PEnabled is true when the EngineQueue can trigger execution engine P2P sync.
EngineP2PEnabled bool `json:"engine_p2p_enabled"` EngineP2PEnabled bool `json:"engine_p2p_enabled"`
// SkipSanityCheck is true when the EngineQueue does not do sanity check on pipeline reset.
SkipSanityCheck bool `json:"skip_sanity_check"`
} }
...@@ -102,7 +102,7 @@ func currentHeads(ctx context.Context, cfg *rollup.Config, l2 L2Chain) (*FindHea ...@@ -102,7 +102,7 @@ func currentHeads(ctx context.Context, cfg *rollup.Config, l2 L2Chain) (*FindHea
// Plausible: meaning that the blockhash of the L2 block's L1 origin // Plausible: meaning that the blockhash of the L2 block's L1 origin
// (as reported in the L1 Attributes deposit within the L2 block) is not canonical at another height in the L1 chain, // (as reported in the L1 Attributes deposit within the L2 block) is not canonical at another height in the L1 chain,
// and the same holds for all its ancestors. // and the same holds for all its ancestors.
func FindL2Heads(ctx context.Context, cfg *rollup.Config, l1 L1Chain, l2 L2Chain, lgr log.Logger) (result *FindHeadsResult, err error) { func FindL2Heads(ctx context.Context, cfg *rollup.Config, l1 L1Chain, l2 L2Chain, lgr log.Logger, syncCfg *Config) (result *FindHeadsResult, err error) {
// Fetch current L2 forkchoice state // Fetch current L2 forkchoice state
result, err = currentHeads(ctx, cfg, l2) result, err = currentHeads(ctx, cfg, l2)
if err != nil { if err != nil {
...@@ -170,18 +170,18 @@ func FindL2Heads(ctx context.Context, cfg *rollup.Config, l1 L1Chain, l2 L2Chain ...@@ -170,18 +170,18 @@ func FindL2Heads(ctx context.Context, cfg *rollup.Config, l1 L1Chain, l2 L2Chain
if (n.Number == result.Finalized.Number) && (n.Hash != result.Finalized.Hash) { if (n.Number == result.Finalized.Number) && (n.Hash != result.Finalized.Hash) {
return nil, fmt.Errorf("%w: finalized %s, got: %s", ReorgFinalizedErr, result.Finalized, n) return nil, fmt.Errorf("%w: finalized %s, got: %s", ReorgFinalizedErr, result.Finalized, n)
} }
// Check we are not reorging L2 incredibly deep
if n.L1Origin.Number+(MaxReorgSeqWindows*cfg.SeqWindowSize) < prevUnsafe.L1Origin.Number {
// If the reorg depth is too large, something is fishy.
// This can legitimately happen if L1 goes down for a while. But in that case,
// restarting the L2 node with a bigger configured MaxReorgDepth is an acceptable
// stopgap solution.
return nil, fmt.Errorf("%w: traversed back to L2 block %s, but too deep compared to previous unsafe block %s", TooDeepReorgErr, n, prevUnsafe)
}
// If we don't have a usable unsafe head, then set it // If we don't have a usable unsafe head, then set it
if result.Unsafe == (eth.L2BlockRef{}) { if result.Unsafe == (eth.L2BlockRef{}) {
result.Unsafe = n result.Unsafe = n
// Check we are not reorging L2 incredibly deep
if n.L1Origin.Number+(MaxReorgSeqWindows*cfg.SeqWindowSize) < prevUnsafe.L1Origin.Number {
// If the reorg depth is too large, something is fishy.
// This can legitimately happen if L1 goes down for a while. But in that case,
// restarting the L2 node with a bigger configured MaxReorgDepth is an acceptable
// stopgap solution.
return nil, fmt.Errorf("%w: traversed back to L2 block %s, but too deep compared to previous unsafe block %s", TooDeepReorgErr, n, prevUnsafe)
}
} }
if ahead { if ahead {
...@@ -212,6 +212,11 @@ func FindL2Heads(ctx context.Context, cfg *rollup.Config, l1 L1Chain, l2 L2Chain ...@@ -212,6 +212,11 @@ func FindL2Heads(ctx context.Context, cfg *rollup.Config, l1 L1Chain, l2 L2Chain
return result, nil return result, nil
} }
if syncCfg.SkipSanityCheck && highestL2WithCanonicalL1Origin.Hash == n.Hash {
lgr.Info("Found highest L2 block with canonical L1 origin. Skip further sanity check and jump to the safe head")
n = result.Safe
continue
}
// Pull L2 parent for next iteration // Pull L2 parent for next iteration
parent, err := l2.L2BlockRefByHash(ctx, n.ParentHash) parent, err := l2.L2BlockRefByHash(ctx, n.ParentHash)
if err != nil { if err != nil {
......
...@@ -76,7 +76,7 @@ func (c *syncStartTestCase) Run(t *testing.T) { ...@@ -76,7 +76,7 @@ func (c *syncStartTestCase) Run(t *testing.T) {
} }
lgr := log.New() lgr := log.New()
lgr.SetHandler(log.DiscardHandler()) lgr.SetHandler(log.DiscardHandler())
result, err := FindL2Heads(context.Background(), cfg, chain, chain, lgr) result, err := FindL2Heads(context.Background(), cfg, chain, chain, lgr, &Config{})
if c.ExpectedErr != nil { if c.ExpectedErr != nil {
require.ErrorIs(t, err, c.ExpectedErr, "expected error") require.ErrorIs(t, err, c.ExpectedErr, "expected error")
return return
...@@ -286,6 +286,37 @@ func TestFindSyncStart(t *testing.T) { ...@@ -286,6 +286,37 @@ func TestFindSyncStart(t *testing.T) {
SafeL2Head: 'D', SafeL2Head: 'D',
ExpectedErr: WrongChainErr, ExpectedErr: WrongChainErr,
}, },
{
// FindL2Heads() keeps walking back to safe head after finding canonical unsafe head
// TooDeepReorgErr must not be raised
Name: "long traverse to safe head",
GenesisL1Num: 0,
L1: "abcdefgh",
L2: "ABCDEFGH",
NewL1: "abcdefgx",
PreFinalizedL2: 'B',
PreSafeL2: 'B',
GenesisL1: 'a',
GenesisL2: 'A',
UnsafeL2Head: 'G',
SeqWindowSize: 1,
SafeL2Head: 'B',
ExpectedErr: nil,
},
{
// L2 reorg is too deep
Name: "reorg too deep",
GenesisL1Num: 0,
L1: "abcdefgh",
L2: "ABCDEFGH",
NewL1: "abijklmn",
PreFinalizedL2: 'B',
PreSafeL2: 'B',
GenesisL1: 'a',
GenesisL2: 'A',
SeqWindowSize: 1,
ExpectedErr: TooDeepReorgErr,
},
} }
for _, testCase := range testCases { for _, testCase := range testCases {
......
...@@ -216,5 +216,6 @@ func NewSnapshotLogger(ctx *cli.Context) (log.Logger, error) { ...@@ -216,5 +216,6 @@ func NewSnapshotLogger(ctx *cli.Context) (log.Logger, error) {
func NewSyncConfig(ctx *cli.Context) *sync.Config { func NewSyncConfig(ctx *cli.Context) *sync.Config {
return &sync.Config{ return &sync.Config{
EngineP2PEnabled: ctx.Bool(flags.L2EngineP2PEnabled.Name), EngineP2PEnabled: ctx.Bool(flags.L2EngineP2PEnabled.Name),
SkipSanityCheck: ctx.Bool(flags.SkipSanityCheck.Name),
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment