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

[not FINAL] feat: new System API call ic0.call_cycles_add128_up_to #316

Closed
wants to merge 7 commits into from
79 changes: 60 additions & 19 deletions spec/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,8 @@ The following sections describe various System API functions, also referred to a
ic0.call_data_append : (src : i32, size : i32) -> (); // U CQ Ry Rt CRy CRt T
ic0.call_cycles_add : (amount : i64) -> (); // U Ry Rt T
ic0.call_cycles_add128 : (amount_high : i64, amount_low: i64) -> (); // U Ry Rt T
ic0.call_cycles_add128_up_to : (max_amount_high : i64, max_amount_low: i64, dst: i32)
-> (); // U Ry Rt T
ic0.call_perform : () -> ( err_code : i32 ); // U CQ Ry Rt CRy CRt T

ic0.stable_size : () -> (page_count : i32); // * s
Expand Down Expand Up @@ -1652,6 +1654,25 @@ There must be at most one call to `ic0.call_on_cleanup` between `ic0.call_new` a

This system call traps if the cycle balance of the canister after transferring cycles decreases below the canister's freezing limit.

- `ic0.call_cycles_add128_up_to : (max_amount_high : i64, max_amount_low : i64, dst: i32) -> ()`

This system call moves cycles from the canister balance onto the call under construction, to be transferred with that call.

This moves the maximum possible amount of cycles onto the call, up to these constraints:

- It moves no more cycles than represented by a 128-bit value which can be obtained by combining the `max_amount_high` and `max_amount_low` parameters.

- The cycle balance of the canister after transferring cycles does not decrease below the canister's freezing limit.
mraszyk marked this conversation as resolved.
Show resolved Hide resolved

The cycles are deducted from the balance as shown by `ic0.canister_cycles_balance128` immediately, and moved back if the call cannot be performed (e.g. if `ic0.call_perform` signals an error, if the canister invokes `ic0.call_new`, or returns without calling `ic0.call_perform`).

This may be called multiple times between `ic0.call_new` and `ic0.call_perform`, each time possibly moving more cycles onto the call.

This system call traps if there is no call under construction, i.e., if not called between `ic0.call_new` and `ic0.call_perform`.

This system call also copies the actual amount of cycles that were moved onto the call represented by a 128-bit value starting at the location `dst` in the canister memory.
mraszyk marked this conversation as resolved.
Show resolved Hide resolved
This amount might be lower than the 128-bit value obtained by combining the `max_amount_high` and `max_amount_low` parameters.

- `ic0.call_perform : () -> ( err_code : i32 )`

This concludes assembling the call. It queues the call message to the given destination, but does not actually act on it until the current WebAssembly function returns without trapping.
Expand All @@ -1676,7 +1697,7 @@ This specification currently does not go into details about which actions cost h

- `ic0.canister_cycle_balance : () → i64`

Indicates the current cycle balance of the canister. It is the canister balance before the execution of the current message, minus a reserve to pay for the execution of the current message, minus any cycles queued up to be sent via `ic0.call_cycles_add` and `ic0.call_cycles_add128`. After execution of the message, the IC may add unused cycles from the reserve back to the balance.
Indicates the current cycle balance of the canister. It is the canister balance before the execution of the current message, minus a reserve to pay for the execution of the current message, minus any cycles queued up to be sent via `ic0.call_cycles_add`, `ic0.call_cycles_add128`, and `ic0.call_cycles_add128_up_to`. After execution of the message, the IC may add unused cycles from the reserve back to the balance.

:::note

Expand All @@ -1686,7 +1707,7 @@ This call traps if the current balance does not fit into a 64-bit value. Caniste

- `ic0.canister_cycle_balance128 : (dst : i32) → ()`

Indicates the current cycle balance of the canister by copying the value at the location `dst` in the canister memory. It is the canister balance before the execution of the current message, minus a reserve to pay for the execution of the current message, minus any cycles queued up to be sent via `ic0.call_cycles_add` and `ic0.call_cycles_add128`. After execution of the message, the IC may add unused cycles from the reserve back to the balance.
Indicates the current cycle balance of the canister by copying the value at the location `dst` in the canister memory. It is the canister balance before the execution of the current message, minus a reserve to pay for the execution of the current message, minus any cycles queued up to be sent via `ic0.call_cycles_add`, `ic0.call_cycles_add128`, and `ic0.call_cycles_add128_up_to`. After execution of the message, the IC may add unused cycles from the reserve back to the balance.

- `ic0.msg_cycles_available : () → i64`

