Skip to content
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
27 changes: 22 additions & 5 deletions node/derivation/derivation.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,17 +483,26 @@ func (d *Derivation) derivationBlock(ctx context.Context) {
if err := d.verifyBatchRoots(batchInfo, lastHeader); err != nil {
// stateException only when the verifier produced a real mismatch
// verdict (root or withdrawal root). Transient failures (e.g.
// MessageRoot RPC error) just log and retry next poll.
// MessageRoot RPC error) must not be reported as divergence.
if errors.Is(err, ErrBatchVerifyDivergence) {
d.metrics.SetBatchStatus(stateException)
}
d.logger.Error("batch roots verification failed", "batchIndex", batchInfo.batchIndex, "error", err)
return
if enforceBatchRootVerification(d.verifyMode) {
d.logger.Error("batch roots verification failed", "batchIndex", batchInfo.batchIndex, "error", err)
return
}
// Tendermint's logger exposes debug/info/error but no warning
// method, so retain warning semantics as a structured info event.
d.logger.Info("batch roots verification warning; continuing in non-validator mode",
"severity", "warning", "batchIndex", batchInfo.batchIndex, "error", err)
} else {
d.metrics.SetBatchStatus(stateNormal)
}
d.metrics.SetBatchStatus(stateNormal)
d.metrics.SetL1SyncHeight(lg.BlockNumber)

// SPEC-005 section 4.7.3: a verified batch (layer1 or local verify) advances safe.
// SPEC-005 section 4.7.3: a content-verified batch advances safe.
// Regular nodes treat root verification as an observational check;
// layer1 validator nodes enforce it above.
d.tagAdvancer.advanceSafe(d.ctx, batchInfo.batchIndex, lastHeader)
}

Expand All @@ -510,6 +519,14 @@ func (d *Derivation) derivationBlock(ctx context.Context) {
d.logger.Info("write latest derivation l1 height success", "l1BlockNumber", end)
}

// enforceBatchRootVerification reports whether a root verification failure must
// stop derivation. Layer1 mode is the validator deployment mode; local mode is
// used by regular nodes, which still verify roots for observability but must not
// lose sync when historical state is unavailable.
func enforceBatchRootVerification(verifyMode string) bool {
return verifyMode == VerifyModeLayer1
}

func (d *Derivation) fetchRollupLog(ctx context.Context, from, to uint64) ([]eth.Log, error) {
query := ethereum.FilterQuery{
FromBlock: big.NewInt(0).SetUint64(from),
Expand Down
30 changes: 30 additions & 0 deletions node/derivation/verify_policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package derivation

import "testing"

func TestEnforceBatchRootVerification(t *testing.T) {
tests := []struct {
name string
verifyMode string
want bool
}{
{
name: "layer1 validator enforces",
verifyMode: VerifyModeLayer1,
want: true,
},
{
name: "local node observes",
verifyMode: VerifyModeLocal,
want: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := enforceBatchRootVerification(tt.verifyMode); got != tt.want {
t.Fatalf("enforceBatchRootVerification(%q) = %t, want %t", tt.verifyMode, got, tt.want)
}
})
}
}
Loading