From ff5fa7065e38d75966de5446b366efb4abc01ac6 Mon Sep 17 00:00:00 2001 From: Marvin Jones Date: Thu, 13 Aug 2026 16:41:16 -0400 Subject: [PATCH] feat(lee): fold program storage into public_state, drop the separate programs map 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. --- lee/state_machine/src/state/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lee/state_machine/src/state/mod.rs b/lee/state_machine/src/state/mod.rs index 814315efa..32b085ef0 100644 --- a/lee/state_machine/src/state/mod.rs +++ b/lee/state_machine/src/state/mod.rs @@ -112,6 +112,10 @@ impl BorshDeserialize for NullifierSet { #[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] #[cfg_attr(test, derive(Debug))] pub struct V03State { + /// Deployed programs live here too, as `Account`s keyed by `AccountId::from(program_id)` + /// (see that impl's doc comment), with the elf held in `Account.data` and `program_owner` + /// set to the reserved `PROGRAM_STORAGE_OWNER` (see its doc comment for why that ownership + /// choice is load-bearing now that these accounts are reachable via ordinary dispatch). public_state: HashMap, private_state: (CommitmentSet, NullifierSet), }