Skip to content

feat: gate block-validation bypasses behind //go:build mock_block_validation#3401

Merged
bdchatham merged 1 commit into
mainfrom
feat/skip-apphash-shadow-buildtag
May 7, 2026
Merged

feat: gate block-validation bypasses behind //go:build mock_block_validation#3401
bdchatham merged 1 commit into
mainfrom
feat/skip-apphash-shadow-buildtag

Conversation

@bdchatham

@bdchatham bdchatham commented May 6, 2026

Copy link
Copy Markdown
Contributor

Why

A node running an alternate execution engine (e.g., Giga V2) needs to bypass certain header-hash checks to validate against canonical-chain blocks. The previous shape exposed --skip-app-hash-validation as a runtime CLI flag on every seid binary, which made the production validator one config edit away from accepting bad blocks.

Approach

The build tag mock_block_validation is the toggle — there is no runtime knob.

// types/consensus_policy_default.go (//go:build !mock_block_validation)
type ConsensusPolicy struct{}
func (ConsensusPolicy) SkipAppHashValidation() bool  { return false }
func (ConsensusPolicy) SkipDataHashValidation() bool { return false }

// types/consensus_policy_mock_block_validation.go (//go:build mock_block_validation)
type ConsensusPolicy struct{}
func (ConsensusPolicy) SkipAppHashValidation() bool  { return true }
func (ConsensusPolicy) SkipDataHashValidation() bool { return true }

ConsensusPolicy is an empty struct in both builds; methods return different constants per build and the compiler folds them. Bypass branches DCE in production. There is no Go API in either build to construct a policy with the opposite behavior.

Block.ValidateBasic(policy) wraps the gated hash checks in if !policy.Skip*() { ... } in-place — no structural changes to the function body. validateBlock(state, block, policy) carries the policy from BlockExecutor down. Production start.go passes DefaultConsensusPolicy() unconditionally — no cobra, no AppOptions, no viper.

Naming

mock_block_validation matches the repo's existing mock_balances precedent — both are mock build tags for engineering / debug binaries. Underscore form is required for valid Go build tags. Image tag uses hyphen for readability: sei-chain:mock-block-validation-<sha>.

Per-check flags, narrow scope

The original ftr-skip-apphash branch gated 6 hash checks under one umbrella flag; this PR ships 2:

Hash check Gated Reason
AppHash Yes State root from execution — guaranteed to diverge for an alternate execution engine.
DataHash Yes Defensive against tx-encoding format skew across versions.
LastCommitHash No Validator-authenticity territory (merkle root of validator signatures). Should match against a same-version canonical chain.
EvidenceHash No Validator-authenticity territory (merkle root of misbehavior evidence). Same reasoning.
ValidatorsHash, NextValidatorsHash No Validator-set updates flow through cosmos-sdk x/staking, not through the EVM execution engine. Should match.
ConsensusHash, LastBlockID, VerifyCommit, ProposerAddress No (unconditional) Network-config or canonical-chain-derived; identical regardless of local execution.
LastResultsHash (separate) Gated by the existing SkipLastResultsHashValidation global, set from gigaExecutorConfig.Enabled — a first-class production runtime feature with intentional gas-accounting divergence, different security model.

If a non-gated check fails on a candidate node, that's actionable signal — either a real bug in the setup or a wire-format incompatibility worth investigating, not silently bypassed.

CI

.github/workflows/ecr.yml adds a single new build step alongside the existing mock_balances and production builds, mirroring the same pattern. Every push produces:

  • sei-chain:<sha> — production
  • sei-chain:mock-<sha> — mock_balances (unchanged)
  • sei-chain:mock-block-validation-<sha> — new

Verification

  • strings seid-default | grep skip-app-hash → 0 hits (no flag, no string in either binary)
  • go build ./... and go build -tags mock_block_validation ./... both clean

Out of scope

  • The two Dockerfile commits from ftr-skip-apphash (libevmone runtime + ubuntu 24.04 base for GLIBCXX) are giga-runtime support and ship as a separate PR.
  • A BlockValidator interface that hides per-step validation behind a strategy-pattern abstraction was discussed and deferred — defensible refactor when there's a second consumer.

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown

The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedMay 7, 2026, 9:11 PM

@codecov

codecov Bot commented May 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.11%. Comparing base (eb17459) to head (7182fd9).

Files with missing lines Patch % Lines
sei-tendermint/light/rpc/client.go 0.00% 2 Missing ⚠️
sei-cosmos/server/start.go 0.00% 1 Missing ⚠️
sei-tendermint/test/e2e/node/main.go 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3401      +/-   ##
==========================================
+ Coverage   59.10%   59.11%   +0.01%     
==========================================
  Files        2106     2108       +2     
  Lines      173525   173535      +10     
