Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6e6b28f
db/kv/prune, db/snapshotsync, execution: --persist.receipts.distance …
AskAlexSharov Jul 9, 2026
1c13ee0
db/snapshotsync, execution: make receipts window authoritative on dow…
AskAlexSharov Jul 9, 2026
f43d398
Merge remote-tracking branch 'origin/main' into alex/prune_persist_rc…
AskAlexSharov Jul 10, 2026
0044e1e
save
AskAlexSharov Jul 10, 2026
48b0897
db/snapshotsync: fold rcache-history download filter into isStateSnap…
AskAlexSharov Jul 10, 2026
6357ec2
Merge remote-tracking branch 'origin/main' into alex/prune_persist_rc…
AskAlexSharov Jul 10, 2026
e024ee0
Merge remote-tracking branch 'origin/main' into alex/prune_persist_rc…
AskAlexSharov Jul 10, 2026
9736ead
save
AskAlexSharov Jul 10, 2026
695b165
Merge remote-tracking branch 'origin/main' into alex/prune_persist_rc…
AskAlexSharov Jul 10, 2026
73035b2
db/kv/prune, execution, cmd: support keep-all for --persist.receipts.…
AskAlexSharov Jul 10, 2026
c35788b
save
AskAlexSharov Jul 10, 2026
a7e42f8
Merge branch 'alex/prune_persist_rcp_and_keepall_36' into alex/prune_…
AskAlexSharov Jul 10, 2026
efd6937
save
AskAlexSharov Jul 10, 2026
3ee3f40
save
AskAlexSharov Jul 10, 2026
1a272cd
save
AskAlexSharov Jul 10, 2026
e060452
Merge remote-tracking branch 'origin/main' into alex/prune_persist_rc…
AskAlexSharov Jul 10, 2026
cda9cf4
Merge remote-tracking branch 'origin/main' into alex/prune_persist_rc…
AskAlexSharov Jul 10, 2026
b522c89
Merge remote-tracking branch 'origin/main' into alex/prune_persist_rc…
AskAlexSharov Jul 10, 2026
73846fa
kick ci
AskAlexSharov Jul 10, 2026
129fe43
Merge remote-tracking branch 'origin/main' into alex/prune_persist_rc…
AskAlexSharov Jul 10, 2026
1db6e6b
db/kv/prune, cmd: fix Mode.String flag name and clarify receipts defa…
AskAlexSharov Jul 10, 2026
3466e93
save
AskAlexSharov Jul 10, 2026
9d50edd
save
AskAlexSharov Jul 10, 2026
46cd315
save
AskAlexSharov Jul 10, 2026
d781e37
db/snapshotsync, db/kv/prune, docs: regenerate llms + CR fixes
AskAlexSharov Jul 10, 2026
d522692
docs: fix orphaned default under prune.receipts.distance, regenerate …
AskAlexSharov Jul 11, 2026
19a42d6
db/kv/prune, node/cli: pass CLI flag name into distance parse error m…
AskAlexSharov Jul 11, 2026
5aca799
save
AskAlexSharov Jul 11, 2026
9718c0b
Merge remote-tracking branch 'origin/main' into alex/prune_persist_rc…
AskAlexSharov Jul 11, 2026
8d85250
docs: regenerate llms-full.txt (fix docs-site --check drift)
AskAlexSharov Jul 11, 2026
d1513e8
Merge remote-tracking branch 'origin/main' into alex/prune_persist_rc…
AskAlexSharov Jul 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
2 changes: 1 addition & 1 deletion .github/workflows/qa-rpc-integration-tests-remote.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ jobs:
run: |
echo "Starting Erigon..."

./erigon --datadir $ERIGON_REFERENCE_DATA_DIR --persist.receipts --prune.experimental.include-commitment-history --nodiscover --maxpeers 0 --no-downloader --prune.mode=archive --http=false > erigon.log 2>&1 &
./erigon --datadir $ERIGON_REFERENCE_DATA_DIR --prune.include-receipts --prune.experimental.include-commitment-history --nodiscover --maxpeers 0 --no-downloader --prune.mode=archive --http=false > erigon.log 2>&1 &

