Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
962c07e
docs: add PBin binary commitment engine implementation plan
awskii Jul 29, 2026
db9c47b
execution/commitment: add PBin bit-path type and DB key codec
awskii Jul 29, 2026
d6b2de6
feat: EIP-8297 tree key derivation and zone routing
awskii Jul 29, 2026
e5b5e7b
feat: EIP-8297 leaf value encoding
awskii Jul 29, 2026
2482bff
feat: EIP-8297 reference oracle for differential testing
awskii Jul 29, 2026
81b0889
feat: EIP-8297 pbinCell, grid and branch record codec
awskii Jul 29, 2026
88ea192
feat: EIP-8297 node merkelization
awskii Jul 29, 2026
167591c
feat: EIP-8297 unfold and needUnfolding
awskii Jul 29, 2026
bcf64f0
feat: EIP-8297 fold primitives
awskii Jul 29, 2026
1151d53
feat: EIP-8297 drive loop, Process and RootHash
awskii Jul 29, 2026
3419150
feat: EIP-8297 variant registration and Trie interface methods
awskii Jul 29, 2026
b2c4062
feat: EIP-8297 hazard guards and differential fuzzing
awskii Jul 29, 2026
4103ecb
feat: verify EIP-8297 acceptance criteria
awskii Jul 29, 2026
58e39f5
feat: EIP-8297 engine doc header, plan completed
awskii Jul 29, 2026
88f4c84
fix: address code review findings
awskii Jul 29, 2026
e8e19e7
fix: address code review findings
awskii Jul 29, 2026
60b621b
fix: address codex review findings
awskii Jul 29, 2026
619065c
execution/commitment: fold the storage tree-index shift into groupDigest
awskii Jul 29, 2026
e9f93ba
execution/commitment: compare binary and hex engine shape over one co…
awskii Jul 29, 2026
bb03aab
execution/commitment: check PBin against EIP-8297 reference vectors
awskii Jul 29, 2026
61759ca
execution/commitment: replay EIP-8297 reference roots against the PBi…
awskii Jul 29, 2026
7a5058f
execution/commitment: drive the PBin engine over reference root vectors
awskii Jul 30, 2026
7b43156
docs: add PBin M1 plan — binary trie as a local EL state trie
awskii Jul 30, 2026
1b352e1
execution/commitment: replay pbin key derivation under BLAKE3 through…
awskii Jul 30, 2026
92e219d
execution/commitment: move the pbin root record off the empty key
awskii Jul 30, 2026
b279f40
execution/commitment: write pbin branch deletions as zero-length valu…
awskii Jul 30, 2026
9a44be4
execution/commitment, db/state: refuse a shared branch cache under th…
awskii Jul 30, 2026
a8c8dff
execution/commitment: pbin state save/restore behind a StatefulTrie seam
awskii Jul 30, 2026
e39c5d5
execution/stagedsync, db/state, cmd, node: --experimental.bin-commitm…
awskii Jul 30, 2026
57f4ba5
db/state/execctx, execution/state/genesiswrite, rpc, db/integrity: ke…
awskii Jul 30, 2026
7d57dec
feat: refuse the hex-only commitment paths under the bin variant
awskii Jul 30, 2026
065d8b0
feat: zero-vs-absent in the pbin engine — a zeroed storage slot keeps…
awskii Jul 30, 2026
e2f978f
feat: M1a gate — pbin over a real MDBX datadir, forward run vs rebuild
awskii Jul 30, 2026
23dbb63
feat: code_size on Update, read from the CodeDomain for the bin trie
awskii Jul 30, 2026
7c4917a
feat: header code chunks in the pbin tree
awskii Jul 30, 2026
43eed08
feat: CODE_ZONE overflow chunks in the pbin tree
awskii Jul 30, 2026
a624306
feat: M1b gate — pbin dev chain from genesis
awskii Jul 30, 2026
77dc829
feat: M1 acceptance audit — hazards, API breaks, pbin naming rule
awskii Jul 30, 2026
e0507af
feat: M1 documentation — pbin engine doc, variant comment, pbin namin…
awskii Jul 30, 2026
058483d
fix: pbin review findings — stale run state, shadow cross-check
awskii Jul 31, 2026
26cc0a8
feat: select the pbin trie hash at runtime
awskii Jul 31, 2026
e835693
execution/commitment: isolate pbin update stream
awskii Jul 31, 2026
ddfb553
execution/commitment: consolidate pbin spec vector tests
awskii Jul 31, 2026
bd98ebb
execution/commitment: drop redundant pbin verifier corpus
awskii Jul 31, 2026
000c857
Merge remote-tracking branch 'origin/main' into awskii/pbin-patricia
awskii Aug 1, 2026
8ad7227
docs: drop the pbin implementation plans
awskii Aug 1, 2026
858d8bc
execution/commitment: shrink pbin comments to the repo policy
awskii Aug 1, 2026
7b7ef58
execution/tests: run the EIP-8297 fixtures against the bin trie
awskii Aug 1, 2026
ff808c2
execution/commitment: trim the pbin test surface
awskii Aug 1, 2026
7ecf7dc
Merge origin/main into binary-trie
awskii Aug 10, 2026
0bae758
execution/protocol/params: apply the revised EIP-8038 gas values
awskii Aug 10, 2026
3bd1726
rpc/jsonrpc: debug_executionWitness for the binary trie (#23136)
awskii Aug 10, 2026
40c25bc
execution: scope EIP-8038's revised gas schedule to the config that o…
awskii Aug 10, 2026
231d775
execution/commitment, rpc/jsonrpc: satisfy gocritic sloppyReassign an…
awskii Aug 10, 2026
1573733
tests: pin the bin commitment tests to the sequential trie
awskii Aug 10, 2026
912ecdd
execution/commitment, rpc/jsonrpc: trim narrative comments from the p…
awskii Aug 10, 2026
45b1bfb
execution/protocol/mdgas: charge the revised create access on creatin…
awskii Aug 10, 2026
2876b42
execution/commitment: refuse a leaf whose state read comes back absent
awskii Aug 11, 2026
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
7 changes: 6 additions & 1 deletion .github/workflows/test-fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ jobs:
- { name: patricia, pkg: db/seg/patricia, fn: FuzzPatricia }
- { name: patricia-longest-match, pkg: db/seg/patricia, fn: FuzzLongestMatch }
- { name: abi, pkg: execution/abi, fn: FuzzABI }
- { name: pbin-bitpath-codec, pkg: execution/commitment, fn: FuzzPBinBitPathCodec }
- { name: pbin-process-oracle, pkg: execution/commitment, fn: FuzzPBinProcessMatchesOracle }
- { name: nibbles-hexcompact, pkg: execution/commitment/nibbles, fn: FuzzHexCompactRoundtrip }
- { name: rlp, pkg: execution/types, fn: FuzzRLP }
- { name: precompiles, pkg: execution/vm, fn: FuzzPrecompiledContracts }
Expand Down Expand Up @@ -108,7 +110,10 @@ jobs:
run: |
mkdir -p "$ERIGON_BUILD/fuzz"
echo "::group::go test -fuzz ${{ matrix.fn }} (./${{ matrix.pkg }}, ${FUZZTIME})"
go test "./${{ matrix.pkg }}/" -run '^$' -fuzz "^${{ matrix.fn }}$" -fuzztime "${FUZZTIME}"
# Minimization is capped: Go's 60s default lets an input found late in
# the run keep minimizing past the coordinator's deadline, which
# surfaces as "context deadline exceeded" rather than as the crash.
go test "./${{ matrix.pkg }}/" -run '^$' -fuzz "^${{ matrix.fn }}$" -fuzztime "${FUZZTIME}" -fuzzminimizetime 10s
echo "::endgroup::"

# Tell a genuine crash from an engine timeout. On a crash `go test` writes a
Expand Down
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ Commit messages: prefix with package(s) modified, e.g., `eth, rpc: make trace co

Don't sign commits, pr's, issues, comments.

`package commitment` holds two engines in one namespace. Every package-level identifier belonging to the EIP-8297 binary trie carries a `pbin` prefix (`PBin` for exported ones) — the hex engine already owns the generic names (`cell`, `fold`, `unfold`, `computeCellHash`), so an unprefixed addition is a collision waiting to happen. Test helpers included.

Selecting the binary trie is process-global, not a per-tester option: set `statecfg.ExperimentalBinCommitment` and `statecfg.BinCommitmentHash`, then `commitment.SetPBinHashSuite`. Calling `SetPBinHashSuite` alone is undone by the settings resolver's keccak default. A test that flips these must restore them in `t.Cleanup` and must not call `t.Parallel` — a concurrent hex test reads the same globals.

The EIP-8297 embedding is not versioned on disk. `erigondb.toml` records `trie_variant` and `trie_hash` and guards a change of either, but nothing records which embedding wrote the state — so a change to key derivation or leaf layout silently recomputes different roots over an existing bin datadir. Rebuild bin datadirs from genesis whenever the embedding changes.

Cite by name, never by line number. An EIP reference is `eip:"<section name>"`, not `eip:NNN-NNN`; a reference to erigon source from `docs/` names the identifier and its file, not `file.go:NNN`. Line anchors rot on the next edit in either repo, and a stale one is worse than none — it points a reader at unrelated code with full confidence.

Run `make lint` before every push. The linter is non-deterministic — run it repeatedly until clean.

**Important**: Always run `make lint` after making code changes and before committing. Fix any linter errors before proceeding. PRs must pass `make lint` before being opened or updated.
Expand Down
5 changes: 4 additions & 1 deletion cmd/integration/commands/commitment.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func init() {
// commitment visualize
cmdCommitmentVisualize.Flags().StringVar(&visualizeOutputDir, "output", "", "existing directory to store output HTML. By default, same as commitment files")
cmdCommitmentVisualize.Flags().IntVarP(&visualizeConcurrency, "concurrency", "j", 4, "amount of concurrently processed files")
cmdCommitmentVisualize.Flags().StringVar(&visualizeTrieVariant, "trie", "hex", "commitment trie variant (values are hex and parallel)")
cmdCommitmentVisualize.Flags().StringVar(&visualizeTrieVariant, "trie", "hex", "commitment trie variant (hex or parallel)")
cmdCommitmentVisualize.Flags().StringVar(&visualizeCompression, "compression", "none", "compression type (none, k, v, kv)")
cmdCommitmentVisualize.Flags().BoolVar(&visualizePrintState, "state", false, "print state of file")
cmdCommitmentVisualize.Flags().IntVar(&visualizeDepth, "depth", 0, "depth of the prefixes to analyze")
Expand Down Expand Up @@ -1376,6 +1376,9 @@ func extractKVPairFromCompressed(filename string, keysSink chan commitment.Branc
}
defer dec.Close()
tv := commitment.ParseTrieVariant(visualizeTrieVariant)
if tv == commitment.VariantBinPatriciaTrie {
return fmt.Errorf("commitment visualize decodes hex records only, not %s", tv)
}

fc, err := seg.ParseFileCompression(visualizeCompression)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions cmd/integration/commands/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ func withDataDir(cmd *cobra.Command) {
func withExperimentalCommitment(cmd *cobra.Command) {
cmd.Flags().BoolVar(&statecfg.ExperimentalParallelCommitment, utils.ExperimentalParallelCommitmentFlag.Name, statecfg.ExperimentalParallelCommitment, utils.ExperimentalParallelCommitmentFlag.Usage)
cmd.Flags().BoolVar(&statecfg.ExperimentalStreamingCommitment, utils.ExperimentalStreamingCommitmentFlag.Name, statecfg.ExperimentalStreamingCommitment, utils.ExperimentalStreamingCommitmentFlag.Usage)
cmd.Flags().BoolVar(&statecfg.ExperimentalBinCommitment, utils.ExperimentalBinCommitmentFlag.Name, statecfg.ExperimentalBinCommitment, utils.ExperimentalBinCommitmentFlag.Usage)
cmd.Flags().StringVar(&statecfg.BinCommitmentHash, utils.ExperimentalBinCommitmentHashFlag.Name, statecfg.BinCommitmentHash, utils.ExperimentalBinCommitmentHashFlag.Usage)
}

func withBatchSize(cmd *cobra.Command) {
Expand Down
37 changes: 37 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,13 @@ import (
"github.com/erigontech/erigon/db/datadir"
"github.com/erigontech/erigon/db/downloader/downloadercfg"
"github.com/erigontech/erigon/db/snapcfg"
"github.com/erigontech/erigon/db/state/statecfg"
"github.com/erigontech/erigon/db/version"
"github.com/erigontech/erigon/diagnostics/metrics"
"github.com/erigontech/erigon/execution/builder/buildercfg"
"github.com/erigontech/erigon/execution/chain/networkname"
chainspec "github.com/erigontech/erigon/execution/chain/spec"
"github.com/erigontech/erigon/execution/commitment"
"github.com/erigontech/erigon/execution/protocol/params"
"github.com/erigontech/erigon/execution/protocol/rules/ethash/ethashcfg"
"github.com/erigontech/erigon/execution/state/genesiswrite"
Expand Down Expand Up @@ -1174,6 +1176,23 @@ var (
Usage: "EXPERIMENTAL: enables streaming trie for commitment (StreamingCommitter, overlaps folding with execution). Takes precedence over --experimental.parallel-commitment if set.",
Value: false,
}
// ExperimentalBinCommitmentFlag selects the EIP-8297 binary commitment trie.
// A whole-datadir property: honoured on a fresh datadir, persisted to
// erigondb.toml there, and adopted from it on later starts.
ExperimentalBinCommitmentFlag = cli.BoolFlag{
Name: "experimental.bin-commitment",
Usage: "EXPERIMENTAL: enables the EIP-8297 binary commitment trie. Takes effect on a fresh datadir only and is persisted there.",
Value: false,
}
// ExperimentalBinCommitmentHashFlag picks H for the binary trie. Persisted and
// adopted like the variant itself: roots do not survive a change.
ExperimentalBinCommitmentHashFlag = cli.StringFlag{
Name: "experimental.bin-commitment.hash",
Usage: "EXPERIMENTAL: hash for the EIP-8297 binary commitment trie: \"keccak\" (default) or \"blake3\". blake3 matches the execution-specs reference and the other clients on the binary-trie testnets. Takes effect on a fresh datadir only and is persisted there.",
// Empty, not "keccak": an unset flag must stay distinguishable from an
// explicit one, which a hex datadir refuses.
Value: "",
}
GDBMeFlag = cli.BoolFlag{
Name: "gdbme",
Usage: "restart erigon under gdb for debug purposes",
Expand Down Expand Up @@ -2069,6 +2088,24 @@ func SetEthConfig(nodeCtx context.Context, ctx *cli.Command, nodeConfig *nodecfg
cfg.ExperimentalStreamingCommitment = true
}

if ctx.Bool(ExperimentalBinCommitmentFlag.Name) {
cfg.ExperimentalBinCommitment = true
// The variant has to be process-wide before any genesis is computed here:
// dev mode derives the beacon Eth1Data from the EL genesis hash while still
// setting up the config, long before the backend applies the flag.
statecfg.ExperimentalBinCommitment = true
}

if h := ctx.String(ExperimentalBinCommitmentHashFlag.Name); h != "" {
if err := commitment.SetPBinHashSuite(h); err != nil {
Fatalf("%v", err)
}
// Genesis is computed here too, so the suite has to be live before the
// datadir reconciles it.
cfg.BinCommitmentHash = h
statecfg.BinCommitmentHash = h
}

cfg.FcuTimeout = ctx.Duration(FcuTimeoutFlag.Name)
cfg.FcuBackgroundPrune = ctx.Bool(FcuBackgroundPruneFlag.Name)
cfg.FcuBackgroundCommit = ctx.Bool(FcuBackgroundCommitFlag.Name)
Expand Down
20 changes: 20 additions & 0 deletions common/dbg/experiments.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ var (
discardCommitment = EnvBool("DISCARD_COMMITMENT", false)
pruneTotalDifficulty = EnvBool("PRUNE_TOTAL_DIFFICULTY", true)

// CheckHeaderStateRoot gates the post-execution comparison of the computed
// state root against the block header's. On by default; switch off only for
// a chain whose headers this node cannot reproduce — with it off nothing
// cross-checks execution results.
CheckHeaderStateRoot = EnvBool("CHECK_HEADER_STATE_ROOT", true)

warnRootCheckOff = sync.OnceFunc(func() {
log.Warn("HEADER STATE-ROOT CHECK IS DISABLED (CHECK_HEADER_STATE_ROOT=false): nothing cross-checks execution results against headers; a wrong chain will look healthy")
})

// force skipping of any non-Erigon2 .torrent files
DownloaderOnlyBlocks = EnvBool("DOWNLOADER_ONLY_BLOCKS", false)

Expand Down Expand Up @@ -163,6 +173,16 @@ func init() {
}
}

// WarnHeaderStateRootCheckDisabled says once per process that nothing
// cross-checks execution against headers. Node startup and the execution path
// both call it, so a runner that never boots a node still says so.
func WarnHeaderStateRootCheckDisabled() {
if CheckHeaderStateRoot {
return
}
warnRootCheckOff()
}

func ReadMemStats(m *runtime.MemStats) {
if noMemstat {
return
Expand Down
8 changes: 4 additions & 4 deletions db/integrity/commitment_integrity.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ func checkCommitmentRootViaFileData(ctx context.Context, tx kv.TemporalTx, br db

func checkCommitmentRootViaSd(ctx context.Context, tx kv.TemporalTx, f state.VisibleFile, info commitmentRootInfo, logger log.Logger) (*execctx.SharedDomains, error) {
maxTxNum := f.EndRootNum() - 1
sd, err := execctx.NewSharedDomains(ctx, tx, logger, execctx.WithSequentialCommitment())
sd, err := execctx.NewSharedDomains(ctx, tx, logger, execctx.WithHexCommitmentOnly())
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1109,7 +1109,7 @@ func CheckCommitmentHistAtBlk(ctx context.Context, db kv.TemporalRoDB, br dbserv
return err
}
defer tx.Rollback()
sd, err := execctx.NewSharedDomains(ctx, tx, logger, execctx.WithoutDeferredBranchUpdates(), execctx.WithSequentialCommitment())
sd, err := execctx.NewSharedDomains(ctx, tx, logger, execctx.WithoutDeferredBranchUpdates(), execctx.WithHexCommitmentOnly())
if err != nil {
return err
}
Expand Down Expand Up @@ -1177,7 +1177,7 @@ func CheckCommitmentHistAtBlkRange(ctx context.Context, sc SamplerCfg, db kv.Tem
return err
}
defer tx.Rollback()
sd, err := execctx.NewSharedDomains(wCtx, tx, logger, execctx.WithoutDeferredBranchUpdates(), execctx.WithSequentialCommitment())
sd, err := execctx.NewSharedDomains(wCtx, tx, logger, execctx.WithoutDeferredBranchUpdates(), execctx.WithHexCommitmentOnly())
if err != nil {
return err
}
Expand All @@ -1191,7 +1191,7 @@ func CheckCommitmentHistAtBlkRange(ctx context.Context, sc SamplerCfg, db kv.Tem
for blockNum := range sampler.BlockNums(windowStart, windowEnd) {
// Fresh SharedDomains per block: an SD is committed-or-closed,
// never reset in place.
sd, err := execctx.NewSharedDomains(wCtx, tx, logger, execctx.WithoutDeferredBranchUpdates())
sd, err := execctx.NewSharedDomains(wCtx, tx, logger, execctx.WithoutDeferredBranchUpdates(), execctx.WithHexCommitmentOnly())
if err != nil {
return err
}
Expand Down
52 changes: 52 additions & 0 deletions db/integrity/pbin_hex_only_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2026 The Erigon Authors
// This file is part of Erigon.
//
// Erigon is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Erigon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Erigon. If not, see <http://www.gnu.org/licenses/>.

package integrity

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/erigontech/erigon/common/log/v3"
"github.com/erigontech/erigon/db/datadir"
"github.com/erigontech/erigon/db/kv/temporal/temporaltest"
"github.com/erigontech/erigon/db/state/execctx"
"github.com/erigontech/erigon/db/state/statecfg"
)

func withBinCommitment(t *testing.T, on bool) {
t.Helper()
orig := statecfg.ExperimentalBinCommitment
t.Cleanup(func() { statecfg.ExperimentalBinCommitment = orig })
statecfg.ExperimentalBinCommitment = on
}

// The history checks recompute roots with the hex trie: on a bin datadir they must
// refuse, not report a mismatch against correct bin records.
func TestPBinCommitmentHistChecksRefuseBin(t *testing.T) {
// No t.Parallel: mutates process-global statecfg flags.
db := temporaltest.NewTestDB(t, datadir.New(t.TempDir()))
withBinCommitment(t, true)

err := CheckCommitmentHistAtBlk(t.Context(), db, nil, 1, log.LvlInfo, log.New())
require.ErrorIs(t, err, execctx.ErrBinCommitmentUnsupported)

sc, err := NewSamplerCfg(1, 1.0)
require.NoError(t, err)
err = CheckCommitmentHistAtBlkRange(t.Context(), sc, db, nil, 0, 1, log.New())
require.ErrorIs(t, err, execctx.ErrBinCommitmentUnsupported)
}
Loading