==========================================
+ Hits       102558   102589      +31     
+ Misses      62093    62073      -20     
+ Partials     8874     8873       -1     
Flag Coverage Δ
sei-chain-pr 67.89% <90.00%> (?)
sei-db 70.41% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
sei-tendermint/internal/consensus/replay.go 69.26% <100.00%> (+0.13%) ⬆️
sei-tendermint/internal/state/execution.go 80.14% <100.00%> (+0.04%) ⬆️
sei-tendermint/internal/state/validation.go 100.00% <100.00%> (ø)
sei-tendermint/node/node.go 65.19% <100.00%> (+0.18%) ⬆️
sei-tendermint/node/public.go 64.10% <100.00%> (+0.94%) ⬆️
sei-tendermint/rpc/test/helpers.go 75.40% <ø> (ø)
sei-tendermint/types/block.go 86.25% <100.00%> (+0.02%) ⬆️
sei-tendermint/types/consensus_policy.go 100.00% <100.00%> (ø)
sei-tendermint/types/consensus_policy_default.go 100.00% <100.00%> (ø)
sei-cosmos/server/start.go 23.39% <0.00%> (-0.09%) ⬇️
... and 2 more

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bdchatham bdchatham force-pushed the feat/skip-apphash-shadow-buildtag branch from b20f4d8 to 5908b0a Compare May 6, 2026 22:58
@bdchatham bdchatham changed the title feat: gate skip-app-hash-validation behind //go:build shadow feat: gate skip-app-hash-validation behind //go:build shadow via ConsensusPolicy May 6, 2026
@bdchatham bdchatham force-pushed the feat/skip-apphash-shadow-buildtag branch 4 times, most recently from ded86df to 955ee32 Compare May 7, 2026 00:18
@bdchatham bdchatham changed the title feat: gate skip-app-hash-validation behind //go:build shadow via ConsensusPolicy feat: gate execution-shadow validation bypasses behind //go:build execution_shadow May 7, 2026
Comment thread .github/workflows/ecr-execution-shadow.yml Outdated
Comment thread sei-tendermint/types/consensus_policy_execution_shadow.go Outdated
@bdchatham bdchatham force-pushed the feat/skip-apphash-shadow-buildtag branch 2 times, most recently from b1436be to b7400cc Compare May 7, 2026 14:40
@bdchatham bdchatham changed the title feat: gate execution-shadow validation bypasses behind //go:build execution_shadow feat: gate block-validation bypasses behind //go:build mock_block_validation May 7, 2026
@bdchatham bdchatham force-pushed the feat/skip-apphash-shadow-buildtag branch 4 times, most recently from 04980b1 to 69fd340 Compare May 7, 2026 18:13
@bdchatham bdchatham requested a review from cody-littley May 7, 2026 18:15

@masih masih left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙌

Comment thread .github/workflows/ecr.yml Outdated
@bdchatham bdchatham force-pushed the feat/skip-apphash-shadow-buildtag branch from 69fd340 to 3a97819 Compare May 7, 2026 19:55
Comment thread sei-tendermint/types/consensus_policy.go Outdated
Comment thread sei-tendermint/types/consensus_policy_default.go
Comment thread sei-tendermint/internal/state/execution.go
…ensusPolicy

Replaces the runtime --skip-app-hash-validation CLI flag with a
build-tag-gated ConsensusPolicy value owned by BlockExecutor. The
seid validator binary built without -tags shadow cannot bypass
AppHash, LastCommitHash, DataHash, EvidenceHash, ValidatorsHash, or
NextValidatorsHash checks — the flag, the bypass field, and the
bypass branches do not exist in the production binary. The shadow
binary always runs in shadow mode; there is no runtime knob.

Why no runtime config

Earlier drafts threaded a CLI flag and AppOptions value into a
ConsensusPolicy{skipAppHashValidation bool} struct. That worked but
left an operator footgun: a shadow binary would silently behave like
production unless the operator remembered to pass the flag, then fail
on the first AppHash mismatch and surprise the operator.

The build tag IS the toggle. The shadow binary's purpose is to
shadow-sync, period. ConsensusPolicy carries no fields; the method
SkipAppHashValidation() returns a different constant in each build
and the compiler folds it.

Refactor

- types.ConsensusPolicy is an empty struct in both builds. Method
  set differs: default returns false, shadow returns true.
  DefaultConsensusPolicy() is the only constructor.
- Block.ValidateBasic now does shape-only validation. The 3 hash
  checks (LastCommitHash / DataHash / EvidenceHash) move into a new
  Block.ValidateHashes(policy) gated by SkipAppHashValidation.
  Deserialization paths (BlockFromProto, light-client, store load)
  call ValidateBasic and pick up the shape-only contract.
- validateBlock(state, block, policy) plumbs the policy to the
  AppHash / ValidatorsHash / NextValidatorsHash gates.
- BlockExecutor carries policy as a struct field; NewBlockExecutor
  takes it as a constructor arg.
- node.New / makeNode / Handshaker thread the policy through.
- sei-cosmos/server/start.go passes tmtypes.DefaultConsensusPolicy()
  unconditionally. No CLI flag, no viper, no AppOptions.

What stays unchanged

tmtypes.SkipLastResultsHashValidation remains a global atomic.Bool
flipped from gigaExecutorConfig.Enabled. Giga is a first-class
runtime-togglable production feature with intentional gas-accounting
divergence; that bypass is not in scope.

Empirical proof
- strings seid-default | grep skip-app-hash → 0 hits
- strings seid-shadow  | grep skip-app-hash → 0 hits
  (the flag literal never existed since we never registered it)
- Both builds clean: go build ./... and go build -tags shadow ./...
- Original 6 hash gates (AppHash, LastCommitHash, DataHash,
  EvidenceHash, ValidatorsHash, NextValidatorsHash from commits
  9fa70d2, 4ce5ed3, d9b7f9283) preserved.

Workflow

.github/workflows/ecr.yml adds sei-chain:shadow-<sha> mirroring the
existing mock_balances build-tag pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bdchatham bdchatham force-pushed the feat/skip-apphash-shadow-buildtag branch from 3a97819 to 7182fd9 Compare May 7, 2026 21:10
@bdchatham bdchatham enabled auto-merge May 7, 2026 21:18
@bdchatham bdchatham added this pull request to the merge queue May 7, 2026
Merged via the queue into main with commit 817ecf0 May 7, 2026
38 checks passed
@bdchatham bdchatham deleted the feat/skip-apphash-shadow-buildtag branch May 7, 2026 21:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants