mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-13 19:49:29 +00:00
update
This commit is contained in:
parent
a338a0b7ae
commit
204c757bdf
@ -342,7 +342,7 @@ The kind is serialized as a fixed 81-byte header prepended to the encrypted acco
|
||||
|
||||
```
|
||||
Regular(ident): 0x00 || ident (16 bytes LE) || [0u8; 64]
|
||||
Pda { program_id, seed, ident }: 0x01 || program_id (32 bytes) || seed (32 bytes) || ident (16 bytes LE)
|
||||
Pda { program_id, seed, ident }: 0x01 || program_id (8 × u32 LE) || seed (32 bytes) || ident (16 bytes LE)
|
||||
```
|
||||
|
||||
Both variants produce 81 header bytes, so ciphertext lengths are uniform across account types.
|
||||
@ -436,11 +436,16 @@ All programs share the same function signature. They take as input:
|
||||
3. A list of accounts, each annotated with metadata.
|
||||
4. An instruction-specific data word list.
|
||||
|
||||
They output:
|
||||
Programs are treated as blackboxes: given some inputs, they produce a `ProgramOutput` that represents their claimed state transition. All validation is performed on the output, never on the raw inputs. In privacy-preserving transactions the sequencer sees only a ZK proof of the circuit; the circuit cryptographically verifies each program output without having access to the program's inputs. In public transactions the program is executed directly, but the same output-based validation applies — this uniformity is intentional, so that the constraint rules do not differ between the two execution paths.
|
||||
|
||||
- A list of input account states echoed from the input (used for circuit verification).
|
||||
- A list of accounts representing the post-execution state.
|
||||
- Optionally, a list of chained calls representing other program execution calls.
|
||||
`ProgramOutput` is the program's complete claimed state transition and contains:
|
||||
|
||||
- `pre_states` — the accounts the program claims to have operated on, including their pre-execution state.
|
||||
- `post_states` — the resulting account states after execution.
|
||||
- `chained_calls` — queued executions of other programs.
|
||||
- `self_program_id` and `caller_program_id` — used by the verifier to check that the output was produced by the expected program and invoked through the correct call chain.
|
||||
- `instruction_data` — used to verify that each chained call was executed with the instruction the calling program requested.
|
||||
- `block_validity_window` and `timestamp_validity_window` — range constraints on which blocks/timestamps this output is valid for.
|
||||
|
||||
Formally:
|
||||
|
||||
@ -456,13 +461,26 @@ pub struct AccountPostState {
|
||||
claim: Option<Claim>,
|
||||
}
|
||||
|
||||
pub struct ProgramOutput {
|
||||
self_program_id: ProgramId,
|
||||
caller_program_id: Option<ProgramId>,
|
||||
instruction_data: InstructionData,
|
||||
pre_states: Vec<AccountWithMetadata>,
|
||||
post_states: Vec<AccountPostState>,
|
||||
chained_calls: Vec<ChainedCall>,
|
||||
block_validity_window: BlockValidityWindow,
|
||||
timestamp_validity_window: TimestampValidityWindow,
|
||||
}
|
||||
|
||||
/// A claim request indicating the executing program intends to take ownership of an account.
|
||||
pub enum Claim {
|
||||
/// Ownership via user authorization (signature or nullifier key proof).
|
||||
/// Standard claim path, used for all account kinds that are not self-owned PDAs. Succeeds
|
||||
/// when the account's `is_authorized` flag is true (public accounts, public PDAs, private
|
||||
/// PDAs), or unconditionally for standalone private accounts.
|
||||
Authorized,
|
||||
/// Ownership via a PDA seed.
|
||||
/// The AccountId is derived from (caller_program_id, seed) for public PDAs,
|
||||
/// or from (caller_program_id, seed, npk, identifier) for private PDAs.
|
||||
/// Ownership via a PDA seed. Only valid for PDAs owned by the executing program itself:
|
||||
/// the AccountId must match the derivation from (self_program_id, seed) for public PDAs,
|
||||
/// or from (self_program_id, seed, npk, identifier) for private PDAs.
|
||||
Pda(PdaSeed),
|
||||
}
|
||||
|
||||
@ -477,18 +495,13 @@ pub struct ChainedCall {
|
||||
|
||||
type Program = fn(
|
||||
ProgramId, Option<ProgramId>, List<AccountWithMetadata>, InstructionData
|
||||
) -> Result<(List<AccountWithMetadata>, List<AccountPostState>, List<ChainedCall>)>;
|
||||
) -> ProgramOutput;
|
||||
```
|
||||
|
||||
We will refer to Program parameters as:
|
||||
The verifier validates that a `ProgramOutput` satisfies the following constraints:
|
||||
|
||||
- Input parameters: `self_program_id`, `caller_program_id`, `pre_states`, `instruction_data`
|
||||
- Output values: `pre_states` (echoed), `post_states`, `chained_calls`
|
||||
|
||||
All programs must satisfy the following constraints:
|
||||
|
||||
1. Program receives a list of unique accounts as input. Each `AccountId` in the list is unique.
|
||||
2. Program receives and outputs the same number of account states. `pre_states` and `post_states` must have the same length `N`.
|
||||
1. The output's `pre_states` contain unique account IDs. Each `AccountId` in the list is unique.
|
||||
2. The output's `pre_states` and `post_states` have the same length `N`.
|
||||
3. Program cannot update an account's nonce. For all `i in 0..N`, `pre_states[i].account.nonce == post_states[i].account.nonce`.
|
||||
4. Program cannot change the program owner of an account. For all `i in 0..N`, `pre_states[i].account.program_owner == post_states[i].account.program_owner`.
|
||||
5. Program can only decrease the native token balance for accounts that the program owns. For all `i in 0..N`, if `post_states[i].account.balance < pre_states[i].account.balance`, then `pre_states[i].account.program_owner == executing_program_id`.
|
||||
@ -595,10 +608,11 @@ impl AccountPostState {
|
||||
|
||||
After execution, the runtime processes each post-state's optional `claim`:
|
||||
|
||||
- `Claim::Authorized` — sets `program_owner = executing_program_id`, but only if the account currently has `DEFAULT_PROGRAM_ID`. The authorization precondition depends on the account's kind:
|
||||
- **Public account or private PDA:** the account must be authorized — i.e. its `is_authorized` flag must be `true`. Authorization comes from a signature (public) or from a caller's `pda_seeds` matching the PDA derivation under the appropriate npk (private PDA).
|
||||
- **Standalone private account** (the `Regular` kinds — `PrivateAuthorizedInit`, `PrivateAuthorizedUpdate`, `PrivateUnauthorized`): the privacy circuit **does not enforce** the `is_authorized` precondition for `Claim::Authorized`. This is intentional: any party producing a valid update for a standalone private account already needs the corresponding `nsk` (to compute the update nullifier), so the authorization is implicit in the proof. The claim is therefore allowed unconditionally for these kinds, even when `pre_state.is_authorized == false`.
|
||||
- `Claim::Pda(seed)` — sets `program_owner = executing_program_id` (when currently default) after verifying the account's ID matches the PDA derivation. For public accounts this is `AccountId::for_public_pda(executing_program_id, seed)`; for private PDAs it is `AccountId::for_private_pda(executing_program_id, seed, npk, identifier)` using the npk supplied for that pre-state. `Claim::Pda` is meaningless on a standalone private account and is not produced by well-behaved programs there.
|
||||
- `Claim::Authorized` — the standard claim path: sets `program_owner = executing_program_id` (when currently default) for all account kinds except self-owned PDAs. Whether `is_authorized` is required depends on the account kind:
|
||||
- **Plain public accounts:** `is_authorized` must be `true`, set when the transaction signer included the account in the authorized set.
|
||||
- **Public and private PDAs:** `is_authorized` must be `true`, set when the caller included the matching seed in `ChainedCall.pda_seeds`.
|
||||
- **Standalone private accounts** (`PrivateAuthorizedInit`, `PrivateAuthorizedUpdate`, `PrivateUnauthorized`): `is_authorized` is not enforced. For the authorized variants, possession of the `nsk` is already implicit proof of ownership; for `PrivateUnauthorized` the pre-state must be `Account::default()` (a fresh account), so there is no prior owner to protect. The claim is therefore allowed unconditionally for all Regular kinds.
|
||||
- `Claim::Pda(seed)` — sets `program_owner = executing_program_id` (when currently default) by proving the account's ID is structurally derived from the executing program's own ID and the given seed, with no user authorization required. Unlike `Claim::Authorized`, the claim is not backed by a signature or nullifier key proof; instead, the program demonstrates ownership by construction: if the address was computed from `(self_program_id, seed)`, then no other program could have produced that same address. The derivation formula depends on the account kind: for public accounts it is `AccountId::for_public_pda(executing_program_id, seed)`; for private PDAs it is `AccountId::for_private_pda(executing_program_id, seed, npk, identifier)` using the npk supplied for that pre-state, making the claim user-specific. `Claim::Pda` is not applicable to standalone private accounts.
|
||||
|
||||
### Program-derived account IDs (PDAs)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user