Skip to content

Ledger-bloat mitigation: state commitment, proofs, retention, cold-pending sweep, storage-weighted PoW - #5150

Open
dhyabi2 wants to merge 5 commits into
nanocurrency:developfrom
dhyabi2:feat/state-commitment
Open

Ledger-bloat mitigation: state commitment, proofs, retention, cold-pending sweep, storage-weighted PoW#5150
dhyabi2 wants to merge 5 commits into
nanocurrency:developfrom
dhyabi2:feat/state-commitment

Conversation

@dhyabi2

@dhyabi2 dhyabi2 commented Aug 21, 2026

Copy link
Copy Markdown

Summary

A read-only foundation for bounding ledger growth from dust / receivable spam, in five layers. Each is measurement / planning / advisory only — no consensus, validation, or storage behavior changes, and no destructive deletion — but together they are the machinery a storage-capped node and a work-throttle would build on.

Motivation

A node can create unbounded permanent state by sending 1-raw "dust" to fabricated destination accounts: each send writes 32 arbitrary bytes into its own chain (link) and mints a permanent pending entry that is never received. Existing mechanisms don't bound this — pruning is opt-in, keeps every account head block, and cannot remove pending entries; balance-bucketed prioritization governs confirmation order, not retention; PoW is a one-time cost uncorrelated with lifetime storage.

The five layers

  1. State commitment (compute_state_commitment) — folds the cemented account frontier and cemented pending set, in canonical store key order, into a Merkle Mountain Range: a canonical root plus per-subtree roots and counts. Pure function of cemented state (byte-identical across honest nodes); domain-separated leaf/node hashes; little-endian integers.

  2. Inclusion proofs (generate_account_proof / generate_pending_proof / verify_state_proof) — succinct MMR proofs against the root; verification is a pure function with no store access, so a light client can run it. A generated proof reconstructs byte-identically the commitment root.

  3. Capped-retention planner (capture_state_checkpoint / plan_capped_retention) — classifies cemented blocks as kept vs droppable for a per-account history window, quantifies reclaimable bytes, and proves each retained account frontier against the checkpoint root. Safety invariant: the commitment leaf set is account frontiers + pending only, never pre-frontier history, so dropping history leaves the root unchanged.

  4. Cold-pending sweep (plan_pending_sweep / generate_cold_pending_proof / verify_cold_pending_proof) — partitions the cemented pending set into hot vs cold (aged AND sub-threshold), commits the cold set under cold_root, and proves each cold entry stays claimable. Cold entries are never expired or returned, preserving the irreversible-send guarantee; a claimant presents a store-free proof to receive. Pruned (age-unreadable) entries stay hot; timestamps only age forward.

  5. Storage-weighted PoW (block_adds_new_account / evaluate_storage_weighted_work) — a state send to a not-yet-opened account creates new permanent footprint, so its required work is scaled by a multiplier (floored at 1.0). Prices the mass-dust vector in CPU without any fee. Advisory policy calculator; consensus validation is unchanged.

Interface (RPCs)

  • state_commitment → root + sub-roots + counts
  • state_proof (account, or account+hash) → claim, path, peaks, roots, verified
  • state_checkpoint{ cemented_height, root, counts }
  • state_retention_plan (window, count) → kept/droppable blocks, reclaimable_bytes, per-account proof safety
  • state_pending_sweep (age, threshold, reference_timestamp, count) → hot/cold counts, cold_root, reclaimable_pending_bytes, cold-proof safety
  • work_storage_weight (hash, multiplier) → base/required threshold, achieved difficulty, creates_new_account, satisfies

Changes

  • nano/secure/state_commitment.{hpp,cpp} — commitment, MMR, proofs + pure verify, checkpoint + retention planner, pending sweep + cold proofs (shared leaf/hash/MMR/reconstruct helpers)
  • nano/secure/storage_weighted_work.{hpp,cpp} — new-account detection + storage-weighted work policy
  • nano/node/json_handler — the six RPCs above
  • nano/core_test/state_commitment.cpp, nano/core_test/storage_weighted_work.cpp — tests for all five layers

Testing

core_test covers all five layers, including tamper rejection on both proof types, hot/cold classification negatives, the retention safety invariant, and the work-weighting branches. The MMR fold, domain-separated hashing, canonical ordering, proof generation/verification, retention arithmetic + invariant, and cold classification + cold-proof verification were additionally validated standalone against the tree's blake2b reference implementation across every mountain shape (n = 1…4999).

