Skip to content

Commit

Permalink
Merge branch 'maksym/exc-1494-repl-api' into 'master'
Browse files Browse the repository at this point in the history
feat: [EXC-1494] implement ic0.in_replicated_execution system API method

This MR implements `ic0.in_replicated_execution` system API method.

It returns `1` if the canister is being run in replicated mode and `0` otherwise.

Also updated `universal_canister` with `in_replicated_execution` method. 

See merge request dfinity-lab/public/ic!16560
  • Loading branch information
maksymar committed Dec 8, 2023
2 parents 185add8 + 30345bf commit 5014ae8
Show file tree
Hide file tree
Showing 20 changed files with 259 additions and 2 deletions.
1 change: 1 addition & 0 deletions hs/spec_compliance/src/IC/Test/Spec.hs
Expand Up @@ -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",
Expand Down
3 changes: 3 additions & 0 deletions hs/spec_compliance/src/IC/Test/Universal.hs
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions rs/embedders/src/wasm_utils/validation.rs
Expand Up @@ -580,6 +580,16 @@ fn get_valid_system_apis() -> HashMap<String, HashMap<String, FunctionSignature>
},
)],
),
(
"in_replicated_execution",
vec![(
API_VERSION_IC0,
FunctionSignature {
param_types: vec![],
return_type: vec![ValType::I32],
},
)],
),
(
"cycles_burn128",
vec![(
Expand Down
12 changes: 12 additions & 0 deletions rs/embedders/src/wasmtime_embedder/system_api.rs
Expand Up @@ -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| {
Expand Down
2 changes: 2 additions & 0 deletions rs/embedders/src/wasmtime_embedder/system_api_complexity.rs
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions rs/execution_environment/benches/system_api/execute_update.rs
Expand Up @@ -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),
Expand Down
3 changes: 2 additions & 1 deletion rs/execution_environment/src/hypervisor/tests.rs
Expand Up @@ -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
);
Expand Down
92 changes: 92 additions & 0 deletions 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<WasmResult, UserError>) -> Vec<u8> {
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);
}
6 changes: 6 additions & 0 deletions rs/interfaces/src/execution_environment.rs
Expand Up @@ -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<u32>;

/// 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<i32>;

/// Burns the provided `amount` cycles.
/// Removes cycles from the canister's balance.
///
Expand Down
5 changes: 5 additions & 0 deletions rs/rust_canisters/dfn_core/src/api.rs
Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions rs/system_api/src/lib.rs
Expand Up @@ -2882,6 +2882,31 @@ impl SystemApi for SystemApiImpl {
result
}

fn ic0_in_replicated_execution(&self) -> HypervisorResult<i32> {
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,
Expand Down
15 changes: 15 additions & 0 deletions rs/system_api/tests/system_api.rs
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down
2 changes: 2 additions & 0 deletions rs/tests/execution/general_execution_test.rs
Expand Up @@ -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::*;
Expand All @@ -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))
Expand Down

0 comments on commit 5014ae8

Please sign in to comment.