Skip to content

feat(seidb): locate EVM digest mismatches during migration - #4156

Open
blindchaser wants to merge 1 commit into
mainfrom
yiren/seidb-digest-json-mode
Open

feat(seidb): locate EVM digest mismatches during migration#4156
blindchaser wants to merge 1 commit into
mainfrom
yiren/seidb-digest-json-mode

Conversation

@blindchaser

@blindchaser blindchaser commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add JSON output for both digest and inspect reports, with progress and warnings on stderr
  • let inspect mode read the mid-migration composite EVM view and semantic memiavl replay, so operators can shard mismatched EVM state during a FlatKV drain
  • keep main-only EVM balance handling intact; the v6.7 backport can resolve that release-branch difference

Tests

  • GOPROXY=direct GOTOOLCHAIN=auto go test ./sei-db/tools/cmd/seidb/operations -count=1
  • GOPROXY=direct GOTOOLCHAIN=auto make fmtcheck

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-11T21:08:57.933610Z 318201e PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

blindchaser added a commit that referenced this pull request Sep 11, 2026
Co-authored-by: Cursor <cursoragent@cursor.com>
@cursor

cursor Bot commented Sep 11, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Mostly CI and release automation, but new coordinated and offline upgrade workflows exercise real release→main upgrades and cross-branch state, and the documented Go 1.27.1 release carries state-sync snapshot and consensus decode behavior changes elsewhere on the branch.

Overview
This PR is mostly CI, release, and toolchain plumbing rather than the seidb evm-logical-digest work described in the PR blurb (that capability appears only as an Unreleased CHANGELOG entry here, with no matching tool code in the diff).

Toolchain and lint: Workflows and Docker move to Go 1.27.1, setup-go@v7, and golangci-lint v2.13.2 (Makefile/golangci.yml aligned; goconst dropped). UCI AI assist/review pins v0.0.21, and AI review can run on seidroid[bot] backports.

Upgrade testing: Adds offline-upgrade-test.sh and release-upgrade-test.sh, Makefile targets (upgrade-test, upgrade-test-offline, upgrade-test-cross-version, upgrade-test-vet, new-upgrade-test), upgradetest/AGENTS.md, and workflows offline-upgrade-test.yml / release-upgrade-test.yml. go-test.yml shards race tests across four jobs, runs make upgrade-test + upgrade-test-vet, and keeps a single Race Detection gate job.

CI reliability: ci-free-disk.sh skips slow reclaim when root already has enough free space (tunable via CI_FREE_DISK_MIN_GIB); go-lint setup uses 60 GiB. New ci-drop-third-party-apt-sources.sh avoids broken Chrome/Microsoft apt metadata on Ubuntu runners.

Integration / EVM CI: integration-test-matrix.json is reorganized (EVM suites bundled per documented packing policy). integration-test.yml adds sparse checkout (skips darwin wasmvm blobs), prepare-rpcnode in parallel, row time budgets for scripts, parallel image pulls, and npm caches. New execution-specs.yml runs Ethereum execution-spec suites on main with a shared devnet image pipeline.

Release builds: GoReleaser and cross-arch-build.yml build linux/amd64 and arm64 static seid (build-static.sh takes an arch), verify goreleaser hooks, and add a native ARM64 static CI job with boot smoke.

Misc: admin config characterization drops golden-key fuzz rows in favor of absent-section + address fuzz tests; .gitignore bench paths and /autobahn-e2e updated; AGENTS.md documents upgrade-test workflows and Go 1.27.1.

Reviewed by Cursor Bugbot for commit b46b058. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown

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

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedSep 14, 2026, 3:59 AM

@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 67.66917% with 43 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.49%. Comparing base (ac460ac) to head (04e0422).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...b/tools/cmd/seidb/operations/evm_logical_digest.go 67.66% 43 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4156      +/-   ##
==========================================
- Coverage   66.60%   65.49%   -1.12%     
==========================================
  Files        2196     2079     -117     
  Lines      169188   157439   -11749     
==========================================
- Hits       112692   103107    -9585     
+ Misses      56355    54191    -2164     
  Partials      141      141              
Flag Coverage Δ
sei-chain-pr 35.56% <67.66%> (?)
sei-db 74.50% <ø> (ø)
sei-db-state-db ?

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

Files with missing lines Coverage Δ
...b/tools/cmd/seidb/operations/evm_logical_digest.go 35.56% <67.66%> (+9.42%) ⬆️

... and 117 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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 318201ee1e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// it from the counts.
MarkerAdjustments []string `json:"marker_adjustments"`

ZeroCensus evmZeroCensus `json:"zero_census"`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Distinguish unavailable zero censuses from real zero counts