🤖 Generated with Claude Code

dhyabi2 and others added 2 commits August 21, 2026 17:03
Introduce a read-only Merkle commitment over cemented ledger state as the
first step toward bounding ledger growth from dust/receivable spam. A node
can create unbounded permanent state by sending 1-raw "dust" to fabricated
destination accounts: each send writes 32 arbitrary bytes into its own chain
and mints a permanent pending entry that is never received. Neither existing
pruning (opt-in, keeps head blocks, cannot touch pending) nor bucketed
prioritization (orders confirmation, not retention) bounds that storage.

This change adds compute_state_commitment(), which folds the cemented account
frontier and the cemented pending set - in canonical store key order - into a
Merkle Mountain Range and returns a single canonical root plus per-subtree
roots and counts. The root is a pure function of cemented state: two honest
nodes on the same cemented set produce byte-identical roots. Leaf and node
hashes are domain-separated; integers are encoded little-endian for
platform independence. Cost is O(cemented accounts + pending) reads and
O(log n) memory, run off the hot path.

This is measurement-only and changes no consensus, validation, or storage
behavior. It is the foundation for later work: succinct membership/balance
proofs against the root, storage-capped nodes that keep only heads plus the
root and backfill on demand, and cold-storing aged sub-threshold pending
behind the commitment.

- nano/secure/state_commitment.{hpp,cpp}: commitment + MMR implementation
- nano/node/json_handler: "state_commitment" RPC exposing the root and counts
- nano/core_test/state_commitment.cpp: determinism, genesis-only, and
  dust-pending sensitivity tests

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Build on the cemented state commitment with a proof service so a holder of
only the trusted root (a light or storage-capped node) can verify that a
specific account frontier+balance, or a specific pending entry, belongs to
cemented state - without holding the ledger. This is what turns the
commitment into actual storage relief: history and cold pending can be
discarded once their membership is provable on demand.

generate_account_proof() / generate_pending_proof() return a Merkle Mountain
Range inclusion proof: the authentication path from the claimed leaf to its
mountain peak, the ordered peak set, the sibling sub-tree root, and the
counts. verify_state_proof() recomputes the leaf from the claim, climbs to
the peak, rebags the peaks and reassembles the overall root exactly as
compute_state_commitment() does, then compares - a pure function that
touches no store, so a light client can run it. A generated proof
reconstructs byte-identically the same root the commitment produces.

The commitment computation is refactored to share one set of leaf/hash/MMR
helpers with proof generation, so the two can never diverge. Behavior of the
existing root is unchanged.

- nano/secure/state_commitment.{hpp,cpp}: proof structures, generation, and
  pure verification; shared MMR helpers
- nano/node/json_handler: "state_proof" RPC (account, or account+hash for a
  pending proof) returning the proof and a self-check
- nano/core_test/state_commitment.cpp: account and pending proof roundtrips,
  root-equivalence, and tamper rejection

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dhyabi2 dhyabi2 changed the title Add cemented state commitment (Merkle root over accounts + pending) Cemented state commitment + succinct inclusion proofs Aug 21, 2026
A storage-capped node keeps, per account, only its cemented frontier plus a
bounded window of recent history and discards everything cemented below that.
The discarded content stays committed under the checkpoint root and can be
re-fetched with an inclusion proof if ever needed, so the node's disk shrinks
while it remains verifiable. This is the safety-gated planner for that mode.

capture_state_checkpoint() snapshots the current cemented commitment as an
anchor {height, root, counts} that a capped node and light clients prove
against. plan_capped_retention() classifies cemented blocks as kept vs
droppable for a given per-account window, quantifies the reclaimable storage,
and - critically - proves each retained account frontier still verifies
against the checkpoint root before anything is considered droppable.

The safety rests on a structural invariant: the commitment leaf set is the
account FRONTIERS plus the pending set, never pre-frontier history. Dropping
history therefore alters no leaf and leaves the root unchanged, so a proof
generated after retention reconstructs the same root. The planner asserts
this per account rather than assuming it.

This computes and proves the safe-to-drop set and the reclaimable bytes; it
performs no destructive deletion. Actual block dropping plus on-demand
proof-backed backfill is the follow-on that uses this planner as its gate.

