Skip to content

Commit

Permalink
Merge branch 'main' into severin/wasm-whitelist
Browse files Browse the repository at this point in the history
  • Loading branch information
sesi200 authored Jun 30, 2023
2 parents cadfdc4 + fa60278 commit 9b61a8c
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 221 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ jobs:
fail-fast: false
matrix:
node:
- 14
- 16
- 18
env:
DFX_VERSION: 0.14.1
SKIP_WASM: true
Expand Down
6 changes: 6 additions & 0 deletions craco.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
const { addBeforeLoader, loaderByName } = require("@craco/craco");
const webpack = require("webpack");

// Patch unsupported MD4 hash function for Node >= 17.x
const crypto = require("crypto");
const { createHash } = crypto;
crypto.createHash = (algorithm) =>
createHash(algorithm === "md4" ? "sha256" : algorithm);

let canisterEnv;

function initCanisterIds() {
Expand Down
423 changes: 216 additions & 207 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/moc.js

Large diffs are not rendered by default.

27 changes: 23 additions & 4 deletions service/pool/Main.mo
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this {
stable var stablePool : [Types.CanisterInfo] = [];
stable var stableMetadata : [(Principal, (Int, Bool))] = [];
stable var stableChildren : [(Principal, [Principal])] = [];
stable var stableTimers : [Types.CanisterInfo] = [];
stable var previousParam : ?Types.InitParams = null;

system func preupgrade() {
let (tree, metadata, children) = pool.share();
let (tree, metadata, children, timers) = pool.share();
stablePool := tree;
stableMetadata := metadata;
stableChildren := children;
stableTimers := timers;
previousParam := ?params;
};

Expand All @@ -46,6 +48,9 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this {
};
};
pool.unshare(stablePool, stableMetadata, stableChildren);
for (info in stableTimers.vals()) {
updateTimer(info);
}
};

