Skip to content
This repository has been archived by the owner on Nov 19, 2024. It is now read-only.

feat: a new install_code mode #135

Merged
merged 25 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4c60c0e
feat: a new install_code mode
roman-kashitsyn Feb 2, 2023
0f08b01
fix broken reference
roman-kashitsyn Feb 2, 2023
81bf4cf
fix another link
roman-kashitsyn Feb 2, 2023
e63b080
version -> module and state
mraszyk Feb 7, 2023
5503f3f
Merge branch 'master' into roman-canister-eviction
mraszyk Mar 21, 2023
31217be
Merge branch 'master' into roman-canister-eviction
mraszyk Jun 28, 2023
cedcb73
Change spec to include optional flag instead of additional variant.
dragoljub-duric Jul 14, 2023
412191b
update index.md
dragoljub-duric Jul 17, 2023
6436303
Update spec/index.md
dragoljub-duric Jul 17, 2023
0e970d6
Merge branch 'master' into roman-canister-eviction
mraszyk Jul 25, 2023
d1bdf1f
make skip_pre_upgrade opt bool
mraszyk Jul 25, 2023
d06b9b7
simplify
mraszyk Jul 25, 2023
9dd4f80
Merge branch 'master' into roman-canister-eviction
mraszyk Jul 25, 2023
161208d
Update spec/index.md
mraszyk Jul 26, 2023
8751048
Update spec/index.md
mraszyk Jul 26, 2023
dc35c5c
fix install and reinstall mode notation
mraszyk Jul 26, 2023
eb806e5
Update spec/index.md
mraszyk Jul 27, 2023
211c3f2
Update spec/index.md
mraszyk Jul 27, 2023
62d36f2
update formal part of the spec
mraszyk Aug 4, 2023
ef18627
Merge branch 'master' into roman-canister-eviction
mraszyk Aug 4, 2023
7ae270e
Merge branch 'master' into roman-canister-eviction
mraszyk Aug 16, 2023
ca48865
Merge branch 'master' into roman-canister-eviction
mraszyk Aug 23, 2023
dc1ccf8
Merge branch 'master' into roman-canister-eviction
mraszyk Sep 5, 2023
d6d3783
Merge branch 'master' into roman-canister-eviction
mraszyk Sep 13, 2023
a46653d
update changelog
mraszyk Sep 13, 2023
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
2 changes: 1 addition & 1 deletion spec/ic.did
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ service ic : {
settings : canister_settings
}) -> ();
install_code : (record {
mode : variant {install; reinstall; upgrade};
mode : variant {install; reinstall; upgrade; evict};
mraszyk marked this conversation as resolved.
Show resolved Hide resolved
canister_id : canister_id;
wasm_module : wasm_module;
arg : blob;
Expand Down
86 changes: 86 additions & 0 deletions spec/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,28 @@ During these steps, no other entry point of the old or new canister is invoked.

These steps are atomic: If `canister_pre_upgrade` or `canister_post_upgrade` trap, the upgrade has failed, and the canister is reverted to the previous state. Otherwise, the upgrade has succeeded, and the old instance is discarded.

[#system-api-canister-eviction]
==== Canister eviction

Eviction is a special type of canister upgrade that skips execution of the `canister_pre_upgrade` method on the old canister instance.
The main purpose of this mode is recovery from cases when the `canister_pre_upgrade` hook traps unconditionally preventing the normal upgrade path.

NOTE: Canister eviction can lead to data loss. Use it only as the last resort and only if the stable memory already contains the entire canister state.

The IC executes canister eviction as follows:

1. Discard the old canister module and state except of its <<system-api-stable-memory,stable memory>>.
2. Instantiate the new module, including the execution of `(start)`, with a fresh WebAssembly state.
3. Invoke `canister_post_upgrade` (if present) on the new instance, passing the `arg` provided in the `install_code` call (<<ic-install_code>>).

The IC preserves stable memory throughout the process and discards any other WebAssembly state.

During these steps, the IC does not invoke any other entry points of the old or new canister.
In particular, the system does _not_ invoke the `canister_init` function of the new canister.

These steps are atomic: if the `canister_post_upgrade` method traps, the upgrade fails, and the IC reverts the canister to its state before the upgrade.
If the upgrade succeeds, the system discards the old instance.

[#system-api-requests]
==== Public methods

Expand Down Expand Up @@ -1606,6 +1628,9 @@ Note that this is different from `uninstall_code` followed by `install_code`, as

* If `mode = upgrade`, this will perform an upgrade of a non-empty canister as described in <<system-api-upgrades>>, passing `arg` to the `canister_post_upgrade` method of the new instance.

* If `mode = evict`, the system handles request similarly to the `mode = upgrade` case, except that it does not execute the `canister_pre_upgrade` method on the old instance.
See <<system-api-canister-eviction>> for more detail.

This is atomic: If the response to this request is a `reject`, then this call had no effect.

NOTE: Some canisters may not be able to make sense of callbacks after upgrades; these should be stopped first, to wait for all outstanding callbacks that are not marked as deleted, or be uninstalled first, to prevent outstanding callbacks from being invoked (by marking the corresponding call contexts as deleted). It is expected that the canister admin (or their tooling) does that separately.
Expand Down Expand Up @@ -3462,6 +3487,67 @@ S with
}
....

=== IC Management Canister: Code eviction

Only the controllers of the given canister can install new code.
This method changes the code of an _existing_ canister, preserving its stable memory.
This involves invoking the `canister_pre_upgrade` method on the old and `canister_post_upgrade` method on the new canister, which must succeed and must not invoke other methods.

Conditions::
....
S.messages = Older_messages · CallMessage M · Younger_messages
(M.queue = Unordered) or (∀ msg ∈ Older_messages. msg.queue ≠ M.queue)
M.callee = ic_principal
M.method_name = 'install_code'
M.arg = candid(A)
Mod = parse_wasm_mod(A.wasm_module)
Public_custom_sections = parse_public_custom_sections(A.wasm_module)
Private_custom_sections = parse_private_custom_sections(A.wasm_module)
A.mode = evict
mraszyk marked this conversation as resolved.
Show resolved Hide resolved
M.caller ∈ S.controllers[A.canister_id]
S.canisters[A.canister_id] = { wasm_state = Old_state; …}
Env = {
time = S.time[A.canister_id];
balance = S.balances[A.canister_id];
freezing_limit = freezing_limit(S, A.canister_id);
certificate = NoCertificate;
status = simple_status(S.canister_status[A.canister_id]);
}
Env1 = Env with {
global_timer = 0;
canister_version = S.canister_version[A.canister_id] + 1;
}
Mod.post_upgrade(A.canister_id, Old_state.stable_memory, A.arg, M.caller, Env1) = Return {new_state = New_state; new_certified_data = New_certified_data; new_global_timer = New_global_timer; cycles_used = Cycles_used;}
Cycles_used ≤ S.balances[A.canister_id]
dom(Mod.update_methods) ∩ dom(Mod.query_methods) = ∅
....
State after::
....
S with
canisters[A.canister_id] = {
wasm_state = New_state;
module = Mod;
raw_module = A.wasm_module;
public_custom_sections = Public_custom_sections;
private_custom_sections = Private_custom_sections;
}
if New_certified_data ≠ NoCertifiedData:
certified_data[A.canister_id] = New_certified_data
if New_global_timer ≠ NoGlobalTimer:
global_timer[A.canister_id] = New_global_timer
else:
global_timer[A.canister_id] = 0
canister_version[A.canister_id] = S.canister_version[A.canister_id] + 1
balances[A.canister_id] = S.balances[A.canister_id] - Cycles_used;
messages = Older_messages · Younger_messages ·
ResponseMessage {
origin = M.origin;
response = Reply (candid());
refunded_cycles = M.transferred_cycles;
}
....


[#rule-uninstall]
==== IC Management Canister: Code uninstallation

Expand Down