diff --git a/lee/state_machine/src/error.rs b/lee/state_machine/src/error.rs index 2d0221bc..94e66e8b 100644 --- a/lee/state_machine/src/error.rs +++ b/lee/state_machine/src/error.rs @@ -132,7 +132,9 @@ pub enum InvalidProgramBehaviorError { #[error("Called program {program_id:?} which is not listed in dependencies")] UndeclaredProgramDependency { program_id: ProgramId }, - #[error("Account {account_id} was declared in the transaction but is missing from the program output")] + #[error( + "Account {account_id} was declared in the transaction but is missing from the program output" + )] DeclaredAccountMissingFromOutput { account_id: AccountId }, } diff --git a/lee/state_machine/src/validated_state_diff/mod.rs b/lee/state_machine/src/validated_state_diff/mod.rs index a740e1ef..e4a92d63 100644 --- a/lee/state_machine/src/validated_state_diff/mod.rs +++ b/lee/state_machine/src/validated_state_diff/mod.rs @@ -294,11 +294,7 @@ impl ValidatedStateDiff { } // Every account the caller declared as part of the transaction must appear in the final - // diff. A well-behaved program's output always echoes back every account it was handed - // (unchanged if it only reads it) — this catches a program (or a macro-generated - // dispatcher wrapping one) that silently drops an account from its own output instead. - // Chained calls may still introduce accounts beyond this original list; this only checks - // the reverse direction. + // diff. for account_id in &message.account_ids { ensure!( state_diff.contains_key(account_id), diff --git a/lee/state_machine/test_methods/guest/src/bin/dropped_account.rs b/lee/state_machine/test_methods/guest/src/bin/dropped_account.rs index e79dbc3b..348eefa8 100644 --- a/lee/state_machine/test_methods/guest/src/bin/dropped_account.rs +++ b/lee/state_machine/test_methods/guest/src/bin/dropped_account.rs @@ -5,12 +5,8 @@ type Instruction = (); /// Silently drops the second account entirely from its own output: given two `pre_states`, it /// returns only one `(pre, post)` pair, echoing the first account back unchanged. /// -/// Unlike `missing_output` (whose own `pre_states.len() != post_states.len()`, tripping -/// `validate_execution`'s internal length check directly), this program's own output is -/// internally consistent — one `pre_state` and one matching `post_state` — it just reports fewer -/// accounts than it was handed. This mirrors a well-behaved-looking dispatcher that filters an -/// account out of both sides of its output together (e.g. a stale-signer-nonce workaround that's -/// too broad), rather than an obviously malformed program. +/// Differs from `missing_output` because the `pre_state` and `post_states` lengths match. We +/// simply drop the account from both before returning them as part of the program's output. fn main() { let ( ProgramInput {