When --memiavl-normalization=translator is selected, runMemiavlTranslatorDigest sends raw leaves only through ImportTranslator, so none of the new census hooks run; composite mode likewise records some raw zero values but skips the account-derived census by passing nil at line 700. Nevertheless every report serializes zero_census here, making a translator scan containing zero rows indistinguishable from a genuinely clean scan and leaving composite fields internally incomplete. Collect the census at a common raw-leaf scan choke point or explicitly encode it as unavailable for unsupported modes.

AGENTS.md reference: AGENTS.md:L115-L119

Useful? React with 👍 / 👎.

Comment on lines +190 to +194
// digestSink is where this command's output goes. prose takes the narration —
// the start banner, scan progress, and the human-readable report. jsonReport is
// nil in the default text mode and, in JSON mode, takes the encoded report while
// prose moves to stderr, so stdout carries only the object.
//

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Rewrite the sink godoc as a what-only description

This new godoc spends multiple paragraphs explaining the output-routing mechanism and the rationale for retaining narration rather than concisely documenting what digestSink represents. Rewrite it as a one- or two-sentence description and keep any load-bearing rationale inline at the relevant routing decision, as required by the repository's godoc convention.

AGENTS.md reference: AGENTS.md:L79-L90

Useful? React with 👍 / 👎.

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Well-scoped forensic-tooling change: --json renders from the same report() value as the prose (pinned by a test), the narration redirect is clean, and the isZeroAccount!isLiveAccount() && hasZeroCodeHash() split is genuinely behaviour-preserving including the nil case, so no digest value moves. The one substantive issue is the new zero_census field, which is populated only partially in composite mode and not at all in the flatkv/translator backends while still being emitted as measured-looking zeros.

Findings: 0 blocking | 4 non-blocking | 2 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • [suggestion] zero_census is emitted unconditionally in JSON but is only ever populated by consumeSemanticMemiavlLeaf/finalizeSemanticAccounts. The flatkv backend (digestFlatKV) and memiavl translator normalization (runMemiavlTranslatorDigest) route every row through d.consume, so those runs report an all-zero census that a caller cannot distinguish from "measured, found none" — the prose sidesteps this by suppressing the block on the zero value, but the object does not. Consider omitempty on a pointer census, or an explicit zero_census_collected/census: null for backends that do not measure it.
  • [suggestion] Several of the new godocs carry rationale rather than description, which AGENTS.md ("Godoc": explain WHAT not WHY; multi-paragraph godocs are rare) asks to move to an inline comment at the line that needs it. sayf's comment is entirely justification for dropping write errors, sink's is entirely justification for being a package variable, and digestSink/enterJSONMode each add a second rationale paragraph. The content is worth keeping — it just belongs inline next to _, _ = fmt.Fprintf(...) and the sink.prose = os.Stderr assignment.
  • 2 suggestion(s)/nit(s) flagged inline on specific lines.

finalizeSemanticAccounts(accounts, d.addLogical)
d.print(ctx)
return nil
finalizeSemanticAccounts(accounts, d.addLogical, nil)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[suggestion] Composite mode ends up with a half-measured census. consumeCompositeMemiavl calls d.consumeSemanticMemiavlLeaf (line 793), which passes &d.census and so accumulates ZeroCodeHashRows, EmptyCodeValues and ZeroStorageSlots over the unmigrated memiavl tail — but finalization here passes nil, so ZeroAccounts and LiveAccountsWith/WithoutCodeHashRow stay 0 no matter what the data holds.

The result is a report whose fields disagree about what was counted: "zero_census":{"zero_accounts":0,...,"zero_codehash_rows":N} with N>0 in JSON, and a prose block that prints (because the struct is non-zero) showing zero_accounts=0 beside a non-zero zero_codehash_rows. A scheduled composite-mode caller — the one this PR is built for, since composite is also what carries the migration boundary in normalization — reads a silent zero for something never measured.

Passing &d.census here is not the fix on its own: composite accounts are merged from FlatKV too via mergeCompositeFlatKVAccount, which never sets codeHashRow, so every FlatKV-sourced account would land in LiveAccountsWithoutCodeHashRow. Either complete the accounting on both sources, or make the omission explicit so the account-level fields are not reported as measured zeros (see the omitempty/census: null note in the review).

cmd.SilenceUsage, cmd.SilenceErrors = true, true

err := cmd.Execute()
require.ErrorContains(t, err, "--inspect-bucket")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[suggestion] This assertion can pass for the wrong reason: the same branch returns --inspect-bucket does not support --memiavl-open-mode=%q yet (line 589), which also contains --inspect-bucket. It only exercises the --json refusal today because --memiavl-open-mode happens to default to memiavlOpenModeSnapshot; change that default and the test stays green while the refusal it names goes untested. Asserting on --json instead pins the intended error.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2cdbfc5. Configure here.

Comment thread sei-db/tools/cmd/seidb/operations/evm_logical_digest.go Outdated
@blindchaser

Copy link
Copy Markdown
Contributor Author

Thanks — three reviewers independently found the composite census bug, and it reproduced exactly as described. Pushed 6e591a1 and 04e0422.

