Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add system API endpoints for cycle cost calculation #4110

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Changes from 11 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
76971dc
cost api
michael-weigelt Jan 27, 2025
d7ccf82
title link
michael-weigelt Jan 27, 2025
3c04c79
Merge remote-tracking branch 'origin' into mwe/cost_api
michael-weigelt Jan 27, 2025
dd4dd3d
Update docs/references/ic-interface-spec.md
michael-weigelt Jan 28, 2025
75356a7
Update docs/references/ic-interface-spec.md
michael-weigelt Jan 28, 2025
a58f10c
Update docs/references/ic-interface-spec.md
michael-weigelt Jan 28, 2025
1dda5a1
Update docs/references/ic-interface-spec.md
michael-weigelt Jan 28, 2025
4bc2bbb
Update docs/references/ic-interface-spec.md
michael-weigelt Jan 28, 2025
d30ff7f
Update docs/references/ic-interface-spec.md
michael-weigelt Jan 28, 2025
46f09cd
suggestions
michael-weigelt Jan 29, 2025
c14e718
semantics
michael-weigelt Jan 29, 2025
364a80f
typo
michael-weigelt Jan 29, 2025
1a9c592
semantics fix
michael-weigelt Jan 29, 2025
ea6a75d
arbitrary
michael-weigelt Jan 29, 2025
8bfa142
quotes
michael-weigelt Jan 29, 2025
6f97a68
renaming, adding curve arg
michael-weigelt Feb 3, 2025
2158afa
Update docs/references/ic-interface-spec.md
michael-weigelt Feb 4, 2025
e8bca2b
comments
michael-weigelt Feb 10, 2025
c277bae
semantics
michael-weigelt Feb 10, 2025
8c380b5
Merge remote-tracking branch 'origin' into mwe/cost_api
michael-weigelt Feb 12, 2025
c359747
clarify cost_call
michael-weigelt Feb 12, 2025
2b05f70
comments
michael-weigelt Feb 18, 2025
6eabee4
Merge branch 'master' into mwe/cost_api
michael-weigelt Feb 18, 2025
2ea843e
Update docs/references/ic-interface-spec.md
michael-weigelt Feb 18, 2025
4494d94
Update docs/references/ic-interface-spec.md
michael-weigelt Feb 18, 2025
7ca24c6
Update docs/references/ic-interface-spec.md
michael-weigelt Feb 18, 2025
b2744cd
comment
michael-weigelt Feb 18, 2025
d4aa91a
Merge branch 'mwe/cost_api' of github.com:dfinity/portal into mwe/cos…
michael-weigelt Feb 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 98 additions & 2 deletions docs/references/ic-interface-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -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_ecdsa(src : I, size : I, dst : I) -> (); // * s
ic0.cost_schnorr(src : I, size : I, dst : I) -> (); // * s
ic0.cost_vetkey(src : I, size : I, 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
Expand Down Expand Up @@ -2027,6 +2035,48 @@ 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_ecdsa(src : I, size : I, dst : I) -> (); `I ∈ {i32, i64}`

- `ic0.cost_schnorr(src : I, size : I, dst : I) -> (); `I ∈ {i32, i64}`

- `ic0.cost_vetkey(src : I, 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` . 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`.

- `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.
Expand Down Expand Up @@ -7465,6 +7515,52 @@ ic0.in_replicated_execution<es>() : i32 =
then return 1
else return 0

I ∈ {i32, i64}
ic0.cost_call<es>(method_name_size: i64, payload_size: i64, dst: I) : () =
copy_cycles_to_canister<es>(dst, arbitrary())

I ∈ {i32, i64}
ic0.cost_create_canister<es>(dst: I) : () =
copy_cycles_to_canister<es>(dst, arbitrary())

I ∈ {i32, i64}
ic0.cost_cost_http_request<es>(request_size: i64, max_res_bytes: i64, dst: I) : () =
copy_cycles_to_canister<es>(dst, arbitrary())

I ∈ {i32, i64}
ic0.cost_ecdsa<es>(src: I, size: I, dst: I) : () =
principal_bytes = copy_from_canister<es>(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<es>(dst, arbitrary())

I ∈ {i32, i64}
ic0.cost_schnorr<es>(src: I, size: I, dst: I) : () =
principal_bytes = copy_from_canister<es>(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<es>(dst, arbitrary())

I ∈ {i32, i64}
ic0.cost_vetkey<es>(src: I, size: I, dst: I) : () =
principal_bytes = copy_from_canister<es>(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<es>(dst, arbitrary())

I ∈ {i32, i64}
ic0.replication_factor<es, S>(src: I, size: I, dst: I) : i32 =
principal_bytes = copy_from_canister<es>(src, size)
if not principal_bytes encode a principal then
Trap {cycles_used = es.cycles_used;}
return S.canister_subnet[principal_bytes].subnet_size

I ∈ {i32, i64}
ic0.debug_print<es>(src : I, size : I) =
return
Expand Down
Loading