Public accounts' pre-transaction values no longer need to be part of the
circuit's proven public output. Materialization already replays
public_diffs against live sequencer state, never the witnessed
pre-state, and proof-internal consistency has nothing left to check
once the field isn't part of the output at all. Duplicate/inconsistent
witnessing was already structurally impossible via the circuit's own
post_states tracking, independent of whether pre_state was exported, so
no new in-circuit assertion is needed to compensate.
Drop the field from PrivacyPreservingCircuitOutput and Message;
reimplement Message::public_account_ids() from deduped public_diffs
account ids. Update every downstream mirror (indexer protocol/FFI,
explorer UI, mock service) to the same id-only shape, and rebuild the
guest ELF, program artifacts, and prebuilt sequencer fixture to match
the changed journal layout.
Trim the signer_account_ids/PublicDiff explanation down to one canonical
location per concept instead of restating it at every call site, drop
dangling "See X" pointers in favor of letting readers look up the code
directly, and shorten the two malicious-victim test docstrings that
re-derived the same mechanism already covered elsewhere.
Separately, fix pre-existing clippy debt that was hidden behind compile
errors in the two guest binaries clippy could originally reach:
- update_from_diff implementations across most lez/programs/* guests,
test_methods_guests, test_programs/guest, and the program_deployment
examples returned Result<Data, Infallible> for no reason; now return
Data directly.
- unchanged(...) helpers made const fn.
- Program::execute_update_from_diff/prove_update_from_diff took Account/Data
by value without consuming them; now take references, dropping the
now-unnecessary clones at every call site.
- One redundant clone, one redundant closure, one large enum variant
(TxEvaluation::Accept now boxes its LeeTransaction), and four
if-let/else patterns simplified to map_or_else/bool::then in the
indexer FFI's AccountDiff/Claim conversions.
Rebuild every affected guest artifact and the prebuilt test fixture.
A bare "See X" with nothing else doesn't add anything a reader
wouldn't already get from the matching field name and
from_circuit_output's mapping a few lines below. Matches the other
undocumented fields on Message.
Message::public_pre_states restated PrivacyPreservingCircuitOutput::
public_pre_states's doc almost verbatim. Point to it instead, matching
the See-pointer style already used by Message's other two mirrored
fields (public_diffs, signer_account_ids).
The cherry-picked PR3 commit predates the diff_data: Vec<u8> -> Data
refactor, so its diff_data-adjacent code needed re-threading through
Option<Data> instead of Option<Vec<u8>>. Also drops a duplicate
expected_diff() helper left behind by the merge, and fixes an
env::verify owner-resolution mismatch in execution_state.rs: it was
checking the calling program's update_from_diff receipt unconditionally,
while the host (circuit/mod.rs) proves that receipt against the
resolved owner program (falling back to the caller only when the
account is still unclaimed). The circuit-side check now mirrors the
host's resolution.
The privacy-preserving circuit's output no longer commits materialized
public-account post-states; it commits the raw AccountDiffs it produced
instead, plus a signer_account_ids set the sequencer cross-checks
against real signatures. Proof verification now checks the proof
against exactly what the circuit witnessed, with no dependency on live
sequencer state, fixing the race condition where an unrelated public
transaction landing between proving and validation would invalidate an
otherwise-valid proof. Materialization moves to a separate step that
replays public_diffs against whatever the live account state actually
is at apply time, mirroring how the public-transaction path already
applies diffs.
Also updates lez/indexer and lez/explorer_service for the new Message
shape, and fixes the pre-existing clippy debt this surfaced across
lez/programs (needless_pass_by_value/missing_const_for_fn/etc.) now
that the workspace builds and lints clean end to end.
Resolves needless_pass_by_value (take AccountWithMetadata/Account by
reference where only borrowed), unnecessary_wraps (drop Result<Data,
Infallible> from passthrough update_from_diff implementations),
missing_const_for_fn, useless_let_if_seq, arbitrary_source_item_ordering,
too_many_arguments, redundant_clone, and useless_conversion across the
guest programs and lee core touched by the incremental-updates rebase.
marvin/incremental-updates-2 was not cargo +nightly fmt --check clean;
these files had drifted since the last fmt pass (mostly unwrapped
long import lists). No functional changes.
Rebasing onto marvin/incremental-updates-1's AccountDiff.diff_data:
Option<Data> change (was Option<Vec<u8>>) needed real fixes, not just a
mechanical rebase: every program's update_from_diff and every
diff_data-constructing call site was still written against the old
Vec<u8> shape. Several sites were doing a pointless Data -> Vec<u8> ->
Data round trip to satisfy the old field type; those now just pass the
Data value straight through. The host-side
execute_update_from_diff/prove_update_from_diff/write_update_from_diff_inputs
in lee/state_machine move to Data too, matching what now flows from
lee_core.
The same "diff_data is already the fully-computed encoding, so this is
a passthrough" explanation was copy-pasted across 13 update_from_diff
implementations. The general contract is already documented once,
centrally, on ProgramCall::UpdateFromDiff in lee_core; each per-program
repeat added nothing beyond what the unused _pre_state parameter and a
one-line body already convey.
Programs now report AccountDiff/AccountDiffOutput instead of full
post-states; balance changes are applied via apply_balance_diff's
checked arithmetic at the protocol level rather than checked in-guest,
and data changes are materialized through a new update_from_diff
guest entrypoint (trusted execution for public transactions, proven
via a recursive receipt for the privacy-preserving circuit). All 15
production programs and test/example guest programs are converted;
attack-surface guest programs that are now structurally impossible
(nonce/program_owner mutation) or redundant (manual balance-sufficiency
bypass) are moved to a dormant/ directory rather than deleted outright.
UpdateFromDiffOutput's docstring didn't make clear that this journal is
never the privacy-preserving circuit's own output — it's a separate
program's inner receipt, reconstructed and checked via env::verify, then
discarded. Clarify that, and trim the rest of the comment down.
write_update_from_diff_output took its inputs by reference and cloned
them to build the owned UpdateFromDiffOutput it commits. It has no
callers yet on this branch and nothing else needs the values afterward,
so take them by value and move them in instead.
Rebuild every guest artifact and the prebuilt sequencer fixture: they
were stale relative to this branch's own committed source (confirmed via
mtimes — most of lee_core and every touched program's source postdated
the checked-in .bin files), causing spurious DeserializeBadBool failures
for anyone running tests against a fresh checkout without first running
`just build-artifacts`.
h2 0.4.14 had an unbounded empty DATA frames flaw (low severity); bumped to
0.4.18 via cargo update -p h2. cargo deny check now passes clean
(advisories/bans/licenses/sources all ok).
Rebuild every guest artifact and the prebuilt test fixture to match the
dependency bump.
This PR introduces the initial Cucumber-based integration test framework for the LEZ, building on the testing-framework integration work started by @andrussal, adds the first set of Cucumber integration scenarios and establishes reusable infrastructure for future Cucumber scenarios.
---------
Co-authored-by: Andrus Salumets <salumets.andrus@gmail.com>
Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com>
Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
Co-authored-by: Roman <zajic@zajic.net>
Co-authored-by: Daniil Polyakov <arjentix@gmail.com>
Co-authored-by: Moudy <m.ellaz@hotmail.com>
Co-authored-by: andrussal <salumets.andrus@gmail.com>
AccountDiff.diff_data, ProgramCall::UpdateFromDiff.diff_data,
UpdateFromDiffOutput.diff_data, and write_update_from_diff_output's
parameter all move from Vec<u8>/&[u8] to Data, so this payload carries
Data's length restriction (DATA_MAX_LENGTH) and validated deserialization
everywhere it flows, rather than only once it lands in account.data.
V03State.programs is gone; deployed programs now live directly in public_state, keyed by AccountId::from(program_id) same as any other account. insert_program sets program_owner to a new reserved sentinel, PROGRAM_STORAGE_OWNER, instead of leaving it at the default.
That ownership choice is load-bearing now in a way it wasn't before: once program accounts share the same map as everything else, they're reachable through ordinary dispatch, so program_owner determines whether they're claimable/writable. Left unclaimed, a program invocation could legitimately claim a program's storage account via the normal claim path and then rewrite its elf; self-ownership has the same flaw, since it authorizes exactly the program whose own invocation would touch its own storage account. The reserved sentinel makes every program account unwritable by construction, since no real chained_call.program_id will ever derive to it.
Also centralizes the program-ownership check behind V03State::get_program and applies the program_owner AccountId migration to code added after the earlier rebase.
BREAKING CHANGE: CrossZonePeer.expected_block_signing_pubkey (single optional
key) is renamed to expected_block_signing_pubkeys (a list, empty = unchecked),
and cross-zone config now refuses unknown fields at startup.
* feat(lee): store deployed programs as Account-shaped state, keyed by AccountId
Program-as-Account migration, first slice: V03State.programs becomes
HashMap<AccountId, Account> instead of HashMap<ProgramId, Program>,
with the elf held directly in Account.data. The map key is derived
from ProgramId via a new 1:1 From<ProgramId> for AccountId conversion
(both types are exactly 32 bytes) rather than a hash, since ProgramId
is already content-derived from the elf.
Account.program_owner stays ProgramId-typed everywhere - this only
changes how deployed programs are stored and looked up host-side, not
the dispatch/authorization model any guest program logic depends on.
Dispatch resolves a ChainedCall's program_id by converting to
AccountId, fetching the Account, and reconstructing a Program via
new_unchecked for execution.
DATA_MAX_LENGTH is raised from 100 KiB to 700 KiB to fit real program
elfs (observed 375 KB-631 KB) directly in Account.data; noted in its
docstring as a rough placeholder pending real transaction/block-size
budget analysis.
* fix(lee): store deployed programs as Account-shaped state, correct SeenShard cap
Corrects lee/state_machine internals for the Program-as-Account migration
and fixes SeenShard::MAX_DELIVERIES, which was still calibrated for the
old 100 KiB DATA_MAX_LENGTH instead of the current 700 KiB cap. Rebuilds
program artifacts and the sequencer test fixture to match.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* address PR #720 review nits
- Use FIXME instead of TODO for the temporary ProgramId->AccountId
conversion, per review convention for patches guaranteed to be
fixed later.
- Derive cross_zone_inbox's MAX_DELIVERIES from DATA_MAX_LENGTH
instead of a hand-recomputed literal, so it stays in sync
automatically the next time the cap changes.
* feat(lee): migrate Account.program_owner from ProgramId to AccountId
Account.program_owner is now AccountId-typed instead of ProgramId,
via a new bijective From<ProgramId> for AccountId / From<AccountId>
for ProgramId conversion pair (pure byte reinterpretation, not a
hash - both types are exactly 32 bytes). Adds DEFAULT_PROGRAM_OWNER
as the AccountId-typed counterpart to DEFAULT_PROGRAM_ID, used at
every program_owner comparison/claim site instead of an inline
AccountId::default().
Touches every call site across lee_core, lee (including the
guest-side privacy-preserving circuit), all 16 deployed guest
programs, wallet/wallet-ffi, indexer_ffi/indexer_service/
indexer_service_protocol, sequencer_core, testnet_initial_state,
system_accounts, cross_zone, storage, cycle_bench, and
integration_tests - mostly mechanical .into() conversions, plus two
simplifications: wallet's manual base58 encode/decode of
program_owner was dead code once it's AccountId (which already has
Display/FromStr), and the FFI crates' program_owner field now reuses
the existing generic FfiBytes32 wrapper instead of the now-unused
FfiProgramId one.
Rebuilds every guest ELF artifact and the prebuilt sequencer test
fixture via just build-artifacts, since execute_and_prove runs
against the checked-in precompiled privacy_preserving_circuit.bin,
which isn't rebuilt automatically by cargo test/check.
* chore(lee): rebuild artifacts after rebase, drop unused base58 dep
Rebases marvin/program-as-account-2 onto the updated
marvin/program-as-account (SeenShard cap fix), regenerating program
and circuit artifacts plus the sequencer test fixture to match.
Also removes lez/wallet's now-unused base58 dependency, dead since
AccountId gained its own Display/FromStr base58 encoding.
* docs(lee): trim DEFAULT_PROGRAM_OWNER and From<AccountId> for ProgramId docs
* test(lee): add known-answer tests for ProgramId/AccountId conversion, rebuild artifacts
* fix(lee): apply program_owner AccountId migration to code added after rebase
dev grew new program_owner call sites (sequencer_stake genesis/config
handling, committee_discovery, a new selective_pda_delegator test
program, and related tests) after this branch's ProgramId->AccountId
migration commit was originally written, so they predated the .into()
sweep and didn't conflict during the rebase - they just still assumed
the old ProgramId-typed field. Converts all of them, fixes a stray
unseparated hex literal clippy caught along the way, and rebuilds
artifacts against the fixed source.
* chore(lee): regenerate test fixture after rebasing onto dev
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* feat(lee): store deployed programs as Account-shaped state, keyed by AccountId
Program-as-Account migration, first slice: V03State.programs becomes
HashMap<AccountId, Account> instead of HashMap<ProgramId, Program>,
with the elf held directly in Account.data. The map key is derived
from ProgramId via a new 1:1 From<ProgramId> for AccountId conversion
(both types are exactly 32 bytes) rather than a hash, since ProgramId
is already content-derived from the elf.
Account.program_owner stays ProgramId-typed everywhere - this only
changes how deployed programs are stored and looked up host-side, not
the dispatch/authorization model any guest program logic depends on.
Dispatch resolves a ChainedCall's program_id by converting to
AccountId, fetching the Account, and reconstructing a Program via
new_unchecked for execution.
DATA_MAX_LENGTH is raised from 100 KiB to 700 KiB to fit real program
elfs (observed 375 KB-631 KB) directly in Account.data; noted in its
docstring as a rough placeholder pending real transaction/block-size
budget analysis.
* fix(lee): store deployed programs as Account-shaped state, correct SeenShard cap
Corrects lee/state_machine internals for the Program-as-Account migration
and fixes SeenShard::MAX_DELIVERIES, which was still calibrated for the
old 100 KiB DATA_MAX_LENGTH instead of the current 700 KiB cap. Rebuilds
program artifacts and the sequencer test fixture to match.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* address PR #720 review nits
- Use FIXME instead of TODO for the temporary ProgramId->AccountId
conversion, per review convention for patches guaranteed to be
fixed later.
- Derive cross_zone_inbox's MAX_DELIVERIES from DATA_MAX_LENGTH
instead of a hand-recomputed literal, so it stays in sync
automatically the next time the cap changes.
* chore: regenerate artifacts after rebasing onto dev
Binary program artifacts and the prebuilt sequencer DB dump were left
as rebase-conflict placeholders; regenerated via `just build-artifacts`
against the fully rebased source.
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>