The composite census (codex P2, seidroid, Bugbot medium)

Confirmed all three legs: consumeCompositeMemiavl reached the row counters through &d.census, finalization passed nil, and the text guard != (evmZeroCensus{}) then let the block print with zero_accounts=0 beside real row counts. seidroid's point that &d.census is not the fix on its own is also right — mergeCompositeFlatKVAccount never sets codeHashRow, so every FlatKV-sourced account would have landed in LiveAccountsWithoutCodeHashRow.

Rather than patch the two call sites, the census now lives in one field both levels read (census *evmZeroCensus), nil on the paths that take none. The two call sites pass the same field, so a half-measured census is unreachable instead of merely fixed here. Composite takes none, and the reason is recorded at the declaration. TestCensusIsAllOrNothingAcrossBothCounterLevels pins it.

zero_census as a measured zero

Same change: the JSON field is now *evmZeroCensus with omitempty, and the prose guard is != nil. An absent object and an absent prose block now mean the same thing, where before FlatKV and translator mode emitted six zeros in JSON and nothing in text — the drift this PR claims to prevent, so thank you for catching it on the PR that makes the claim. TestCensusFreeDigestReadsAsUnmeasuredInBothForms pins it.

The digestSink godoc (codex P1)

Agreed, and rewritten from scratch per the convention rather than patched. The reason the narration is redirected rather than silenced moved to enterJSONMode.

TestJSONWithInspectBucketIsRefused (seidroid)

Correct, and a good catch: the assertion would have stayed green off the --memiavl-open-mode refusal, which also names --inspect-bucket. Now asserts on --json.

One I did not take: refusing --json when SEI_LOG_OUTPUT points at stdout

Warning on stderr instead. A healthy run does not need the variable — raising seilog to error level already clears every line such a run emits, measured as stdout being exactly the object either way — so refusing would convert an unset variable into a failed read at the moment someone is reading digests because something is wrong, including the runbook's own invocations. The warning names the condition and says that a report which fails to parse is this rather than a digest defect. Two tests pin it, one of them that the warning itself stays off stdout, since emitting it onto the report would be the fault it reports.

make fmtcheck clean; 54 tests pass in the package with no skips.

@blindchaser

Copy link
Copy Markdown
Contributor Author

Verified the zero_census fix on a live mainnet-sized store rather than only in tests.

Pod eng-yiren/pacific1-flatkv-shadow-v67-migrating-0, context harbor, seidb built from 04e04227b as CGO_ENABLED=0 GOOS=linux GOARCH=amd64. Composite backend, --memiavl-open-mode snapshot, --height 0, over 707,473,660 logical EVM keys. Both readings exit 0, about 13 minutes each.

case exit stdout lines stdout bytes zero_census
before the fix, 2026-09-01 0 1 1184 present, six zeros
after the fix 0 1 1065 absent

grep -c zero_census over the captured stdout returns 0, and the prose run at the same settings carries no Zero-value memiavl census line. So the two forms now agree on what "not measured" looks like, which is the drift the shared report() value exists to prevent. The object is 119 bytes smaller.

The new SEI_LOG_OUTPUT warning landed as the first line of stderr while stdout stayed exactly one JSON object on one line — the property that warning protects.

Two honest limits on this run, both recorded in the evidence note:

Text and JSON were not shown here to carry the same numbers. Both used --height 0, which resolves to the current version, and the node committed 1,686 versions during the 13.5 minutes between the two starts, so the readings are of different states. Their structure and non-digest fields do agree — same backend, mode, db_dir, normalization, the same single marker_adjustments entry, no census on either side. The numeric equality is pinned deterministically by TestDigestJSONReportCarriesTheSameNumbersAsTheProse, which renders both forms from one report(), so it cannot drift.

The partial-census case this PR also closes is unreachable on this node. A composite digest scans memIAVL leaves only while boundary.Status() != MigrationComplete, and this node is past H_done, so the row counters are never raised and the half-measured census cannot appear. Covering that needs a node with a drain in flight.

@blindchaser
blindchaser force-pushed the yiren/seidb-digest-json-mode branch from 04e0422 to ca02c28 Compare September 14, 2026 03:32
@blindchaser blindchaser changed the title feat(seidb): machine-readable digest output and a zero-value census feat(seidb): locate EVM digest mismatches during migration Sep 14, 2026
@blindchaser
blindchaser changed the base branch from main to release/v6.7 September 14, 2026 03:32
Co-authored-by: Cursor <cursoragent@cursor.com>
@blindchaser
blindchaser force-pushed the yiren/seidb-digest-json-mode branch from ca02c28 to b46b058 Compare September 14, 2026 03:57
@blindchaser blindchaser added the backport release/v6.7 Backport to release v6.7 label Sep 14, 2026
@blindchaser
blindchaser changed the base branch from release/v6.7 to main September 14, 2026 03:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant