diff --git a/hs/spec_compliance/src/IC/Test/Spec.hs b/hs/spec_compliance/src/IC/Test/Spec.hs index 32a6e3df1e6..807ef206db7 100644 --- a/hs/spec_compliance/src/IC/Test/Spec.hs +++ b/hs/spec_compliance/src/IC/Test/Spec.hs @@ -1022,6 +1022,7 @@ icTests my_sub other_sub = t "time" star $ ignore getTime, t "performance_counter" star $ ignore $ performanceCounter (int 0), t "is_controller" star $ ignore $ isController "", + t "in_replicated_execution" star $ ignore inReplicatedExecution, t "canister_version" star $ ignore $ canisterVersion, t "global_timer_set" "I G U Ry Rt C T" $ ignore $ apiGlobalTimerSet (int64 0), t "debug_print" star $ debugPrint "hello", diff --git a/hs/spec_compliance/src/IC/Test/Universal.hs b/hs/spec_compliance/src/IC/Test/Universal.hs index 13f5156d4af..b62ec9c3293 100644 --- a/hs/spec_compliance/src/IC/Test/Universal.hs +++ b/hs/spec_compliance/src/IC/Test/Universal.hs @@ -279,6 +279,9 @@ oneWayCallNew = op 76 isController :: Exp 'B -> Exp 'I isController = op 77 +inReplicatedExecution :: Exp 'I +inReplicatedExecution = op 81 + -- Some convenience combinators -- This allows us to write byte expressions as plain string literals diff --git a/rs/embedders/src/wasm_utils/validation.rs b/rs/embedders/src/wasm_utils/validation.rs index 4e91b2dbc64..f59098214d6 100644 --- a/rs/embedders/src/wasm_utils/validation.rs +++ b/rs/embedders/src/wasm_utils/validation.rs @@ -580,6 +580,16 @@ fn get_valid_system_apis() -> HashMap }, )], ), + ( + "in_replicated_execution", + vec![( + API_VERSION_IC0, + FunctionSignature { + param_types: vec![], + return_type: vec![ValType::I32], + }, + )], + ), ( "cycles_burn128", vec![( diff --git a/rs/embedders/src/wasmtime_embedder/system_api.rs b/rs/embedders/src/wasmtime_embedder/system_api.rs index 2c06d1f7301..44fe546dc62 100644 --- a/rs/embedders/src/wasmtime_embedder/system_api.rs +++ b/rs/embedders/src/wasmtime_embedder/system_api.rs @@ -1114,6 +1114,18 @@ pub(crate) fn syscalls( }) .unwrap(); + linker + .func_wrap("ic0", "in_replicated_execution", { + move |mut caller: Caller<'_, StoreData>| { + charge_for_cpu( + &mut caller, + overhead!(IN_REPLICATED_EXECUTION, metering_type), + )?; + with_system_api(&mut caller, |s| s.ic0_in_replicated_execution()) + } + }) + .unwrap(); + linker .func_wrap("ic0", "data_certificate_copy", { move |mut caller: Caller<'_, StoreData>, dst: u32, offset: u32, size: u32| { diff --git a/rs/embedders/src/wasmtime_embedder/system_api_complexity.rs b/rs/embedders/src/wasmtime_embedder/system_api_complexity.rs index f562f84efb4..5a6e9a0d945 100644 --- a/rs/embedders/src/wasmtime_embedder/system_api_complexity.rs +++ b/rs/embedders/src/wasmtime_embedder/system_api_complexity.rs @@ -41,6 +41,7 @@ pub mod overhead { pub const DEBUG_PRINT: NumInstructions = NumInstructions::new(100); pub const GLOBAL_TIMER_SET: NumInstructions = NumInstructions::new(0); pub const IS_CONTROLLER: NumInstructions = NumInstructions::new(1_000); + pub const IN_REPLICATED_EXECUTION: NumInstructions = NumInstructions::new(0); pub const MSG_ARG_DATA_COPY: NumInstructions = NumInstructions::new(20); pub const MSG_ARG_DATA_SIZE: NumInstructions = NumInstructions::new(0); pub const MSG_CALLER_COPY: NumInstructions = NumInstructions::new(0); @@ -96,6 +97,7 @@ pub mod overhead { pub const DEBUG_PRINT: NumInstructions = NumInstructions::new(100); pub const GLOBAL_TIMER_SET: NumInstructions = NumInstructions::new(500); pub const IS_CONTROLLER: NumInstructions = NumInstructions::new(1_000); + pub const IN_REPLICATED_EXECUTION: NumInstructions = NumInstructions::new(500); pub const MSG_ARG_DATA_COPY: NumInstructions = NumInstructions::new(500); pub const MSG_ARG_DATA_SIZE: NumInstructions = NumInstructions::new(500); pub const MSG_CALLER_COPY: NumInstructions = NumInstructions::new(500); diff --git a/rs/execution_environment/benches/system_api/execute_update.rs b/rs/execution_environment/benches/system_api/execute_update.rs index e805bfd9d4f..5b3228a4103 100644 --- a/rs/execution_environment/benches/system_api/execute_update.rs +++ b/rs/execution_environment/benches/system_api/execute_update.rs @@ -307,6 +307,11 @@ pub fn execute_update_bench(c: &mut Criterion) { Module::Test.from_ic0("is_controller", Params2(0, 29), Result::I32), 1048000006, ), + common::Benchmark( + "ic0_in_replicated_execution()".into(), + Module::Test.from_ic0("in_replicated_execution", NoParams, Result::I32), + 517000006, + ), common::Benchmark( "ic0_cycles_burn128()".into(), Module::Test.from_ic0("cycles_burn128", Params3(1_i64, 2_i64, 3_i32), Result::No), diff --git a/rs/execution_environment/src/hypervisor/tests.rs b/rs/execution_environment/src/hypervisor/tests.rs index 88569bea5b5..bc7c46ca297 100644 --- a/rs/execution_environment/src/hypervisor/tests.rs +++ b/rs/execution_environment/src/hypervisor/tests.rs @@ -4430,7 +4430,8 @@ fn cycles_are_refunded_if_callee_is_reinstalled() { WasmResult::Reject(reject_message) => reject_message, }; assert!( - reject_message.contains("trapped explicitly: panicked at 'get_callback: 1 out of bounds'"), + reject_message.contains("trapped explicitly: panicked at") + && reject_message.contains("get_callback: 1 out of bounds"), "Unexpected error message: {}", reject_message ); diff --git a/rs/execution_environment/tests/in_replicated_execution.rs b/rs/execution_environment/tests/in_replicated_execution.rs new file mode 100644 index 00000000000..ced403b3e2c --- /dev/null +++ b/rs/execution_environment/tests/in_replicated_execution.rs @@ -0,0 +1,92 @@ +use ic_ic00_types::CanisterInstallMode; +use ic_registry_subnet_type::SubnetType; +use ic_state_machine_tests::{StateMachine, StateMachineBuilder, UserError, WasmResult}; +use ic_test_utilities::universal_canister::{wasm, UNIVERSAL_CANISTER_WASM}; +use ic_types::{CanisterId, Cycles}; + +const REPLICATED_EXECUTION: [u8; 4] = [1, 0, 0, 0]; +const NON_REPLICATED_EXECUTION: [u8; 4] = [0, 0, 0, 0]; + +fn setup() -> (StateMachine, CanisterId) { + let env = StateMachineBuilder::new() + .with_subnet_type(SubnetType::Application) + .with_checkpoints_enabled(false) + .build(); + let canister_id = + env.create_canister_with_cycles(None, Cycles::from(100_000_000_000_u128), None); + env.install_wasm_in_mode( + canister_id, + CanisterInstallMode::Install, + UNIVERSAL_CANISTER_WASM.to_vec(), + vec![], + ) + .unwrap(); + + (env, canister_id) +} + +pub fn expect_reply(result: Result) -> Vec { + match result { + Ok(wasm_result) => match wasm_result { + WasmResult::Reply(bytes) => bytes, + WasmResult::Reject(msg) => panic!("Unexpected reject: {}", msg), + }, + Err(err) => panic!("Unexpected error: {}", err), + } +} + +#[test] +fn test_in_replicated_execution_for_update_returns_1() { + // Arrange. + let (env, canister_id) = setup(); + // Act. + let result = env.execute_ingress( + canister_id, + "update", + wasm().in_replicated_execution().reply_int().build(), + ); + // Assert. + assert_eq!(expect_reply(result), REPLICATED_EXECUTION); +} + +#[test] +fn test_in_replicated_execution_for_replicated_query_returns_1() { + // Arrange. + let (env, canister_id) = setup(); + // Act. + let result = env.execute_ingress( + canister_id, + "query", + wasm().in_replicated_execution().reply_int().build(), + ); + // Assert. + assert_eq!(expect_reply(result), REPLICATED_EXECUTION); +} + +#[test] +fn test_in_replicated_execution_for_query_returns_0() { + // Arrange. + let (env, canister_id) = setup(); + // Act. + let result = env.query( + canister_id, + "query", + wasm().in_replicated_execution().reply_int().build(), + ); + // Assert. + assert_eq!(expect_reply(result), NON_REPLICATED_EXECUTION); +} + +#[test] +fn test_in_replicated_execution_for_composite_query_returns_0() { + // Arrange. + let (env, canister_id) = setup(); + // Act. + let result = env.query( + canister_id, + "composite_query", + wasm().in_replicated_execution().reply_int().build(), + ); + // Assert. + assert_eq!(expect_reply(result), NON_REPLICATED_EXECUTION); +} diff --git a/rs/interfaces/src/execution_environment.rs b/rs/interfaces/src/execution_environment.rs index a6889e20d6d..f25117eb81d 100644 --- a/rs/interfaces/src/execution_environment.rs +++ b/rs/interfaces/src/execution_environment.rs @@ -927,6 +927,12 @@ pub trait SystemApi { /// This system call traps if src+size exceeds the size of the WebAssembly memory. fn ic0_is_controller(&self, src: u32, size: u32, heap: &[u8]) -> HypervisorResult; + /// If run in replicated execution (i.e. an update call or a certified + /// query), returns 1. + /// If run in non-replicated execution (i.e. query), + /// returns 0 if the data certificate is present, 1 otherwise. + fn ic0_in_replicated_execution(&self) -> HypervisorResult; + /// Burns the provided `amount` cycles. /// Removes cycles from the canister's balance. /// diff --git a/rs/rust_canisters/dfn_core/src/api.rs b/rs/rust_canisters/dfn_core/src/api.rs index 9a14107b658..e9d1c65d3f8 100644 --- a/rs/rust_canisters/dfn_core/src/api.rs +++ b/rs/rust_canisters/dfn_core/src/api.rs @@ -84,6 +84,7 @@ pub mod ic0 { pub fn canister_version() -> u64; pub fn mint_cycles(amount: u64) -> u64; pub fn is_controller(src: u32, size: u32) -> u32; + pub fn in_replicated_execution() -> u32; } } @@ -286,6 +287,10 @@ pub mod ic0 { pub unsafe fn is_controller(_src: u32, _size: u32) -> u32 { wrong_arch("is_controller") } + + pub unsafe fn in_replicated_execution() -> u32 { + wrong_arch("in_replicated_execution") + } } // Convenience wrappers around the DFINTY System API diff --git a/rs/system_api/src/lib.rs b/rs/system_api/src/lib.rs index fdc74f84bc4..368ba62a8f9 100644 --- a/rs/system_api/src/lib.rs +++ b/rs/system_api/src/lib.rs @@ -2882,6 +2882,31 @@ impl SystemApi for SystemApiImpl { result } + fn ic0_in_replicated_execution(&self) -> HypervisorResult { + let result = match &self.api_type { + ApiType::Start { .. } => Err(self.error_for("ic0_in_replicated_execution")), + ApiType::Init { .. } + | ApiType::ReplyCallback { .. } + | ApiType::RejectCallback { .. } + | ApiType::Cleanup { .. } + | ApiType::PreUpgrade { .. } + | ApiType::InspectMessage { .. } + | ApiType::Update { .. } + | ApiType::SystemTask { .. } => Ok(1), + ApiType::ReplicatedQuery { + data_certificate, .. + } + | ApiType::NonReplicatedQuery { + data_certificate, .. + } => match data_certificate { + None => Ok(1), + Some(_) => Ok(0), + }, + }; + trace_syscall!(self, ic0_in_replicated_execution, result); + result + } + fn ic0_cycles_burn128( &mut self, amount: Cycles, diff --git a/rs/system_api/tests/system_api.rs b/rs/system_api/tests/system_api.rs index 2ba2efd384b..33b40e0b41a 100644 --- a/rs/system_api/tests/system_api.rs +++ b/rs/system_api/tests/system_api.rs @@ -180,6 +180,7 @@ fn test_canister_init_support() { assert_api_supported(api.ic0_canister_status()); assert_api_not_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_supported(api.ic0_in_replicated_execution()); assert_api_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } @@ -237,6 +238,7 @@ fn test_canister_update_support() { assert_api_supported(api.ic0_canister_status()); assert_api_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_supported(api.ic0_in_replicated_execution()); assert_api_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } @@ -294,6 +296,7 @@ fn test_canister_replicated_query_support() { assert_api_supported(api.ic0_canister_status()); assert_api_not_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_supported(api.ic0_in_replicated_execution()); assert_api_not_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } @@ -351,6 +354,7 @@ fn test_canister_pure_query_support() { assert_api_supported(api.ic0_canister_status()); assert_api_not_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_supported(api.ic0_in_replicated_execution()); assert_api_not_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } @@ -418,6 +422,7 @@ fn test_canister_stateful_query_support() { assert_api_supported(api.ic0_canister_status()); assert_api_not_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_supported(api.ic0_in_replicated_execution()); assert_api_not_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } @@ -475,6 +480,7 @@ fn test_reply_api_support_on_nns() { assert_api_supported(api.ic0_canister_status()); assert_api_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_supported(api.ic0_in_replicated_execution()); assert_api_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } @@ -532,6 +538,7 @@ fn test_reply_api_support_non_nns() { assert_api_supported(api.ic0_canister_status()); assert_api_not_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_supported(api.ic0_in_replicated_execution()); assert_api_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } @@ -590,6 +597,7 @@ fn test_reject_api_support_on_nns() { assert_api_supported(api.ic0_canister_status()); assert_api_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_supported(api.ic0_in_replicated_execution()); assert_api_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } @@ -648,6 +656,7 @@ fn test_reject_api_support_non_nns() { assert_api_supported(api.ic0_canister_status()); assert_api_not_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_supported(api.ic0_in_replicated_execution()); assert_api_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } @@ -705,6 +714,7 @@ fn test_pre_upgrade_support() { assert_api_supported(api.ic0_canister_status()); assert_api_not_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_supported(api.ic0_in_replicated_execution()); assert_api_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } @@ -762,6 +772,7 @@ fn test_start_support() { assert_api_not_supported(api.ic0_canister_status()); assert_api_not_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_not_supported(api.ic0_in_replicated_execution()); assert_api_not_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } @@ -823,6 +834,7 @@ fn test_cleanup_support() { assert_api_supported(api.ic0_canister_status()); assert_api_not_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_supported(api.ic0_in_replicated_execution()); assert_api_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } @@ -885,6 +897,7 @@ fn test_inspect_message_support() { assert_api_supported(api.ic0_canister_status()); assert_api_not_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_supported(api.ic0_in_replicated_execution()); assert_api_not_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } @@ -943,6 +956,7 @@ fn test_canister_system_task_support() { assert_api_supported(api.ic0_canister_status()); assert_api_not_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_supported(api.ic0_in_replicated_execution()); assert_api_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } @@ -1000,6 +1014,7 @@ fn test_canister_system_task_support_nns() { assert_api_supported(api.ic0_canister_status()); assert_api_supported(api.ic0_mint_cycles(0)); assert_api_supported(api.ic0_is_controller(0, 0, &[])); + assert_api_supported(api.ic0_in_replicated_execution()); assert_api_supported(api.ic0_cycles_burn128(Cycles::zero(), 0, &mut [])); check_stable_apis_support(api); } diff --git a/rs/tests/execution/general_execution_test.rs b/rs/tests/execution/general_execution_test.rs index ef25c943113..08012f5640e 100644 --- a/rs/tests/execution/general_execution_test.rs +++ b/rs/tests/execution/general_execution_test.rs @@ -12,6 +12,7 @@ use ic_tests::execution::api_tests::node_metrics_history_query_fails; use ic_tests::execution::api_tests::node_metrics_history_update_succeeds; use ic_tests::execution::api_tests::test_controller; use ic_tests::execution::api_tests::test_cycles_burn; +use ic_tests::execution::api_tests::test_in_replicated_execution; use ic_tests::execution::api_tests::test_raw_rand_api; use ic_tests::execution::big_stable_memory::*; use ic_tests::execution::canister_heartbeat::*; @@ -32,6 +33,7 @@ fn main() -> Result<()> { .add_test(systest!(malicious_input_test)) .add_test(systest!(test_raw_rand_api)) .add_test(systest!(test_controller)) + .add_test(systest!(test_in_replicated_execution)) .add_test(systest!(test_cycles_burn)) .add_test(systest!(node_metrics_history_update_succeeds)) .add_test(systest!(node_metrics_history_query_fails)) diff --git a/rs/tests/src/execution/api_tests.rs b/rs/tests/src/execution/api_tests.rs index f3798219102..a774bec5011 100644 --- a/rs/tests/src/execution/api_tests.rs +++ b/rs/tests/src/execution/api_tests.rs @@ -110,6 +110,61 @@ pub fn test_controller(env: TestEnv) { }) } +pub fn test_in_replicated_execution(env: TestEnv) { + let logger = env.logger(); + let app_node = env.get_first_healthy_application_node_snapshot(); + let agent = app_node.build_default_agent(); + block_on({ + async move { + let canister = UniversalCanister::new_with_retries( + &agent, + app_node.effective_canister_id(), + &logger, + ) + .await; + + const REPLICATED_EXECUTION: [u8; 4] = [1u8, 0u8, 0u8, 0u8]; + const NON_REPLICATED_EXECUTION: [u8; 4] = [0u8, 0u8, 0u8, 0u8]; + + // Assert update is in replicated execution. + assert_eq!( + canister + .update(wasm().in_replicated_execution().reply_int()) + .await + .unwrap(), + REPLICATED_EXECUTION + ); + + // Assert replicated query is in replicated execution. + assert_eq!( + canister + .replicated_query(wasm().in_replicated_execution().reply_int()) + .await + .unwrap(), + REPLICATED_EXECUTION + ); + + // Assert query is NOT in replicated execution. + assert_eq!( + canister + .query(wasm().in_replicated_execution().reply_int()) + .await + .unwrap(), + NON_REPLICATED_EXECUTION + ); + + // Assert composite query is NOT in replicated execution. + assert_eq!( + canister + .composite_query(wasm().in_replicated_execution().reply_int()) + .await + .unwrap(), + NON_REPLICATED_EXECUTION + ); + } + }) +} + pub fn test_cycles_burn(env: TestEnv) { let nns_node = env.get_first_healthy_nns_node_snapshot(); let agent = nns_node.build_default_agent(); diff --git a/rs/tests/src/util.rs b/rs/tests/src/util.rs index 1458cb211e6..d66edfaac89 100644 --- a/rs/tests/src/util.rs +++ b/rs/tests/src/util.rs @@ -470,6 +470,17 @@ impl<'a> UniversalCanister<'a> { .await } + pub async fn replicated_query>>( + &self, + payload: P, + ) -> Result, AgentError> { + self.agent + .update(&self.canister_id, "query") + .with_arg(payload.into()) + .call_and_wait() + .await + } + pub async fn composite_query>>( &self, payload: P, diff --git a/rs/universal_canister/impl/src/api.rs b/rs/universal_canister/impl/src/api.rs index b65c39943f7..4eb51423fef 100644 --- a/rs/universal_canister/impl/src/api.rs +++ b/rs/universal_canister/impl/src/api.rs @@ -71,6 +71,7 @@ mod ic0 { pub fn mint_cycles(amount: u64) -> u64; pub fn is_controller(src: u32, size: u32) -> u32; + pub fn in_replicated_execution() -> u32; pub fn cycles_burn128(amount_high: u64, amount_low: u64, dst: u32) -> (); } @@ -396,6 +397,10 @@ pub fn is_controller(data: &[u8]) -> u32 { unsafe { ic0::is_controller(data.as_ptr() as u32, data.len() as u32) } } +pub fn in_replicated_execution() -> u32 { + unsafe { ic0::in_replicated_execution() } +} + /// Burn cycles. pub fn cycles_burn128(amount_high: u64, amount_low: u64) -> Vec { let mut bytes = vec![0u8; CYCLES_SIZE]; diff --git a/rs/universal_canister/impl/src/lib.rs b/rs/universal_canister/impl/src/lib.rs index ccb0848b083..9d4f2c186f5 100644 --- a/rs/universal_canister/impl/src/lib.rs +++ b/rs/universal_canister/impl/src/lib.rs @@ -108,5 +108,6 @@ try_from_u8!( CyclesBurn128 = 78, BlobLength = 79, PushEqualBytes = 80, + InReplicatedExecution = 81, } ); diff --git a/rs/universal_canister/impl/src/main.rs b/rs/universal_canister/impl/src/main.rs index 2286ea82ec3..9fca6f77c12 100644 --- a/rs/universal_canister/impl/src/main.rs +++ b/rs/universal_canister/impl/src/main.rs @@ -412,6 +412,7 @@ fn eval(ops_bytes: OpsBytes) { let data = vec![byte as u8; length as usize]; stack.push_blob(data); } + Ops::InReplicatedExecution => stack.push_int(api::in_replicated_execution()), } } } diff --git a/rs/universal_canister/lib/src/lib.rs b/rs/universal_canister/lib/src/lib.rs index a6f661d38fb..6e2aae454e7 100644 --- a/rs/universal_canister/lib/src/lib.rs +++ b/rs/universal_canister/lib/src/lib.rs @@ -17,7 +17,7 @@ use universal_canister::Ops; /// `rs/universal_canister`. pub const UNIVERSAL_CANISTER_WASM: &[u8] = include_bytes!("universal-canister.wasm"); pub const UNIVERSAL_CANISTER_WASM_SHA256: [u8; 32] = - hex!("93c8bebd7e74cd67f60bb9ffebc7a82eaea8d60053ec967a7f5a225e54144a95"); + hex!("68fdfb339cb5ce5351a63ef8910cce62ebc8e00ca8d5a6f01ac2b18d79a04985"); /// A succinct shortcut for creating a `PayloadBuilder`, which is used to encode /// instructions to be executed by the UC. @@ -568,6 +568,11 @@ impl PayloadBuilder { self } + pub fn in_replicated_execution(mut self) -> Self { + self.0.push(Ops::InReplicatedExecution as u8); + self + } + pub fn build(self) -> Vec { self.0 } diff --git a/rs/universal_canister/lib/src/universal-canister.wasm b/rs/universal_canister/lib/src/universal-canister.wasm index 1c4808e81fe..3b637b47a35 100755 Binary files a/rs/universal_canister/lib/src/universal-canister.wasm and b/rs/universal_canister/lib/src/universal-canister.wasm differ