Skip to content

Commit

Permalink
change origin type to include array of tags
Browse files Browse the repository at this point in the history
  • Loading branch information
chenyan-dfinity committed Sep 7, 2023
1 parent b86dd45 commit 8a96629
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 87 deletions.
46 changes: 31 additions & 15 deletions service/pool/Logs.mo
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,47 @@ import {now = timeNow} "mo:base/Time";
import {toText} "mo:base/Int";

module {
public type SharedStatsByOrigin = (Map.Tree<Text,Nat>, Map.Tree<Text,Nat>);
public type Origin = { origin: Text; tags: [Text] };
public type SharedStatsByOrigin = (Map.Tree<Text,Nat>, Map.Tree<Text,Nat>, Map.Tree<Text, Nat>);
public class StatsByOrigin() {
var canisters = Map.RBTree<Text, Nat>(compare);
var installs = Map.RBTree<Text, Nat>(compare);
public func share() : SharedStatsByOrigin = (canisters.share(), installs.share());
var tags = Map.RBTree<Text, Nat>(compare);
public func share() : SharedStatsByOrigin = (canisters.share(), installs.share(), tags.share());
public func unshare(x : SharedStatsByOrigin) {
canisters.unshare(x.0);
installs.unshare(x.1);
tags.unshare(x.2);
};
public func addCanister(origin: Text) {
switch (canisters.get(origin)) {
case null { canisters.put(origin, 1) };
case (?n) { canisters.put(origin, n + 1) };
}
func addTags(list: [Text]) {
for (tag in list.vals()) {
switch (tags.get(tag)) {
case null { tags.put(tag, 1) };
case (?n) { tags.put(tag, n + 1) };
};
};
};
public func addCanister(origin: Origin) {
switch (canisters.get(origin.origin)) {
case null { canisters.put(origin.origin, 1) };
case (?n) { canisters.put(origin.origin, n + 1) };
};
// Not storing tags for create canister to avoid duplicate counting of tags
// addTags(origin.tags);
};
public func addInstall(origin: Text, referrer: ?Text) {
// TODO: keep track of `referrer`
switch (installs.get(origin)) {
case null { installs.put(origin, 1) };
case (?n) { installs.put(origin, n + 1) };
}
public func addInstall(origin: Origin) {
switch (installs.get(origin.origin)) {
case null { installs.put(origin.origin, 1) };
case (?n) { installs.put(origin.origin, n + 1) };
};
// Only record tags for canister install
addTags(origin.tags);
};
public func dump() : ([(Text, Nat)], [(Text, Nat)]) {
public func dump() : ([(Text, Nat)], [(Text, Nat)], [(Text, Nat)]) {
(toArray<(Text, Nat)>(canisters.entries()),
toArray<(Text, Nat)>(installs.entries()))
toArray<(Text, Nat)>(installs.entries()),
toArray<(Text, Nat)>(tags.entries())
)
};
public func metrics() : Text {
var result = "";
Expand Down
24 changes: 12 additions & 12 deletions service/pool/Main.mo
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this {
stable var stableChildren : [(Principal, [Principal])] = [];
stable var stableTimers : [Types.CanisterInfo] = [];
stable var previousParam : ?Types.InitParams = null;
stable var stableStatsByOrigin : Logs.SharedStatsByOrigin = (#leaf, #leaf);
stable var stableStatsByOrigin : Logs.SharedStatsByOrigin = (#leaf, #leaf, #leaf);

system func preupgrade() {
let (tree, metadata, children, timers) = pool.share();
Expand Down Expand Up @@ -61,9 +61,9 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this {
params;
};

public query func getStats() : async (Logs.Stats, [(Text, Nat)], [(Text, Nat)]) {
let (canister, install) = statsByOrigin.dump();
(stats, canister, install);
public query func getStats() : async (Logs.Stats, [(Text, Nat)], [(Text, Nat)], [(Text, Nat)]) {
let (canister, install, tags) = statsByOrigin.dump();
(stats, canister, install, tags);
};

public query func balance() : async Nat {
Expand All @@ -75,7 +75,7 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this {
ignore Cycles.accept amount;
};

private func getExpiredCanisterInfo(origin : Text) : async Types.CanisterInfo {
private func getExpiredCanisterInfo(origin : Logs.Origin) : async Types.CanisterInfo {
switch (pool.getExpiredCanisterId()) {
case (#newId) {
Cycles.add(params.cycles_per_canister);
Expand Down Expand Up @@ -118,8 +118,8 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this {
};
};

public shared ({ caller }) func getCanisterId(nonce : PoW.Nonce, origin : Text) : async Types.CanisterInfo {
if (origin == "") {
public shared ({ caller }) func getCanisterId(nonce : PoW.Nonce, origin : Logs.Origin) : async Types.CanisterInfo {
if (origin.origin == "") {
throw Error.reject "Please specify an origin";
};
if (caller != controller and not nonceCache.checkProofOfWork(nonce)) {
Expand All @@ -135,9 +135,9 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this {
await getExpiredCanisterInfo(origin);
};

type InstallConfig = { profiling: Bool; is_whitelisted: Bool; origin: Text; referrer: ?Text };
type InstallConfig = { profiling: Bool; is_whitelisted: Bool; origin: Logs.Origin };
public shared ({ caller }) func installCode(info : Types.CanisterInfo, args : Types.InstallArgs, install_config : InstallConfig) : async Types.CanisterInfo {
if (install_config.origin == "") {
if (install_config.origin.origin == "") {
throw Error.reject "Please specify an origin";
};
if (info.timestamp == 0) {
Expand Down Expand Up @@ -169,7 +169,7 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this {
};
await IC.install_code newArgs;
stats := Logs.updateStats(stats, #install);
statsByOrigin.addInstall(install_config.origin, install_config.referrer);
statsByOrigin.addInstall(install_config.origin);
switch (pool.refresh(info, install_config.profiling)) {
case (?newInfo) {
updateTimer(newInfo);
Expand Down Expand Up @@ -310,7 +310,7 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this {
if (not pool.findId caller) {
throw Error.reject "Only a canister managed by the Motoko Playground can call create_canister";
};
let info = await getExpiredCanisterInfo("spawned");
let info = await getExpiredCanisterInfo({origin="spawned"; tags=[]});
let result = pool.setChild(caller, info.id);
if (not result) {
throw Error.reject("In the Motoko Playground, each top level canister can only spawn " # Nat.toText(params.max_family_tree_size) # " descendants including itself");
Expand All @@ -335,7 +335,7 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this {
switch (sanitizeInputs(caller, canister_id)) {
case (#ok info) {
let args = { arg; wasm_module; mode; canister_id };
let config = { profiling = pool.profiling caller; is_whitelisted = false; origin = "spawned"; referrer = null };
let config = { profiling = pool.profiling caller; is_whitelisted = false; origin = {origin = "spawned"; tags = [] } };
ignore await installCode(info, args, config); // inherit the profiling of the parent
};
case (#err makeMsg) throw Error.reject(makeMsg "install_code");
Expand Down
21 changes: 11 additions & 10 deletions service/pool/tests/actor_class/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ let deleter = file(".dfx/local/canisters/Deleter/Deleter.wasm");
let S = install(wasm, null, opt 100_000_000_000_000);

let nonce = record { timestamp = 1 : int; nonce = 1 : nat };
let c1 = call S.getCanisterId(nonce, "test");
let origin = record { origin = "test"; tags = vec {} };
let c1 = call S.getCanisterId(nonce, origin);
let args = record { arg = blob ""; wasm_module = parent; mode = variant { install }; canister_id = c1.id };
call S.installCode(c1, args, record { profiling = false; is_whitelisted = false; origin = "test" });
call S.installCode(c1, args, record { profiling = false; is_whitelisted = false; origin = origin });

let c1 = c1.id;

Expand Down Expand Up @@ -48,26 +49,26 @@ let init = opt record {
let S = install(wasm, init, opt 100_000_000_000_000);

let nonce = record { timestamp = 1 : int; nonce = 1 : nat };
let c1 = call S.getCanisterId(nonce, "test");
let c1 = call S.getCanisterId(nonce, origin);
let args = record { arg = blob ""; wasm_module = parent; mode = variant { install }; canister_id = c1.id };
call S.installCode(c1, args, record { profiling = false; is_whitelisted = false; origin = "test" });
call S.installCode(c1, args, record { profiling = false; is_whitelisted = false; origin = origin });
let c1 = c1.id;

fail call c1.makeChild(0);
call S.getCanisterId(nonce, "test");
call S.getCanisterId(nonce, "test");
call S.getCanisterId(nonce, origin);
call S.getCanisterId(nonce, origin);

// Security check
let S = install(wasm, null, opt 100_000_000_000_000);

let nonce = record { timestamp = 1 : int; nonce = 1 : nat };
let c1 = call S.getCanisterId(nonce, "test");
let c1 = call S.getCanisterId(nonce, origin);
let args = record { arg = blob ""; wasm_module = parent; mode = variant { install }; canister_id = c1.id };
call S.installCode(c1, args, record { profiling = false; is_whitelisted = false; origin = "test" });
call S.installCode(c1, args, record { profiling = false; is_whitelisted = false; origin = origin });

let c2 = call S.getCanisterId(nonce, "test");
let c2 = call S.getCanisterId(nonce, origin);
let args = record { arg = blob ""; wasm_module = deleter; mode = variant { install }; canister_id = c2.id };
call S.installCode(c2, args, record { profiling = false; is_whitelisted = false; origin = "test" });
call S.installCode(c2, args, record { profiling = false; is_whitelisted = false; origin = origin });

let c1 = c1.id;
let c2 = c2.id;
Expand Down
25 changes: 13 additions & 12 deletions service/pool/tests/canisterPool.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ load "prelude.sh";

let wasm = file("../../../.dfx/local/canisters/backend/backend.wasm");
let empty_wasm = blob "\00asm\01\00\00\00";
let origin = record { origin = "test"; tags = vec {"tag"} };

let init = opt record {
cycles_per_canister = 105_000_000_000 : nat;
Expand All @@ -13,8 +14,8 @@ let init = opt record {
};
let S = install(wasm, init, null);
let nonce = record { timestamp = 1 : int; nonce = 1 : nat };
let CID = call S.getCanisterId(nonce, "test");
call S.installCode(CID, record { arg = blob ""; wasm_module = empty_wasm; mode = variant { install }; canister_id = CID.id }, record { profiling = false; is_whitelisted = false; origin = "test" });
let CID = call S.getCanisterId(nonce, origin);
call S.installCode(CID, record { arg = blob ""; wasm_module = empty_wasm; mode = variant { install }; canister_id = CID.id }, record { profiling = false; is_whitelisted = false; origin = origin });
metadata(CID.id, "module_hash");

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

let c1 = call S.getCanisterId(nonce, "test");
let c1 = call S.getCanisterId(nonce, origin);
c1;
let c2 = call S.getCanisterId(nonce, "test");
let c2 = call S.getCanisterId(nonce, origin);
c2;
let c3 = call S.getCanisterId(nonce, "test");
let c3 = call S.getCanisterId(nonce, origin);
c3;
let c4 = call S.getCanisterId(nonce, "test");
let c4 = call S.getCanisterId(nonce, origin);
c4;
assert c1.id != c2.id;
assert c1.id == c3.id;
Expand All @@ -48,14 +49,14 @@ let init = opt record {
max_family_tree_size = 5 : nat;
};
reinstall(S, wasm, init);
let c3 = call S.getCanisterId(nonce, "test");
let c3 = call S.getCanisterId(nonce, origin);
c3;
let c4 = call S.getCanisterId(nonce, "test");
let c4 = call S.getCanisterId(nonce, origin);
c4;
fail call S.getCanisterId(nonce, "test");
fail call S.getCanisterId(nonce, origin);
assert _ ~= "No available canister id";
call S.removeCode(c4);
call S.getCanisterId(nonce, "test");
call S.getCanisterId(nonce, origin);
assert _.id == c4.id;
assert _.timestamp != c4.timestamp;

Expand All @@ -68,15 +69,15 @@ let init = opt record {
max_family_tree_size = 5 : nat;
};
let S = install(wasm, init, opt 100_000_000_000);
fail call S.getCanisterId(nonce, "test");
fail call S.getCanisterId(nonce, origin);
assert _ ~= "105_000_000_000 cycles";
call ic.provisional_top_up_canister(
record {
canister_id = S;
amount = 100_000_000_000_000;
},
);
call S.getCanisterId(nonce, "test");
call S.getCanisterId(nonce, origin);

// Enough time has passed that the timer has removed the canister code
fail metadata(CID.id, "module_hash");
Expand Down
9 changes: 5 additions & 4 deletions service/pool/tests/nonce.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
load "prelude.sh";

let wasm = file("../../../.dfx/local/canisters/backend/backend.wasm");
let origin = record { origin = "test"; tags = vec {} };

identity alice;
let init = opt record {
Expand All @@ -13,11 +14,11 @@ let init = opt record {
};
let S = install(wasm, init, null);

call S.getCanisterId(record { timestamp = 4780472194_000_000_000; nonce = 1 }, "test");
fail call S.getCanisterId(record { timestamp = 4780472194_000_000_000; nonce = 1 }, "test");
call S.getCanisterId(record { timestamp = 4780472194_000_000_000; nonce = 1 }, origin);
fail call S.getCanisterId(record { timestamp = 4780472194_000_000_000; nonce = 1 }, origin);
assert _ ~= "Nonce already used";
call S.getCanisterId(record { timestamp = 4780472194_000_000_001; nonce = 1 }, "test");
call S.getCanisterId(record { timestamp = 4780472194_000_000_001; nonce = 1 }, origin);

identity bob;
fail call S.getCanisterId(record { timestamp = 4780472194_000_000_002; nonce = 1 }, "test");
fail call S.getCanisterId(record { timestamp = 4780472194_000_000_002; nonce = 1 }, origin);
assert _ ~= "Proof of work check failed";
15 changes: 8 additions & 7 deletions service/pool/tests/upgrade.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
load "prelude.sh";

let wasm = file("../../../.dfx/local/canisters/backend/backend.wasm");
let origin = record { origin = "test"; tags = vec {"tag"} };

let init = opt record {
cycles_per_canister = 105_000_000_000 : nat;
Expand All @@ -13,15 +14,15 @@ 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, "test");
let c1 = call S.getCanisterId(nonce, origin);
c1;
let c2 = call S.getCanisterId(nonce, "test");
let c2 = call S.getCanisterId(nonce, origin);
c2;

upgrade(S, wasm, init);
let c3 = call S.getCanisterId(nonce, "test");
let c3 = call S.getCanisterId(nonce, origin);
c3;
let c4 = call S.getCanisterId(nonce, "test");
let c4 = call S.getCanisterId(nonce, origin);
c4;
assert c1.id != c2.id;
assert c1.id == c3.id;
Expand All @@ -40,11 +41,11 @@ upgrade(S, wasm, init);
// stats are preserved after upgrade
call S.getStats();
assert _ == stats;
let c5 = call S.getCanisterId(nonce, "test");
let c5 = call S.getCanisterId(nonce, origin);
c5;
assert c5.id != c1.id;
assert c5.id != c2.id;
fail call S.getCanisterId(nonce, "test");
fail call S.getCanisterId(nonce, origin);
assert _ ~= "No available canister id";

// Cannot reduce pool
Expand All @@ -58,5 +59,5 @@ let init = opt record {
fail upgrade(S, wasm, init);
assert _ ~= "Cannot reduce canisterPool for upgrade";
// still old canister, new TTL does not apply
fail call S.getCanisterId(nonce, "test");
fail call S.getCanisterId(nonce, origin);
assert _ ~= "No available canister id";
6 changes: 3 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ async function fetchFromUrlParams(
const { origin, files } = result;
await dispatch({
type: "setOrigin",
payload: { origin: `playground:post:${origin}` },
payload: { origin: `playground:post:${origin}`, tags: [] },
});
return files;
}
Expand All @@ -95,7 +95,7 @@ async function fetchFromUrlParams(
};
await dispatch({
type: "setOrigin",
payload: { origin: `playground:git:${git}` },
payload: { origin: "playground:git", tags: [`git:${git}`] },
});
return await worker.fetchGithub(repo);
}
Expand Down Expand Up @@ -147,7 +147,7 @@ async function fetchFromUrlParams(
}
await dispatch({
type: "setOrigin",
payload: { origin: `playground:tag:${tag}` },
payload: { origin: "playground:tag", tags: [`tag:${tag}`] },
});
return files;
}
Expand Down
Loading

0 comments on commit 8a96629

Please sign in to comment.