diff --git a/docs/references/ic-interface-spec.md b/docs/references/ic-interface-spec.md index b011081f80..dd33099668 100644 --- a/docs/references/ic-interface-spec.md +++ b/docs/references/ic-interface-spec.md @@ -1487,8 +1487,16 @@ defaulting to `I = i32` if the canister declares no memory. ic0.time : () -> (timestamp : i64); // * ic0.global_timer_set : (timestamp : i64) -> i64; // I G U Ry Rt C T ic0.performance_counter : (counter_type : i32) -> (counter : i64); // * s - ic0.is_controller: (src : I, size : I) -> ( result: i32); // * s - ic0.in_replicated_execution: () -> (result: i32); // * s + ic0.is_controller : (src : I, size : I) -> ( result : i32); // * s + ic0.in_replicated_execution : () -> (result : i32); // * s + + ic0.cost_call : (method_name_size: i64, payload_size : i64, dst : I) -> (); // * s + ic0.cost_create_canister : (dst : I) -> (); // * s + ic0.cost_http_request : (request_size : i64, max_res_bytes : i64, dst : I) -> (); // * s + ic0.cost_sign_with_ecdsa : (src : I, size : I, ecdsa_curve: i32, dst : I) -> (); // * s + ic0.cost_sign_with_schnorr : (src : I, size : I, algorithm: i32, dst : I) -> (); // * s + ic0.cost_vetkd_derive_encrypted_key : (src : I, size : I, vetkd_curve: i32, dst : I) -> (); // * s + ic0.replication_factor : (src : I, size : I) -> i32; // * s ic0.debug_print : (src : I, size : I) -> (); // * s ic0.trap : (src : I, size : I) -> (); // * s @@ -2027,6 +2035,53 @@ When executing a query or composite query method via a query call (i.e. in non-r This traps if `ic0.data_certificate_present()` returns `0`. +### Cycle cost calculation {#system-api-cycle-cost} + +Inter-canister calls have an implicit cost, and some calls to the management canister require the caller to attach cycles to the call explicitly. +The various cost factors may change over time, so the following system calls give the canister programmatic, up-to-date information about the costs. + +These system calls return costs in Cycles, represented by 128 bits, which will be written to the heap memory starting at offset `dst`. + +- `ic0.cost_call : (method_name_size: i64, payload_size : i64, dst : I) -> ()`; `I ∈ {i32, i64}` + + The cost of an inter-canister call. `method_name_size` is the byte length of the method name, and `payload_size` is the byte length of the argument to the method. + +- `ic0.cost_create_canister : (dst : I) -> ()`; `I ∈ {i32, i64}` + + The cost of creating a canister on the same subnet as the calling canister via [`create_canister`](#ic-create_canister). Note that canister creation via a call to the CMC can have a different cost if the target subnet has a different replication factor. In order to facilitate conversions, see `ic0.replication_factor`. When converting costs using ratios of subnet sizes, be mindful of rounding issues and consider adding a safety margin. + +- `ic0.cost_http_request(request_size : i64, max_res_bytes : i64, dst : I) -> ()`; `I ∈ {i32, i64}` + + The cost of a canister http outcall via [`http_request`](#ic-http_request). `request_size` is the sum of the byte lengths of the following components of an http request: + - url + - method + - headers - i.e., the sum of the lengths of all keys and values + - body + - transform - i.e., the sum of the transform method name length and the length of the transform context + + `max_res_bytes` is the maximum response length the caller wishes to accept. Note that this argument is not optional like in the call to the management canister. The cost depends on `max_res_bytes`, so the caller must provide it explicitly. See the [`http_request`](#ic-http_request) call to the management canister API to learn about the current default and maximum values. + +- `ic0.cost_sign_with_ecdsa(src : I, size : I, ecdsa_curve: i32, dst : I) -> ()`; `I ∈ {i32, i64}` + +- `ic0.cost_sign_with_schnorr(src : I, size : I, algorithm: i32, dst : I) -> ()`; `I ∈ {i32, i64}` + +- `ic0.cost_vetkd_derive_encrypted_key(src : I, vetkd_curve: i32, size : I, dst : I) -> ()`; `I ∈ {i32, i64}` + + These system calls accept a key name via a textual representation for the specific signing scheme / key of a given size stored in the heap memory starting at offset `src`. They also accept an `i32` with the following interpretations: + - `ecdsa_curve: 0 → secp256k1` + - `schnorr_algorithm: 0 → bip340secp256k1, 1 → ed25519` + - `vetkd_curve: 0 → bls12_381` + + See [`sign_with_ecdsa`](#ic-sign_with_ecdsa), [`sign_with_schnorr`](#ic-sign_with_schnorr) and [`vetkd_encrypted_key`](#ic-vetkd_encrypted_key) for more information. + + These system calls trap if the string represented by `src` + `size` does not correspond to a valid key name, such as `dfx_test_key`, `test_key_1` or `key_1`, or if the provided curve enum is an unspecified variant. + +- `ic0.replication_factor : (src : I, size : I) -> i32`; `I ∈ {i32, i64}` + + Returns the replication factor (subnet size) of the subnet identified by the `Principal` at `src` + `size`. + + This system call traps if `src` + `size` do not represent a valid `Principal` or the given `Principal` is not a subnet. + ### Debugging aids In a local canister execution environment, the canister needs a way to emit textual trace messages. On the "real" network, these do not do anything. @@ -7465,6 +7520,54 @@ ic0.in_replicated_execution() : i32 = then return 1 else return 0 +I ∈ {i32, i64} +ic0.cost_call(method_name_size: i64, payload_size: i64, dst: I) : () = + copy_cycles_to_canister(dst, arbitrary()) + +I ∈ {i32, i64} +ic0.cost_create_canister(dst: I) : () = + copy_cycles_to_canister(dst, arbitrary()) + +I ∈ {i32, i64} +ic0.cost_cost_http_request(request_size: i64, max_res_bytes: i64, dst: I) : () = + copy_cycles_to_canister(dst, arbitrary()) + +I ∈ {i32, i64} +ic0.cost_sign_with_ecdsa(src: I, size: I, ecdsa_curve: i32, dst: I) : () = + principal_bytes = copy_from_canister(src, size) + if not principal_bytes encode a principal then + Trap {cycles_used = es.cycles_used;} + if principal_bytes ∉ es.subnets then + Trap {cycles_used = es.cycles_used;} + copy_cycles_to_canister(dst, arbitrary()) + +I ∈ {i32, i64} +ic0.cost_sign_with_schnorr(src: I, size: I, algorithm: i32, dst: I) : () = + principal_bytes = copy_from_canister(src, size) + if not principal_bytes encode a principal then + Trap {cycles_used = es.cycles_used;} + if principal_bytes ∉ es.subnets then + Trap {cycles_used = es.cycles_used;} + copy_cycles_to_canister(dst, arbitrary()) + +I ∈ {i32, i64} +ic0.cost_vetkd_derive_encrypted_key(src: I, size: I, vetkd_curve: i32, dst: I) : () = + principal_bytes = copy_from_canister(src, size) + if not principal_bytes encode a principal then + Trap {cycles_used = es.cycles_used;} + if principal_bytes ∉ es.subnets then + Trap {cycles_used = es.cycles_used;} + copy_cycles_to_canister(dst, arbitrary()) + +I ∈ {i32, i64} +ic0.replication_factor(src: I, size: I) : i32 = + principal_bytes = copy_from_canister(src, size) + if not principal_bytes encode a principal then + Trap {cycles_used = es.cycles_used;} + if principal_bytes ∉ es.subnets then + Trap {cycles_used = es.cycles_used;} + return arbitrary() + I ∈ {i32, i64} ic0.debug_print(src : I, size : I) = return