diff --git a/integration_tests/tests/cross_zone_state_machine.rs b/integration_tests/tests/cross_zone_state_machine.rs index 30725c680..c3ca941ab 100644 --- a/integration_tests/tests/cross_zone_state_machine.rs +++ b/integration_tests/tests/cross_zone_state_machine.rs @@ -590,7 +590,7 @@ fn a_second_emit_at_the_same_slot_is_rejected() { let first = lock_tx(&holder_key, holder_id, zone_b, ordinal, 0); let diff = ValidatedStateDiff::from_public_transaction(&first, &state, 1, 0) .expect("the first lock executes"); - state.apply_state_diff(diff); + drop(state.apply_state_diff(diff)); // Same slot, fresh nonce, so the only thing that can reject it is the slot // already holding a record. Matched on the guest's own message rather than @@ -646,7 +646,7 @@ fn two_emitters_share_an_ordinal_without_colliding() { let lock = lock_tx(&holder_key, holder_id, zone_b, ordinal, 0); let diff = ValidatedStateDiff::from_public_transaction(&lock, &state, 1, 0) .expect("the lock executes"); - state.apply_state_diff(diff); + drop(state.apply_state_diff(diff)); let send = send_tx( vec![sender_config_account_id(sender_id), send_slot], @@ -936,7 +936,7 @@ fn the_bridge_pins_are_written_once_and_replayable() { 0, ) .expect("the first init claims the config PDA"); - state.apply_state_diff(diff); + drop(state.apply_state_diff(diff)); assert_eq!( bridge_lock_core::read_config(&state.get_account_by_id(config_id).data.into_inner()), Some((outbox_id, wrapped_token_id)), @@ -1042,7 +1042,7 @@ fn the_outbox_pin_is_written_once_and_replayable() { let first = init(outbox_id); let diff = ValidatedStateDiff::from_public_transaction(&first, &state, 1, 0) .expect("the first init claims the config PDA"); - state.apply_state_diff(diff); + drop(state.apply_state_diff(diff)); assert_eq!( read_outbox(&state.get_account_by_id(config_id).data.into_inner()), Some(outbox_id), @@ -1180,7 +1180,7 @@ fn the_token_authority_path_holds() { 0, ) .expect("the configured authority changes sources"); - state.apply_state_diff(diff); + drop(state.apply_state_diff(diff)); let cfg = wrapped_token_core::WrappedTokenConfig::from_bytes( &state.get_account_by_id(config_id).data.into_inner(), ) @@ -1200,7 +1200,7 @@ fn the_token_authority_path_holds() { 0, ) .expect("the authority acts again"); - state.apply_state_diff(second); + drop(state.apply_state_diff(second)); let updated_cfg = wrapped_token_core::WrappedTokenConfig::from_bytes( &state.get_account_by_id(config_id).data.into_inner(), ) @@ -1220,7 +1220,7 @@ fn the_token_authority_path_holds() { let renounced = ValidatedStateDiff::from_public_transaction(&renounce(authority, &key, 2), &state, 3, 0) .expect("the authority renounces itself"); - state.apply_state_diff(renounced); + drop(state.apply_state_diff(renounced)); let renounced_cfg = wrapped_token_core::WrappedTokenConfig::from_bytes( &state.get_account_by_id(config_id).data.into_inner(), ) @@ -1431,7 +1431,7 @@ fn the_receiver_authority_path_holds() { let diff = ValidatedStateDiff::from_public_transaction(&update(authority, &key, 0), &state, 1, 0) .expect("the configured authority changes sources"); - state.apply_state_diff(diff); + drop(state.apply_state_diff(diff)); let cfg = ping_core::ReceiverConfig::from_bytes( &state.get_account_by_id(config_id).data.into_inner(), ) @@ -1442,7 +1442,7 @@ fn the_receiver_authority_path_holds() { let renounce_diff = ValidatedStateDiff::from_public_transaction(&renounce(authority, &key, 1), &state, 2, 0) .expect("the authority renounces itself"); - state.apply_state_diff(renounce_diff); + drop(state.apply_state_diff(renounce_diff)); let renounced_cfg = ping_core::ReceiverConfig::from_bytes( &state.get_account_by_id(config_id).data.into_inner(), ) @@ -1555,7 +1555,7 @@ fn the_governance_path_holds() { 0, ) .expect("the governance path changes sources"); - state.apply_state_diff(first); + drop(state.apply_state_diff(first)); let cfg = wrapped_token_core::WrappedTokenConfig::from_bytes( &state.get_account_by_id(config_id).data.into_inner(), @@ -1570,7 +1570,7 @@ fn the_governance_path_holds() { let second = ValidatedStateDiff::from_public_transaction(&update(vec![]), &state, 2, 0) .expect("the governance path acts again"); - state.apply_state_diff(second); + drop(state.apply_state_diff(second)); let cleared_cfg = wrapped_token_core::WrappedTokenConfig::from_bytes( &state.get_account_by_id(config_id).data.into_inner(), ) @@ -1583,7 +1583,7 @@ fn the_governance_path_holds() { let renounced = ValidatedStateDiff::from_public_transaction(&renounce(), &state, 3, 0) .expect("the governance path renounces"); - state.apply_state_diff(renounced); + drop(state.apply_state_diff(renounced)); let renounced_cfg = wrapped_token_core::WrappedTokenConfig::from_bytes( &state.get_account_by_id(config_id).data.into_inner(), ) @@ -1723,7 +1723,7 @@ fn the_receiver_governance_path_holds() { let diff = ValidatedStateDiff::from_public_transaction(&tx, &state, 1, 0) .expect("the receiver governance path changes sources"); - state.apply_state_diff(diff); + drop(state.apply_state_diff(diff)); let cfg = ping_core::ReceiverConfig::from_bytes( &state.get_account_by_id(config_id).data.into_inner(), ) @@ -1767,7 +1767,7 @@ fn a_shared_authority_survives_the_first_claim() { ); let first = ValidatedStateDiff::from_public_transaction(&token_update, &state, 1, 0) .expect("the token claims the shared authority"); - state.apply_state_diff(first); + drop(state.apply_state_diff(first)); assert_eq!( state.get_account_by_id(authority).program_owner, wrapped_token_id.into(), @@ -1786,7 +1786,7 @@ fn a_shared_authority_survives_the_first_claim() { ); let second = ValidatedStateDiff::from_public_transaction(&receiver_update, &state, 2, 0) .expect("the other target still acts on the token-owned authority"); - state.apply_state_diff(second); + drop(state.apply_state_diff(second)); let receiver_cfg = ping_core::ReceiverConfig::from_bytes( &state .get_account_by_id(receiver_config_id) @@ -1814,7 +1814,7 @@ fn a_shared_authority_survives_the_first_claim() { ); let third = ValidatedStateDiff::from_public_transaction(&receiver_renounce, &state, 3, 0) .expect("the other target renounces on the token-owned authority"); - state.apply_state_diff(third); + drop(state.apply_state_diff(third)); let renounced_cfg = ping_core::ReceiverConfig::from_bytes( &state .get_account_by_id(receiver_config_id) diff --git a/lee/state_machine/core/src/program/mod.rs b/lee/state_machine/core/src/program/mod.rs index 1ff881bef..36ab94276 100644 --- a/lee/state_machine/core/src/program/mod.rs +++ b/lee/state_machine/core/src/program/mod.rs @@ -468,6 +468,14 @@ impl From for ValidityWindow { #[error("Invalid window")] pub struct InvalidWindow; +#[derive(Serialize, Deserialize, Clone, BorshSerialize, BorshDeserialize)] +#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))] +pub struct ProgramEvent { + pub selector: [u8; 8], + /// The arbitrary event-data emitted in the program output. + pub data: Vec, +} + #[derive(Serialize, Deserialize, Clone)] #[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))] #[must_use = "ProgramOutput does nothing unless written"] @@ -491,7 +499,7 @@ pub struct ProgramOutput { pub timestamp_validity_window: TimestampValidityWindow, /// A vector of event data. Dropped for private transaction for function /// privacy. - pub events: Vec>, + pub events: Vec, } impl ProgramOutput { @@ -524,7 +532,7 @@ impl ProgramOutput { self } - pub fn with_events(mut self, events: Vec>) -> Self { + pub fn with_events(mut self, events: Vec) -> Self { self.events = events; self } @@ -582,11 +590,10 @@ impl ProgramOutput { /// A struct holding an event-output of a program. #[cfg(feature = "host")] #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] -pub struct Event { +pub struct TransactionEvent { /// Which program emitted the event. pub program_id: ProgramId, - /// The arbitrary event-data emitted in the program output. - pub data: Vec, + pub event: ProgramEvent, } /// Representation of a number as `lo + hi * 2^128`. diff --git a/lee/state_machine/src/state/mod.rs b/lee/state_machine/src/state/mod.rs index 58beecc55..082c8da2a 100644 --- a/lee/state_machine/src/state/mod.rs +++ b/lee/state_machine/src/state/mod.rs @@ -5,7 +5,7 @@ use lee_core::{ BlockId, Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, MembershipProof, Nullifier, Timestamp, account::{Account, AccountId, Data}, - program::{Event, PROGRAM_STORAGE_OWNER, ProgramId}, + program::{PROGRAM_STORAGE_OWNER, ProgramId, TransactionEvent}, }; use crate::{ @@ -204,7 +204,8 @@ impl V03State { self.public_state.insert(account_id, account); } - pub fn apply_state_diff(&mut self, diff: ValidatedStateDiff) -> Vec { + #[must_use] + pub fn apply_state_diff(&mut self, diff: ValidatedStateDiff) -> Vec { let StateDiff { signer_account_ids, public_diff, @@ -238,7 +239,7 @@ impl V03State { tx: &PublicTransaction, block_id: BlockId, timestamp: Timestamp, - ) -> Result, LeeError> { + ) -> Result, LeeError> { let diff = ValidatedStateDiff::from_public_transaction(tx, self, block_id, timestamp)?; Ok(self.apply_state_diff(diff)) } @@ -251,7 +252,7 @@ impl V03State { ) -> Result<(), LeeError> { let diff = ValidatedStateDiff::from_privacy_preserving_transaction(tx, self, block_id, timestamp)?; - self.apply_state_diff(diff); + drop(self.apply_state_diff(diff)); Ok(()) } @@ -260,7 +261,7 @@ impl V03State { tx: &ProgramDeploymentTransaction, ) -> Result<(), LeeError> { let diff = ValidatedStateDiff::from_program_deployment_transaction(tx, self)?; - self.apply_state_diff(diff); + drop(self.apply_state_diff(diff)); Ok(()) } diff --git a/lee/state_machine/src/state/tests/events.rs b/lee/state_machine/src/state/tests/events.rs index e82d0ced0..8674738f8 100644 --- a/lee/state_machine/src/state/tests/events.rs +++ b/lee/state_machine/src/state/tests/events.rs @@ -12,8 +12,18 @@ fn program_transaction( PublicTransaction::new(message, witness_set) } -fn payloads(events: &[lee_core::program::Event]) -> Vec> { - events.iter().map(|event| event.data.clone()).collect() +fn payloads(events: &[TransactionEvent]) -> Vec> { + events + .iter() + .map(|event| event.event.data.clone()) + .collect() +} + +fn emitted(n: u8) -> ProgramEvent { + ProgramEvent { + selector: [n; 8], + data: vec![n; 4], + } } #[test] @@ -26,7 +36,7 @@ fn emitted_events_are_returned_in_order_and_attributed_to_the_emitter() { emitter_id, account_id, EmitterInstruction { - events: vec![vec![0; 4], vec![1; 4]], + events: vec![emitted(0), emitted(1)], chain: vec![], }, ); @@ -34,6 +44,13 @@ fn emitted_events_are_returned_in_order_and_attributed_to_the_emitter() { let events = state.transition_from_public_transaction(&tx, 1, 0).unwrap(); assert_eq!(payloads(&events), vec![vec![0; 4], vec![1; 4]]); + assert_eq!( + events + .iter() + .map(|event| event.event.selector) + .collect::>(), + vec![[0; 8], [1; 8]] + ); assert!(events.iter().all(|event| event.program_id == emitter_id)); } @@ -44,7 +61,7 @@ fn parent_events_precede_chained_callee_events() { let emitter_id = crate::test_methods::event_emitter().id(); let callee_instruction_data = Program::serialize_instruction(EmitterInstruction { - events: vec![vec![1; 4], vec![2; 4]], + events: vec![emitted(1), emitted(2)], chain: vec![], }) .unwrap(); @@ -53,7 +70,7 @@ fn parent_events_precede_chained_callee_events() { emitter_id, account_id, EmitterInstruction { - events: vec![vec![0; 4]], + events: vec![emitted(0)], chain: vec![(emitter_id, callee_instruction_data)], }, ); @@ -74,17 +91,17 @@ fn chained_events_follow_depth_first_pre_order() { let emitter_id = crate::test_methods::event_emitter().id(); let grandchild = Program::serialize_instruction(EmitterInstruction { - events: vec![vec![2; 4]], + events: vec![emitted(2)], chain: vec![], }) .unwrap(); let first_callee = Program::serialize_instruction(EmitterInstruction { - events: vec![vec![1; 4]], + events: vec![emitted(1)], chain: vec![(emitter_id, grandchild)], }) .unwrap(); let second_callee = Program::serialize_instruction(EmitterInstruction { - events: vec![vec![3; 4]], + events: vec![emitted(3)], chain: vec![], }) .unwrap(); @@ -93,7 +110,7 @@ fn chained_events_follow_depth_first_pre_order() { emitter_id, account_id, EmitterInstruction { - events: vec![vec![0; 4]], + events: vec![emitted(0)], chain: vec![(emitter_id, first_callee), (emitter_id, second_callee)], }, ); @@ -139,7 +156,7 @@ fn chained_callee_events_are_attributed_to_the_callee_not_the_caller() { // three sibling chained calls, so the only emitting program is neither the top-level // program nor its caller. let callback_instruction_data = Program::serialize_instruction(EmitterInstruction { - events: vec![vec![0; 4]], + events: vec![emitted(0)], chain: vec![], }) .unwrap(); diff --git a/lee/state_machine/src/state/tests/mod.rs b/lee/state_machine/src/state/tests/mod.rs index fb99474b6..9f4929219 100644 --- a/lee/state_machine/src/state/tests/mod.rs +++ b/lee/state_machine/src/state/tests/mod.rs @@ -14,7 +14,8 @@ use lee_core::{ encryption::ViewingPublicKey, program::{ BlockValidityWindow, ExecutionValidationError, InstructionData, MAX_NUMBER_CHAINED_CALLS, - PdaSeed, ProgramId, TimestampValidityWindow, WrappedBalanceSum, + PdaSeed, ProgramEvent, ProgramId, TimestampValidityWindow, TransactionEvent, + WrappedBalanceSum, }, }; @@ -183,7 +184,7 @@ enum FlashSwapInstruction { #[derive(serde::Serialize, serde::Deserialize)] struct EmitterInstruction { - events: Vec>, + events: Vec, chain: Vec<(ProgramId, InstructionData)>, } diff --git a/lee/state_machine/src/validated_state_diff/mod.rs b/lee/state_machine/src/validated_state_diff/mod.rs index aaafa6622..2582cc3d4 100644 --- a/lee/state_machine/src/validated_state_diff/mod.rs +++ b/lee/state_machine/src/validated_state_diff/mod.rs @@ -8,7 +8,7 @@ use lee_core::{ BlockId, Commitment, Nullifier, PrivacyPreservingCircuitOutput, PublicAction, Timestamp, account::{Account, AccountId, AccountWithMetadata}, program::{ - CallerData, ChainedCall, Claim, DEFAULT_PROGRAM_OWNER, Event, + CallerData, ChainedCall, Claim, DEFAULT_PROGRAM_OWNER, TransactionEvent, compute_public_authorized_pdas, validate_execution, }, }; @@ -32,7 +32,7 @@ pub struct StateDiff { pub new_commitments: Vec, pub new_nullifiers: Vec, pub program: Option, - pub events: Vec, + pub events: Vec, } /// The validated output of executing or verifying a transaction, ready to be applied to the state. @@ -90,7 +90,7 @@ impl ValidatedStateDiff { .collect(); let mut state_diff: HashMap = HashMap::new(); - let mut events: Vec = Vec::new(); + let mut events: Vec = Vec::new(); let initial_call = ChainedCall { program_id: message.program_id, @@ -264,10 +264,15 @@ impl ValidatedStateDiff { // Write all the output event data into a proper event struct, // marking its emitter program. - events.extend(program_output.events.into_iter().map(|data| Event { - program_id: chained_call.program_id, - data, - })); + events.extend( + program_output + .events + .into_iter() + .map(|event| TransactionEvent { + program_id: chained_call.program_id, + event, + }), + ); // Source from `program_output.pre_states`, not `chained_call.pre_states`: // the loop above already gates program_output's `is_authorized` via the diff --git a/lee/state_machine/test_methods/guest/src/bin/event_emitter.rs b/lee/state_machine/test_methods/guest/src/bin/event_emitter.rs index 199fc9dc8..6191bc8e3 100644 --- a/lee/state_machine/test_methods/guest/src/bin/event_emitter.rs +++ b/lee/state_machine/test_methods/guest/src/bin/event_emitter.rs @@ -1,12 +1,12 @@ use lee_core::program::{ - AccountPostState, ChainedCall, InstructionData, ProgramId, ProgramInput, ProgramOutput, - read_lee_inputs, + AccountPostState, ChainedCall, InstructionData, ProgramEvent, ProgramId, ProgramInput, + ProgramOutput, read_lee_inputs, }; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] pub struct EmitterInstruction { - pub events: Vec>, + pub events: Vec, pub chain: Vec<(ProgramId, InstructionData)>, } diff --git a/lez/common/src/transaction.rs b/lez/common/src/transaction.rs index 9970bf7d7..5ac9f5c93 100644 --- a/lez/common/src/transaction.rs +++ b/lez/common/src/transaction.rs @@ -133,7 +133,7 @@ impl LeeTransaction { let diff = self .validate_on_state(state, block_id, timestamp) .inspect_err(|err| warn!("Error at transition {err:#?}"))?; - state.apply_state_diff(diff); + drop(state.apply_state_diff(diff)); Ok(self) } @@ -151,7 +151,7 @@ impl LeeTransaction { let diff = self .compute_state_diff(state, block_id, timestamp) .inspect_err(|err| warn!("Error at transition {err:#?}"))?; - state.apply_state_diff(diff); + drop(state.apply_state_diff(diff)); Ok(self) } diff --git a/lez/sequencer/core/src/lib.rs b/lez/sequencer/core/src/lib.rs index 7b7b7db85..0f9316508 100644 --- a/lez/sequencer/core/src/lib.rs +++ b/lez/sequencer/core/src/lib.rs @@ -922,7 +922,7 @@ impl SequencerCore { withdrawals.push(withdraw_data); } - state.apply_state_diff(validated_diff); + drop(state.apply_state_diff(validated_diff)); } TransactionOrigin::Sequencer => { let LeeTransaction::Public(public_tx) = tx else {