Skip to content
This repository was 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 all 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
8 changes: 7 additions & 1 deletion spec/_attachments/ic.did
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,13 @@ service ic : {
sender_canister_version : opt nat64;
}) -> ();
install_code : (record {
mode : variant {install; reinstall; upgrade};
mode : variant {
install;
reinstall;
upgrade : opt record {
skip_pre_upgrade: opt bool;
}
};
canister_id : canister_id;
wasm_module : wasm_module;
arg : blob;
Expand Down
1 change: 1 addition & 0 deletions spec/_attachments/interface-spec-changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* Update conditions on requested paths in HTTP read state requests.
* Added new query methods in the Bitcoin API.
* Added node public keys to certified state and node signatures to query call responses.
* Added a new mode for canister upgrades skipping pre-upgrade method's execution.

### 0.20.0 (2023-07-11) {#0_20_0}
* IC Bitcoin API, ECDSA API, canister HTTPS outcalls API, and 128-bit cycles System API are considered stable.
Expand Down
43 changes: 32 additions & 11 deletions spec/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1173,7 +1173,7 @@ The IC assumes the canister to be fully instantiated if the `canister_init` meth

When a canister is upgraded to a new WebAssembly module, the IC:

1. Invokes `canister_pre_upgrade` (if present) on the old instance, to give the canister a chance to clean up (e.g. move data to [stable memory](#system-api-stable-memory)).
1. Invokes `canister_pre_upgrade` (if exported by the current canister code and `skip_pre_upgrade` is not `opt true` ) on the old instance, to give the canister a chance to clean up (e.g. move data to [stable memory](#system-api-stable-memory)).

2. Instantiates the new module, including the execution of `(start)`, with a fresh WebAssembly state.

Expand All @@ -1185,6 +1185,14 @@ 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.

:::note
The `skip_pre_upgrade` flag can be enabled to skip the 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.

Skipping the pre-upgrade can lead to data loss.
Use it only as the last resort and only if the stable memory already contains the entire canister state.
:::

#### Public methods {#system-api-requests}

To define a public method of name `name`, a WebAssembly module exports a function with name `canister_update <name>`, `canister_query <name>`, or `canister_composite_query <name>` and type `() -> ()`. We call this the *method entry point*. The name of the exported function distinguishes update, query, and composite query methods.
Expand Down Expand Up @@ -1893,13 +1901,15 @@ This method installs code into a canister.

Only controllers of the canister can install code.

- If `mode = install`, the canister must be empty before. This will instantiate the canister module and invoke its `canister_init` method (if present), as explained in Section "[Canister initialization](#system-api-init)", passing the `arg` to the canister.
- If `mode = variant { install }`, the canister must be empty before. This will instantiate the canister module and invoke its `canister_init` method (if present), as explained in Section "[Canister initialization](#system-api-init)", passing the `arg` to the canister.

- If `mode = reinstall`, if the canister was not empty, its existing code and state (including stable memory) is removed before proceeding as for `mode = install`.
- If `mode = variant { reinstall }`, if the canister was not empty, its existing code and state (including stable memory) is removed before proceeding as for `mode = install`.

Note that this is different from `uninstall_code` followed by `install_code`, as `uninstall_code` generates a synthetic reject response to all callers of the uninstalled canister that the uninstalled canister did not yet reply to and ensures that callbacks to outstanding calls made by the uninstalled canister won't be executed (i.e., upon receiving a response from a downstream call made by the uninstalled canister, the cycles attached to the response are refunded, but no callbacks are executed).

- If `mode = upgrade`, this will perform an upgrade of a non-empty canister as described in [Canister upgrades](#system-api-upgrades), passing `arg` to the `canister_post_upgrade` method of the new instance.
- If `mode = variant { upgrade }`, `mode = variant { upgrade = opt record { skip_pre_upgrade = null } }`, or `mode = variant { upgrade = opt record { skip_pre_upgrade = opt false} }`, this will perform an upgrade of a non-empty canister as described in [Canister upgrades](#system-api-upgrades), passing `arg` to the `canister_post_upgrade` method of the new instance.

- If `mode = variant { upgrade = opt record { skip_pre_upgrade = opt true} }`, the system handles this method similarly to the `mode = variant { upgrade }` case, except that it does not execute the `canister_pre_upgrade` method on the old instance.

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

Expand Down Expand Up @@ -3960,7 +3970,7 @@ S with

#### IC Management Canister: Code upgrade

Only the controllers of the given canister can install new code. This changes the code of an *existing* canister, preserving the state in the 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.
Only the controllers of the given canister can install new code. This changes the code of an *existing* canister, preserving the state in the stable memory. This involves invoking the `canister_pre_upgrade` method, if the `skip_pre_upgrade` flag is not set to `opt true`, on the old and `canister_post_upgrade` method on the new canister, which must succeed and must not invoke other methods.

Conditions

Expand All @@ -3974,7 +3984,6 @@ 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 = upgrade
M.caller ∈ S.controllers[A.canister_id]
S.canisters[A.canister_id] = { wasm_state = Old_state; module = Old_module, …}

Expand All @@ -3995,11 +4004,23 @@ Env = {
certificate = NoCertificate;
status = simple_status(S.canister_status[A.canister_id]);
}
Env1 = Env with {
global_timer = S.global_timer[A.canister_id];
canister_version = S.canister_version[A.canister_id];
}
Old_module.pre_upgrade(Old_State, M.caller, Env1) = Return {stable_memory = Stable_memory; new_certified_data = New_certified_data; cycles_used = Cycles_used;}

(
(A.mode = upgrade or A.mode = upgrade {skip_pre_upgrade = false})
Env1 = Env with {
global_timer = S.global_timer[A.canister_id];
canister_version = S.canister_version[A.canister_id];
}
Old_module.pre_upgrade(Old_State, M.caller, Env1) = Return {stable_memory = Stable_memory; new_certified_data = New_certified_data; cycles_used = Cycles_used;}
)
or
(
A.mode = upgrade {skip_pre_upgrade = true}
Stable_memory = Old_State.stable_mem
New_certified_data = NoCertifiedData
Cycles_used = 0
)

Env2 = Env with {
memory_usage_raw_module = memory_usage_raw_module(A.wasm_module);
memory_usage_canister_history = memory_usage_canister_history(New_canister_history);
Expand Down