From 9b5b43ecdf9cbc56bc2eab827a234dfb3b0e3fdd Mon Sep 17 00:00:00 2001 From: Milosz Muszynski Date: Thu, 28 Nov 2024 15:11:01 +0100 Subject: [PATCH] piecrust: added test for ICCs not rolled back when panic --- contracts/crossover/src/lib.rs | 29 +++++++++++++++++++++++++ piecrust/tests/crossover.rs | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/contracts/crossover/src/lib.rs b/contracts/crossover/src/lib.rs index a10f74ef..9729e2e7 100644 --- a/contracts/crossover/src/lib.rs +++ b/contracts/crossover/src/lib.rs @@ -67,6 +67,27 @@ impl Crossover { self.set_crossover(value_to_set); } + // Chain of ICC is not being rolled back when a callee panics and its panic + // is not propagated up the call chain. + pub fn check_iccs_dont_rollback( + &mut self, + contract: ContractId, + value_to_set: i32, + ) { + self.set_crossover(value_to_set); + + const ANY_VALUE_1: i32 = 5; + const ANY_VALUE_2: i32 = 6; + + uplink::debug!("calling panicking contract {contract:?}"); + uplink::call::<_, ()>( + contract, + "set_back_and_panic", + &(ANY_VALUE_1, ANY_VALUE_2), + ) + .expect_err("should give an error on a panic"); + } + // Sets the contract's value and then calls its caller's [`set_crossover`] // call to set their value. The caller is assumed to be another crossover // contract. @@ -125,6 +146,14 @@ unsafe fn check_consistent_state_on_errors(arg_len: u32) -> u32 { }) } +/// Expose `Crossover::check_iccs_dont_rollback()` to the host +#[no_mangle] +unsafe fn check_iccs_dont_rollback(arg_len: u32) -> u32 { + uplink::wrap_call(arg_len, |(contract, s)| { + STATE.check_iccs_dont_rollback(contract, s) + }) +} + /// Expose `Crossover::set_back_and_panic()` to the host #[no_mangle] unsafe fn set_back_and_panic(arg_len: u32) -> u32 { diff --git a/piecrust/tests/crossover.rs b/piecrust/tests/crossover.rs index 97a947c1..ab475886 100644 --- a/piecrust/tests/crossover.rs +++ b/piecrust/tests/crossover.rs @@ -70,3 +70,42 @@ fn crossover() -> Result<(), Error> { Ok(()) } + +#[test] +fn iccs_dont_rollback() -> Result<(), Error> { + let vm = VM::ephemeral()?; + + let mut session = vm.session(SessionData::builder())?; + + session.deploy( + contract_bytecode!("crossover"), + ContractData::builder() + .owner(OWNER) + .contract_id(CROSSOVER_ONE), + LIMIT, + )?; + session.deploy( + contract_bytecode!("crossover"), + ContractData::builder() + .owner(OWNER) + .contract_id(CROSSOVER_TWO), + LIMIT, + )?; + // These value should not be set to `INITIAL_VALUE` in the contract. + const CROSSOVER_TO_SET: i32 = 42; + + session.call::<_, ()>( + CROSSOVER_ONE, + "check_iccs_dont_rollback", + &(CROSSOVER_TWO, CROSSOVER_TO_SET), + LIMIT, + )?; + + assert_eq!( + session.call::<_, i32>(CROSSOVER_ONE, "crossover", &(), LIMIT)?.data, + CROSSOVER_TO_SET, + "The crossover should still be set even though the other contract panicked" + ); + + Ok(()) +}