ERIGON_PID=$!
ERIGON_EXIT_STATUS=$?
Expand Down
35 changes: 31 additions & 4 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

### Breaking Changes

#### `--persist.receipts`: historical receipts cache now off by default in all prune modes
#### `--prune.include-receipts`: historical receipts cache now off by default in all prune modes

The historical ("fat") receipts cache is no longer enabled by default on non-archive nodes. Previously `--persist.receipts` defaulted on for every prune mode except `archive`; it now defaults off everywhere. The consensus layer was the consumer that justified retaining these receipts on pruned nodes, and it no longer needs them ([#21617](https://github.com/erigontech/erigon/issues/21617)).
The historical ("fat") receipts cache is no longer enabled by default on non-archive nodes. Previously
`--prune.include-receipts` (formerly `--persist.receipts`, still accepted as an alias) defaulted on for every prune mode
except `archive`; it now defaults off everywhere. The consensus layer was the consumer that justified retaining these
receipts on pruned nodes, and it no longer needs them ([#21617](https://github.com/erigontech/erigon/issues/21617)).

**What changed:**

Expand All @@ -15,14 +18,38 @@ The historical ("fat") receipts cache is no longer enabled by default on non-arc
| `blocks` | on | off |
| `minimal` | on | off |

Receipts and logs stay available within a node's retention window regardless: without the cache they are re-executed on demand from state history, so `eth_getLogs` and `eth_getBlockReceipts` keep working, at higher latency. For `full` and `minimal` nodes the availability window is unchanged (receipts follow the state-history window either way). For `blocks` nodes the cache previously made receipts and logs queryable back to genesis; without it they follow the state-history window (last 262,144 blocks) — pass `--persist.receipts` if you rely on full-range `eth_getLogs`.
Receipts and logs stay available within a node's retention window regardless: without the cache they are re-executed on
demand from state history, so `eth_getLogs` and `eth_getBlockReceipts` keep working, at higher latency. For `full` and
`minimal` nodes the availability window is unchanged (receipts follow the state-history window either way). For `blocks`
nodes the cache previously made receipts and logs queryable back to genesis; without it they follow the state-history
window (last 262,144 blocks) — pass `--prune.include-receipts` if you rely on full-range `eth_getLogs`.

**Migration:** existing datadirs are unaffected — `--persist.receipts` is recorded at datadir creation and the stored value wins, so a node already syncing with the cache keeps it. Such a node now logs a startup notice that `--persist.receipts` differs from the value stored in the datadir; pass `--persist.receipts` explicitly to silence it. Only newly-created `full`/`minimal`/`blocks` datadirs start without the cache; pass `--persist.receipts` on a fresh datadir to opt back in.
**Migration:** existing datadirs are unaffected — the receipts-cache setting is recorded at datadir creation and the
stored value wins, so a node already syncing with the cache keeps it. Such a node now logs a startup notice that
`--prune.include-receipts` differs from the value stored in the datadir; pass `--prune.include-receipts` explicitly to
silence it. Only newly-created `full`/`minimal`/`blocks` datadirs start without the cache; pass
`--prune.include-receipts` on a fresh datadir to opt back in.

(#22296) — by @yperbasis

---

#### CLI: receipts and commitment-history pruning flags moved under `--prune.*`

The receipt cache and commitment history now share the `--prune.*` naming used by the rest of the pruning flags. All
former names keep working as aliases, and stored datadir settings are unaffected.

- `--persist.receipts` → `--prune.include-receipts` (alias: `--persist.receipts`, `--experiment.persist.receipts.v2`).
- New `--prune.receipts.distance` (alias: `--persist.receipts.distance`) bounds how far back the receipt cache is kept:
a block count, `keep-all`, or empty/`0` (default) to follow the state-history window. Requires
`--prune.include-receipts`. Snapshots older than the window are skipped at download time.
- `--prune.commitment-history.distance` now also accepts `keep-all` (in addition to a block count); empty or `0` still
keeps everything.

(#22349) — by @AskAlexSharov

---

#### JSON-RPC: block-number strings must use the `0x` hex format

Quoted decimal strings (e.g., `"3"`) are no longer accepted as block-number
Expand Down
12 changes: 7 additions & 5 deletions cmd/erigon/node/config_snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,9 @@ func TestConfigWithFlags(t *testing.T) {
}

// TestPersistReceiptsDefaultByMode pins that the historical receipts cache
// (--persist.receipts) defaults to off in every prune mode, and is enabled only
// when the operator sets the flag explicitly.
// (--prune.include-receipts) defaults to off in every prune mode, and is enabled
// only when the operator sets the flag explicitly. The legacy --persist.receipts
// alias must keep working.
func TestPersistReceiptsDefaultByMode(t *testing.T) {
tests := []struct {
name string
Expand All @@ -204,9 +205,10 @@ func TestPersistReceiptsDefaultByMode(t *testing.T) {
{"full", []string{"--prune.mode=full"}, false},
{"minimal", []string{"--prune.mode=minimal"}, false},
{"blocks", []string{"--prune.mode=blocks"}, false},
{"explicit on with full", []string{"--prune.mode=full", "--persist.receipts"}, true},
{"explicit on with archive", []string{"--prune.mode=archive", "--persist.receipts"}, true},
{"explicit off with full", []string{"--prune.mode=full", "--persist.receipts=false"}, false},
{"explicit on with full", []string{"--prune.mode=full", "--prune.include-receipts"}, true},
{"explicit on with archive", []string{"--prune.mode=archive", "--prune.include-receipts"}, true},
{"explicit off with full", []string{"--prune.mode=full", "--prune.include-receipts=false"}, false},
{"legacy persist.receipts alias", []string{"--prune.mode=full", "--persist.receipts"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
15 changes: 10 additions & 5 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ var (
Value: ethconfig.Defaults.NetworkID,
}
PersistReceiptsV2Flag = cli.BoolFlag{
Name: "persist.receipts",
Aliases: []string{"experiment.persist.receipts.v2"},
Usage: "Download historical Receipts. If disabled: using state-history to re-exec transactions and generate Receipts - all RPC: eth_getLogs, eth_getBlockReceipts will work (just higher latency)",
Name: "prune.include-receipts",
Aliases: []string{"experiment.persist.receipts.v2", "persist.receipts"},
Usage: "Download historical Receipts (stored on disk as the rcache domain: snapshots/history/*rcache*.v). If disabled: using state-history to re-exec transactions and generate Receipts - all RPC: eth_getLogs, eth_getBlockReceipts will work (just higher latency)",
Value: ethconfig.Defaults.PersistReceiptsCacheV2,
}
DevValidatorSeedFlag = cli.StringFlag{
Expand Down Expand Up @@ -1113,9 +1113,14 @@ var (
Usage: "Enables blazing fast eth_getProof for executed block",
Aliases: []string{"experimental.commitment-history", "prune.experimental.include-commitment-history"},
}
CommitmentHistoryDistanceFlag = cli.Uint64Flag{
CommitmentHistoryDistanceFlag = cli.StringFlag{
Name: "prune.commitment-history.distance",
Usage: "Keep commitment history only for the latest N blocks. Older snapshots are skipped at download time. 0 (default) keeps everything. Requires --prune.include-commitment-history.",
Usage: "Keep commitment history only for the latest N blocks, or \"keep-all\". Older snapshots are skipped at download time. Empty or 0 (default) keeps everything. Requires --prune.include-commitment-history",
}
PersistReceiptsDistanceFlag = cli.StringFlag{
Name: "prune.receipts.distance",
Aliases: []string{"persist.receipts.distance"},
Usage: "Keep the receipt cache only for the latest N blocks, or \"keep-all\" to keep it all. Empty or 0 (default) follows the state-history window (NOT keep-all). Older snapshots are skipped at download time. Requires --prune.include-receipts",
}
AlwaysGenerateChangesetsFlag = cli.BoolFlag{
Name: "experimental.always-generate-changesets",
Expand Down
43 changes: 29 additions & 14 deletions db/kv/prune/distance.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package prune

import (
"fmt"
"math"
"strconv"
"strings"
)
Expand All @@ -28,11 +27,9 @@ const (
historyDistanceAliasHint = `a block count or "keep-all"`
)

// ParseBlocksDistance parses a --prune.distance.blocks value. It accepts a
// decimal block count, the named sentinels ("keep-post-merge" →
// KeepPostMergeBlocksPruneMode, "keep-all" → KeepAllBlocksPruneMode), or an
// empty string (returns 0, meaning unset).
func ParseBlocksDistance(s string) (uint64, error) {
// ParseBlocksDistance parses a block-distance value: a decimal count,
// "keep-post-merge", "keep-all", or empty ("" → 0, unset).
func ParseBlocksDistance(s, flagName string) (uint64, error) {
switch normalizeDistanceAlias(s) {
case "":
return 0, nil
Expand All @@ -41,22 +38,40 @@ func ParseBlocksDistance(s string) (uint64, error) {
case "keep-all":
return uint64(KeepAllBlocksPruneMode), nil
default:
return parseDistanceNumber(s, "--prune.distance.blocks", blocksDistanceAliasHint)
return parseDistanceNumber(s, "--"+flagName, blocksDistanceAliasHint)
}
}

// ParseHistoryDistance parses a --prune.distance value. It accepts a decimal
// block count, the "keep-all" alias (keep all state history), or an empty
// string (returns 0, meaning unset). The block-only "keep-post-merge" policy
// is rejected: chain history-expiry has no meaning for state history.
func ParseHistoryDistance(s string) (uint64, error) {
// ParseHistoryDistance parses a state-history distance value: a decimal count,
// "keep-all", or empty ("" → 0, unset).
func ParseHistoryDistance(s, flagName string) (uint64, error) {
return parseStateHistoryDistance(s, "--"+flagName, KeepPostMergeBlocksPruneMode)
}

// ParseCommitmentHistoryDistance parses a commitment-history distance value;
// "keep-all" maps to KeepAllBlocksPruneMode (its default).
func ParseCommitmentHistoryDistance(s, flagName string) (uint64, error) {
return parseStateHistoryDistance(s, "--"+flagName, KeepAllBlocksPruneMode)
}

// ParseReceiptsDistance parses a receipts distance value; "keep-all" maps to
// KeepAllReceiptsPruneMode, distinct from the KeepAllBlocksPruneMode follow-history default.
func ParseReceiptsDistance(s, flagName string) (uint64, error) {
return parseStateHistoryDistance(s, "--"+flagName, KeepAllReceiptsPruneMode)
}

// parseStateHistoryDistance parses a state-history-style distance flag: a decimal
// block count, "keep-all" (→ keepAll), or an empty string (→ 0, unset). Unlike
// Blocks, "keep-post-merge" is rejected — chain history-expiry is meaningless for
// state.
func parseStateHistoryDistance(s, flag string, keepAll Distance) (uint64, error) {
switch normalizeDistanceAlias(s) {
case "":
return 0, nil
case "keep-all":
return math.MaxUint64, nil
return uint64(keepAll), nil
default:
return parseDistanceNumber(s, "--prune.distance", historyDistanceAliasHint)
return parseDistanceNumber(s, flag, historyDistanceAliasHint)
}
}

Expand Down
81 changes: 77 additions & 4 deletions db/kv/prune/distance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func TestParseBlocksDistance(t *testing.T) {
}
for _, tc := range cases {
t.Run(tc.in, func(t *testing.T) {
got, err := ParseBlocksDistance(tc.in)
got, err := ParseBlocksDistance(tc.in, "prune.distance.blocks")
if tc.wantErr {
require.Error(t, err)
return
Expand Down Expand Up @@ -88,7 +88,7 @@ func TestParseHistoryDistance(t *testing.T) {
}
for _, tc := range cases {
t.Run(tc.in, func(t *testing.T) {
got, err := ParseHistoryDistance(tc.in)
got, err := ParseHistoryDistance(tc.in, "prune.distance")
if tc.wantErr {
require.Error(t, err)
return
Expand All @@ -108,11 +108,84 @@ func TestBlocksDistanceCLIValue(t *testing.T) {
// A keep-post-merge Blocks sentinel must render with its readable alias rather
// than the raw MaxUint64 magic number.
func TestModeString_BlocksSentinelAlias(t *testing.T) {
blockDist, err := ParseBlocksDistance("keep-post-merge")
blockDist, err := ParseBlocksDistance("keep-post-merge", "prune.distance.blocks")
require.NoError(t, err)

mode, err := FromCli(archiveModeStr, 0, blockDist, 0)
mode, err := FromCli(archiveModeStr, 0, blockDist, 0, 0)
require.NoError(t, err)
assert.Equal(t, KeepPostMergeBlocksPruneMode, mode.Blocks)
assert.Equal(t, "archive --prune.distance.blocks=keep-post-merge", mode.String())
}

func TestParseCommitmentHistoryDistance(t *testing.T) {
cases := []struct {
in string
want uint64
wantErr bool
}{
{in: "", want: 0},
{in: "100000", want: 100_000},
{in: "keep-all", want: uint64(KeepAllBlocksPruneMode)},
{in: "Keep-All", want: uint64(KeepAllBlocksPruneMode)},
{in: "0x186a0", want: 100_000},
{in: "keep-post-merge", wantErr: true},
{in: "garbage", wantErr: true},
}
for _, tc := range cases {
t.Run(tc.in, func(t *testing.T) {
got, err := ParseCommitmentHistoryDistance(tc.in, "prune.commitment-history.distance")
if tc.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tc.want, got)
})
}
}

func TestParseReceiptsDistance(t *testing.T) {
cases := []struct {
in string
want uint64
wantErr bool
}{
{in: "", want: 0},
{in: "100000", want: 100_000},
// keep-all maps to KeepAllReceiptsPruneMode, distinct from the
// KeepAllBlocksPruneMode follow-history default.
{in: "keep-all", want: uint64(KeepAllReceiptsPruneMode)},
{in: "Keep-All", want: uint64(KeepAllReceiptsPruneMode)},
{in: "keep-post-merge", wantErr: true},
{in: "garbage", wantErr: true},
}
for _, tc := range cases {
t.Run(tc.in, func(t *testing.T) {
got, err := ParseReceiptsDistance(tc.in, "prune.receipts.distance")
if tc.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tc.want, got)
})
}
}

// Explicit receipts keep-all must round-trip through FromCli, be distinct from
// the follow-history default, and render with its alias (not the magic number).
func TestModeString_ReceiptsKeepAll(t *testing.T) {
dist, err := ParseReceiptsDistance("keep-all", "prune.receipts.distance")
require.NoError(t, err)

mode, err := FromCli(minimalModeStr, 0, 0, 0, dist)
require.NoError(t, err)
assert.Equal(t, KeepAllReceiptsPruneMode, mode.Receipts)
assert.False(t, mode.ReceiptsFollowHistory(), "explicit keep-all is not the follow-history default")
assert.Contains(t, mode.String(), "--prune.receipts.distance=keep-all")

def, err := FromCli(minimalModeStr, 0, 0, 0, 0)
require.NoError(t, err)
assert.True(t, def.ReceiptsFollowHistory(), "unset receipts follows history")
assert.NotContains(t, def.String(), "prune.receipts.distance")
}
Loading
Loading