Expand Down Expand Up @@ -2378,7 +2399,7 @@ As a provisional method on development instances, the `provisional_create_canist

The optional `sender_canister_version` parameter can contain the caller's canister version. If provided, its value must be equal to `ic0.canister_version`.

Cycles added to this call via `ic0.call_cycles_add` and `ic0.call_cycles_add128` are returned to the caller.
Cycles added to this call via `ic0.call_cycles_add`, `ic0.call_cycles_add128`, and `ic0.call_cycles_add128_up_to` are returned to the caller.

This method is only available in local development instances.

Expand All @@ -2388,7 +2409,7 @@ This method can be called by canisters as well as by external users via ingress

As a provisional method on development instances, the `provisional_top_up_canister` method is provided. It adds `amount` cycles to the balance of canister identified by `amount`.

Cycles added to this call via `ic0.call_cycles_add` and `ic0.call_cycles_add128` are returned to the caller.
Cycles added to this call via `ic0.call_cycles_add`, `ic0.call_cycles_add128`, and `ic0.call_cycles_add128_up_to` are returned to the caller.

Any user can top-up any canister this way.

Expand Down Expand Up @@ -6812,25 +6833,45 @@ ic0.call_cycles_add<es>(amount : i64) =
es.balance := es.balance - amount
es.pending_call.transferred_cycles := es.pending_call.transferred_cycles + amount

ic0.call_cycles_add128<es>(amount_high : i64, amount_low : i64) =
if es.context ∉ {U, Ry, Rt, T} then Trap {cycles_used = es.cycles_used;}
if es.pending_call = NoPendingCall then Trap {cycles_used = es.cycles_used;}
let amount = amount_high * 2^64 + amount_low
if liquid_balance(
es.balance,
es.params.sysenv.reserved_balance,
freezing_limit(
es.params.sysenv.compute_allocation,
es.params.sysenv.memory_allocation,
es.params.sysenv.freezing_threshold,
memory_usage_wasm_state(es.wasm_state) + es.params.sysenv.memory_usage_raw_module + es.params.sysenv.memory_usage_canister_history,
es.params.sysenv.subnet_size,
)
) < amount then Trap {cycles_used = es.cycles_used;}
ic0.call_cycles_add128<es>(amount_high : i64, amount_low : i64) =
if es.context ∉ {U, Ry, Rt, T} then Trap {cycles_used = es.cycles_used;}
if es.pending_call = NoPendingCall then Trap {cycles_used = es.cycles_used;}
let amount = amount_high * 2^64 + amount_low
if liquid_balance(
es.balance,
es.params.sysenv.reserved_balance,
freezing_limit(
es.params.sysenv.compute_allocation,
es.params.sysenv.memory_allocation,
es.params.sysenv.freezing_threshold,
memory_usage_wasm_state(es.wasm_state) + es.params.sysenv.memory_usage_raw_module + es.params.sysenv.memory_usage_canister_history,
es.params.sysenv.subnet_size,
)
) < amount then Trap {cycles_used = es.cycles_used;}

es.balance := es.balance - amount
es.pending_call.transferred_cycles := es.pending_call.transferred_cycles + amount

ic0.call_cycles_add128_up_to<es>(max_amount_high : i64, max_amount_low : i64, dst: i32) =
if es.context ∉ {U, Ry, Rt, T} then Trap {cycles_used = es.cycles_used;}
if es.pending_call = NoPendingCall then Trap {cycles_used = es.cycles_used;}
let max_amount = max_amount_high * 2^64 + max_amount_low
let amount = min(max_amount, liquid_balance(
es.balance,
es.params.sysenv.reserved_balance,
freezing_limit(
es.params.sysenv.compute_allocation,
es.params.sysenv.memory_allocation,
es.params.sysenv.freezing_threshold,
memory_usage_wasm_state(es.wasm_state) + es.params.sysenv.memory_usage_raw_module + es.params.sysenv.memory_usage_canister_history,
Copy link
Contributor Author

@mraszyk mraszyk Aug 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
memory_usage_wasm_state(es.wasm_state) + es.params.sysenv.memory_usage_raw_module + es.params.sysenv.memory_usage_canister_history,
memory_usage_wasm_state(es.wasm_state) + es.params.sysenv.memory_usage_raw_module + es.params.sysenv.memory_usage_canister_history + max(|es.pending_call.method_name| + |es.pending_call.arg|, MAX_RESPONSE_SIZE),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not needed though as we don't model message memory anyway.

es.params.sysenv.subnet_size,
)
))

es.balance := es.balance - amount
es.pending_call.transferred_cycles := es.pending_call.transferred_cycles + amount
copy_cycles_to_canister<es>(dst, amount.to_little_endian_bytes())

ic0.call_peform<es>() : ( err_code : i32 ) =
if es.context ∉ {U, CQ, Ry, Rt, CRy, CRt, T} then Trap {cycles_used = es.cycles_used;}
if es.pending_call = NoPendingCall then Trap {cycles_used = es.cycles_used;}
Expand Down
Loading