public query func getInitParams() : async Types.InitParams {
Expand Down Expand Up @@ -86,8 +91,9 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this {
Cycles.add topUpCycles;
await IC.deposit_cycles cid;
};
// Lazily cleanup the reused canister
await IC.uninstall_code cid;
if (Option.isSome(status.module_hash)) {
await IC.uninstall_code cid;
};
switch (status.status) {
case (#stopped or #stopping) {
await IC.start_canister cid;
Expand Down Expand Up @@ -150,12 +156,25 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this {
await IC.install_code newArgs;
stats := Logs.updateStats(stats, #install);
switch (pool.refresh(info, profiling)) {
case (?newInfo) newInfo;
case (?newInfo) {
updateTimer(newInfo);
newInfo;
};
case null { throw Error.reject "Cannot find canister" };
};
};
};

func updateTimer(info: Types.CanisterInfo) {
func job() : async () {
pool.removeTimer(info.id);
// It is important that the timer job checks for the timestamp first.
// This prevents late-runner jobs from deleting newly installed code.
await removeCode(info);
};
pool.updateTimer(info, job);
};

public func callForward(info : Types.CanisterInfo, function : Text, args : Blob) : async Blob {
if (pool.find info) {
await InternetComputer.call(info.id, function, args);
Expand Down
32 changes: 29 additions & 3 deletions service/pool/Types.mo
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Array "mo:base/Array";
import List "mo:base/List";
import Option "mo:base/Option";
import Int "mo:base/Int";
import Timer "mo:base/Timer";

module {
public type InitParams = {
Expand Down Expand Up @@ -51,9 +52,11 @@ module {
public class CanisterPool(size: Nat, ttl: Nat, max_family_tree_size: Nat) {
var len = 0;
var tree = Splay.Splay<CanisterInfo>(canisterInfoCompare);
// Metadata is a replicate of splay tree, which allows lookup without timestamp. Internal use only.
var metadata = TrieMap.TrieMap<Principal, (Int, Bool)>(Principal.equal, Principal.hash);
var childrens = TrieMap.TrieMap<Principal, List.List<Principal>>(Principal.equal, Principal.hash);
var parents = TrieMap.TrieMap<Principal, Principal>(Principal.equal, Principal.hash);
let timers = TrieMap.TrieMap<Principal, Timer.TimerId>(Principal.equal, Principal.hash);

public type NewId = { #newId; #reuse:CanisterInfo; #outOfCapacity:Nat };

Expand Down Expand Up @@ -123,6 +126,24 @@ module {
return true;
};

public func updateTimer(info: CanisterInfo, job : () -> async ()) {
let elapsed = Time.now() - info.timestamp;
let duration = if (elapsed > ttl) { 0 } else { Int.abs(ttl - elapsed) };
let tid = Timer.setTimer(#nanoseconds duration, job);
switch (timers.replace(info.id, tid)) {
case null {};
case (?old_id) {
// The old job can still run when it has expired, but the future
// just started to run. To be safe, the job needs to check for timestamp.
Timer.cancelTimer(old_id);
};
};
};

public func removeTimer(cid: Principal) {
timers.delete cid;
};

private func notExpired(info: CanisterInfo, now: Int) : Bool = (info.timestamp > now - ttl);

// Return a list of canister IDs from which to uninstall code
Expand All @@ -140,17 +161,22 @@ module {
result
};

public func share() : ([CanisterInfo], [(Principal, (Int, Bool))], [(Principal, [Principal])]) {
public func share() : ([CanisterInfo], [(Principal, (Int, Bool))], [(Principal, [Principal])], [CanisterInfo]) {
let stableInfos = Iter.toArray(tree.entries());
let stableMetadata = Iter.toArray(metadata.entries());
let stableChildrens =
let stableChildren =
Iter.toArray(
Iter.map<(Principal, List.List<Principal>), (Principal, [Principal])>(
childrens.entries(),
func((parent, children)) = (parent, List.toArray(children))
)
);
(stableInfos, stableMetadata, stableChildrens)
let stableTimers = Iter.toArray(
Iter.filter<CanisterInfo>(
tree.entries(),
func (info) = Option.isSome(timers.get(info.id))
));
(stableInfos, stableMetadata, stableChildren, stableTimers)
};

public func unshare(stableInfos: [CanisterInfo], stableMetadata: [(Principal, (Int, Bool))], stableChildrens : [(Principal, [Principal])]) {
Expand Down
4 changes: 1 addition & 3 deletions service/pool/tests/actor_class/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,7 @@ let args = record { arg = blob ""; wasm_module = parent; mode = variant { instal
call S.installCode(c1, args, false, false);
let c1 = c1.id;

call c1.makeChild(0);
fail call c1.makeChild(1);
assert _ ~= "Canister has been uninstalled";
fail call c1.makeChild(0);
call S.getCanisterId(nonce);
call S.getCanisterId(nonce);

Expand Down
19 changes: 18 additions & 1 deletion service/pool/tests/canisterPool.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@
load "prelude.sh";

let wasm = file("../../../.dfx/local/canisters/backend/backend.wasm");
let empty_wasm = blob "\00asm\01\00\00\00";

let init = opt record {
cycles_per_canister = 105_000_000_000 : nat;
max_num_canisters = 2 : nat;
nonce_time_to_live = 1 : nat;
canister_time_to_live = 5_000_000_000 : nat;
max_family_tree_size = 5 : nat;
};
let S = install(wasm, init, null);
let nonce = record { timestamp = 1 : int; nonce = 1 : nat };
let CID = call S.getCanisterId(nonce);
call S.installCode(CID, record { arg = blob ""; wasm_module = empty_wasm; mode = variant { install }; canister_id = CID.id }, false);
metadata(CID.id, "module_hash");

// Immediately expire
let init = opt record {
Expand All @@ -13,7 +27,6 @@ let init = opt record {
};
let S = install(wasm, init, null);

let nonce = record { timestamp = 1 : int; nonce = 1 : nat };
let c1 = call S.getCanisterId(nonce);
c1;
let c2 = call S.getCanisterId(nonce);
Expand Down Expand Up @@ -64,3 +77,7 @@ call ic.provisional_top_up_canister(
},
);
call S.getCanisterId(nonce);

// Enough time has passed that the timer has removed the canister code
fail metadata(CID.id, "module_hash");
assert _ ~= "unknown";
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { DeployModal, DeploySetter } from "./components/DeployModal";
import { backend, saved } from "./config/actor";
import { setupEditorIntegration } from "./integrations/editorIntegration";

const MOC_VERSION = "0.9.1";
const MOC_VERSION = "0.9.3";

const GlobalStyles = createGlobalStyle`
:root {
Expand Down

0 comments on commit 9b61a8c

Please sign in to comment.