- nano/secure/state_commitment.{hpp,cpp}: checkpoint capture + retention planner
- nano/node/json_handler: "state_checkpoint" and "state_retention_plan" RPCs
- nano/core_test/state_commitment.cpp: retention classification, reclaimable
  accounting, and per-account proof safety against the checkpoint root

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dhyabi2 dhyabi2 changed the title Cemented state commitment + succinct inclusion proofs Cemented state commitment, inclusion proofs, and capped-retention planner Aug 21, 2026
The pending table is the un-prunable dust residue: a 1-raw send to a
never-opened account mints a receivable that lives forever on every node.
Expiring or returning it would break the irreversible-send guarantee and is
unsound without a global clock, so instead this identifies AGED, SUB-THRESHOLD
pending entries and moves them out of the hot working set into a committed
COLD subtree. A cold entry is never lost or returned: its membership stays
committed under cold_root and its receiver can still claim it at any time by
presenting an inclusion proof. This classifies, commits, and proves; it
deletes nothing.

plan_pending_sweep() partitions the cemented pending set into hot vs cold for
a given age (send-block timestamp vs a caller-supplied reference time, so the
library stays deterministic) and amount threshold, computes cold_root over
exactly the cold entries, quantifies reclaimable bytes, and proves each cold
entry stays claimable against cold_root. generate_cold_pending_proof() /
verify_cold_pending_proof() give a receiver a standalone, store-free proof of
their offloaded entry. A send block that is pruned (age unreadable) is kept
hot, and future/skewed timestamps only age forward.

The MMR root reconstruction shared by the overall-root verifier and the cold
verifier is factored into one helper so they cannot diverge.

- nano/secure/state_commitment.{hpp,cpp}: pending classification, sweep planner,
  cold proof generation + pure verification; shared reconstruct helper
- nano/node/json_handler: "state_pending_sweep" RPC
- nano/core_test/state_commitment.cpp: cold classification, reclaimable
  accounting, cold-proof roundtrip + tamper rejection, and hot-path negatives

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dhyabi2 dhyabi2 changed the title Cemented state commitment, inclusion proofs, and capped-retention planner State commitment, proofs, retention planner, and cold-pending sweep Aug 21, 2026
Proof-of-work today is a one-time issuance cost uncorrelated with the
permanent storage a block imposes, so minting dust is nearly free. This
prices the mass-dust vector in CPU - never a monetary fee, so the feeless
model is preserved: a state send whose destination account is not yet opened
creates new permanent ledger footprint, so it is required to carry more work
than a send to an already-existing account.

Legitimate first-contact onboarding pays the higher cost once per real new
account (rare, acceptable), while an attacker fanning dust out to fabricated
destinations pays it on every block; dodging it by pre-opening the
destinations costs an open + receive per destination, which is the
self-limiting cost the throttle imposes.

block_adds_new_account() detects the state-creating send (destination account
absent from the ledger). evaluate_storage_weighted_work() computes the
base requirement, scales it by the multiplier when the weight applies (floored
at 1.0 so it can only ever raise the bar), and reports whether the block's
work satisfies it. This is an advisory policy calculator: it does not change
consensus work validation, which would require network-wide activation.

- nano/secure/storage_weighted_work.{hpp,cpp}: detection + policy calculator
- nano/node/json_handler: "work_storage_weight" RPC (hash, multiplier)
- nano/core_test/storage_weighted_work.cpp: new-account weighting, existing-
  account exemption, and the multiplier floor

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dhyabi2 dhyabi2 changed the title State commitment, proofs, retention planner, and cold-pending sweep Ledger-bloat mitigation: state commitment, proofs, retention, cold-pending sweep, storage-weighted PoW Aug 21, 2026
@gr0vity-dev-bot

Copy link
Copy Markdown

Test Results for Commit 505659c

Pull Request 5150: Results
Overall Status:

Test Case Results

  • 5n4pr_conf_10k_bintree: PASS (Duration: 113s)
  • 5n4pr_conf_10k_change: PASS (Duration: 192s)
  • 5n4pr_conf_change_dependant: PASS (Duration: 139s)
  • 5n4pr_conf_change_independant: PASS (Duration: 106s)
  • 5n4pr_conf_send_dependant: PASS (Duration: 108s)
  • 5n4pr_conf_send_independant: PASS (Duration: 114s)
  • 5n4pr_rocks_10k_bintree: PASS (Duration: 113s)
  • 5n4pr_rocks_10k_change: PASS (Duration: 168s)

Last updated: 2026-08-21 14:39:16 UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants