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.
programs() is removed; dispatch and the deployment-existence check go
through get_account_by_id_ref like any other account lookup.
genesis_fingerprint drops its separate program-hashing loop, since
program accounts now fall out of the existing public_state loop.
Rebuilt all guest artifacts and the test fixture via just
build-artifacts as a precaution, since V03State's Borsh shape changed
even though Account's did not.
Removed explanatory comments that restated context better left to commit
history/PR description across the deploy-dispatch and program-storage code.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.
programs() is removed; dispatch and the deployment-existence check go
through get_account_by_id_ref like any other account lookup.
genesis_fingerprint drops its separate program-hashing loop, since
program accounts now fall out of the existing public_state loop.
Rebuilt all guest artifacts and the test fixture via just
build-artifacts as a precaution, since V03State's Borsh shape changed
even though Account's did not.
Chained-call and public-transaction dispatch now address the target
program directly by AccountId instead of routing through ProgramId and
converting internally, closing the gap that blocked PDA-addressed
program invocation. The field is named program_account_id (not
account_id) to stay unambiguous next to the account_ids list it sits
beside in the same structs.
Execution/PDA-derivation logic that fundamentally needs the RISC0 image
id (self_program_id, caller_program_id, env::verify, PDA seed
derivation) stays ProgramId-typed, recovering it from the dispatched
AccountId via the existing bijection where needed.
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.
programs() is removed; dispatch and the deployment-existence check go
through get_account_by_id_ref like any other account lookup.
genesis_fingerprint drops its separate program-hashing loop, since
program accounts now fall out of the existing public_state loop.
Rebuilt all guest artifacts and the test fixture via just
build-artifacts as a precaution, since V03State's Borsh shape changed
even though Account's did not.
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.
* 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>
Decentralized-sequencing foundation: a shared chain_state crate (two-tier
head/final ChainState, apply_block, AcceptOutcome, StallReason, and the
absorbed channel-consistency machinery), turn-gated block production, the
publisher follow path for adopted/orphaned/finalized peer blocks, and
persistence that keeps disk order equal to apply order under the chain lock.
Rebased onto dev after #600/#606: chain_consistency is absorbed into
chain_state, the sequencer bootstrap's verify_and_reconstruct is re-wired
onto the two-tier ChainState (reconstruction applies channel history
through the final tier and persists via the follow-path primitives), and
test fixtures adopt the SequencerSetup builder extended with
with_bedrock_signing_key.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BREAKING CHANGE: GenesisAction::DeployProgram and IndexerConfig.deploy_programs are removed. The cross-zone programs (inbox, outbox, ping_sender, ping_receiver, bridge_lock, wrapped_token) are now base builtins in testnet_initial_state, because program bytecode exceeds the genesis block inscription limit, so they cannot be shipped through genesis transactions.
Cross-zone builtin programs are no longer registered in the production
genesis. A zone that participates declares the ones it uses via a new
GenesisAction::DeployProgram (sequencer) and a matching deploy_programs
list (indexer), both resolved through CrossZoneProgram and registered
with with_programs. Cross-zone genesis accounts (inbox config,
wrapped-token config) are seeded through the state constructor for a
receiving zone, and bridge-lock holdings are seeded from their actions
regardless of receiving config, dropping V03State::insert_genesis_account.
GenesisAction amounts now use the Balance alias. Documents cross_zone as
the reference LEZ adapter and the bridge demo as not production-safe.
The sequencer's DeployProgram set and the indexer's deploy_programs are
configured separately, so both nodes now log a deterministic genesis
fingerprint (V03State::genesis_fingerprint) at startup: equal values
confirm the two genesis states agree, a mismatch flags a divergent
deploy set.
BREAKING CHANGE: the genesis state root changes (cross-zone builtins are
out of production genesis) and the sequencer/indexer configs gain the
DeployProgram / deploy_programs list that cross-zone-participating zones
must set.
BREAKING:
Before: An endpoint exposing getting a single proof for a commitment
existed.
After: There is one endpoint where you give a vector of commitments
and a vector of Maybe proofs back alongside the shared root.
Mitigation: Use the new rpc endpoint with the appopriate vector.
* refactor(lee): split large modules into directories and extract tests
Split state.rs, program.rs, circuit.rs, validated_state_diff.rs,
merkle_tree, and core/program.rs into module directories with separate
test files. State tests are further split into themed files (genesis,
authenticated_transfer, circuit, claiming, etc.). Extract
authenticate_public_transaction_signers helper in validated_state_diff
to remove duplicated authentication logic.
* chore: rebuild artifacts