前言
这是dfinity中动态创建Canister例子,就是在A.mo 的actor中动态创建B.mo中的actor类。
项目
1.首先创建一个项目
dfx new CanisterMGT
2.启动项目
cd CanisterMGT
dfx start --clean
3.在项目下创建types.mo 和 sub.mo文件
4.types.mo是canister所需要的类型和属性,添加如下代码:
module{
public type canister_settings =
{
freezing_threshold : ?Nat;
controllers : ?[Principal];
memory_allocation : ?Nat;
compute_allocation : ?Nat;
};
public type definite_canister_settings =
{
freezing_threshold : Nat;
controllers : [Principal];
memory_allocation : Nat;
compute_allocation : Nat;
};
public type user_id = Principal;
public type wasm_module = [Nat8];
public type canister_id = Principal;
public type ICActor = actor
{
canister_status : shared { canister_id : canister_id } -> async
{
// status : { #stopped; #stopping; #running };
// memory_size : Nat;
// cycles : Nat;
settings : { controllers : [Principal] };
//module_hash : ?[Nat8];
};
create_canister : shared { settings : ?canister_settings } -> async
{
canister_id : canister_id;
};
delete_canister : shared { canister_id : canister_id } -> async ();
deposit_cycles : shared { canister_id : canister_id } -> async ();
install_code : shared
{
arg : [Nat8];wasm_module : wasm_module;mode : { #reinstall; #upgrade; #install };canister_id : canister_id;
} -> async ();
provisional_create_canister_with_cycles : shared
{
settings : ?canister_settings;
amount : ?Nat;
} -> async { canister_id : canister_id };
provisional_top_up_canister : shared
{
canister_id : canister_id;
amount : Nat;
} -> async ();
raw_rand : shared () -> async [Nat8];
start_canister : shared { canister_id : canister_id } -> async ();
stop_canister : shared { canister_id : canister_id } -> async ();
uninstall_code : shared { canister_id : canister_id } -> async ();
update_settings : shared
{
canister_id : Principal;
settings : canister_settings;
} -> async ();
};
}
5.sub.mo是接下要在main.mo里面创建的canistet,sub.mo里面的代码就是它自身要实现的代码,但cycleBalance() 和wallet_receive()这两个属性是必须要添加在里面的,有必要也添加下canister的升级代码。
import Cycles "mo:base/ExperimentalCycles";
import Nat "mo:base/Nat";
shared({caller}) actor class Sub(installer : Principal) = this
{
public query({caller}) func cycleBalance() : async Nat
{
Cycles.balance()
};
public shared({caller}) func wallet_receive() : async Nat
{
Cycles.accept(Cycles.available())
};
public shared query ({caller}) func whoami() : async Principal
{
caller
};
stable var counter = 0;
public query func get() : async Nat
{
return counter;
};
public func inc() : async ()
{
counter += 1;
};
}
6.main.mo是创建canister的代码
import Types "./types";
import Cycles "mo:base/ExperimentalCycles";
import RBT "mo:base/RBTree";
import Nat "mo:base/Nat";
import Principal "mo:base/Principal";
import Sub "./sub";
import Array "mo:base/Array";
actor Self
{
public type canister_info =
{
canister_id : Principal;
id:Nat;
};
//获取当前pid
public shared query ({caller}) func whoami() : async Principal
{
caller
};
//获取当前canister id
public shared query ({caller}) func getCanisterId() : async Principal
{
Principal.fromActor(Self);
};
//记录创建的canister个数
private stable var subs_index : Nat = 0;
private let subs = RBT.RBTree<Nat, Principal>(Nat.compare); //存储创建的sub canister
private let IC : Types.ICActor = actor "aaaaa-aa";
private let CYCLE_LIMIT = 50; //创建canister要分配的cycle
//动态创建canister的函数,返回创建的canisister id
public shared({caller}) func createSub() : async canister_info
{
Cycles.add(CYCLE_LIMIT);
let sub = await Sub.Sub(caller);
let principal = Principal.fromActor(sub);
await IC.update_settings({
canister_id = principal;
settings =
{
freezing_threshold = ?20;
controllers = ?[principal]; //控制器是它自身
memory_allocation = ?0;
compute_allocation = ?0;
}
});
subs.put(b_index, principal);
subs_index += 1;
let canister_info = {canister_id = principal;id = subs_index;}
};
//获取canister的控制器
public func get_controllers(canister_id : Principal) : async [Principal]
{
let status = await IC.canister_status({ canister_id = canister_id });
return status.settings.controllers;
};
};
7.运行项目
dfx deploy
8.调用创建canister的接口
dfx canister call CanisterMGT createSub
动态创建第一个canister
(base) dalao@dalaodeMacBook-Pro CanisterMGT % dfx canister call CanisterMGT createSub
(record { id = 1 : nat; canister_id = principal "r7inp-6aaaa-aaaaa-aaabq-cai" })
9.现在来访问新创建的这个canister里面的属性
查看cycles,这50个是刚刚转进去的
(base) dalao@dalaodeMacBook-Pro CanisterMGT % dfx canister call r7inp-6aaaa-aaaaa-aaabq-cai cycleBalance
(50 : nat)
计数
base) dalao@dalaodeMacBook-Pro CanisterMGT % dfx canister call r7inp-6aaaa-aaaaa-aaabq-cai get
(0 : nat)
(base) dalao@dalaodeMacBook-Pro CanisterMGT % dfx canister call r7inp-6aaaa-aaaaa-aaabq-cai inc
()
(base) dalao@dalaodeMacBook-Pro CanisterMGT % dfx canister call r7inp-6aaaa-aaaaa-aaabq-cai get
(1 : nat)
(base) dalao@dalaodeMacBook-Pro CanisterMGT % dfx canister call r7inp-6aaaa-aaaaa-aaabq-cai inc
()
(base) dalao@dalaodeMacBook-Pro CanisterMGT % dfx canister call r7inp-6aaaa-aaaaa-aaabq-cai get
(2 : nat)
然后再创建一个canister
(base) dalao@dalaodeMacBook-Pro CanisterMGT % dfx canister call CanisterMGT createSub
(record { id = 2 : nat; canister_id = principal "rkp4c-7iaaa-aaaaa-aaaca-cai" })
访问里面的属性
(base) dalao@dalaodeMacBook-Pro CanisterMGT % dfx canister call rkp4c-7iaaa-aaaaa-aaaca-cai get
(0 : nat)
(base) dalao@dalaodeMacBook-Pro CanisterMGT % dfx canister call rkp4c-7iaaa-aaaaa-aaaca-cai inc
()
(base) dalao@dalaodeMacBook-Pro CanisterMGT % dfx canister call rkp4c-7iaaa-aaaaa-aaaca-cai get
(1 : nat)
(base) dalao@dalaodeMacBook-Pro CanisterMGT %
10.查看控制状态
如果canister当前只有一个控制器,它是拒绝外部访问它的控制器
(base) dalao@dalaodeMacBook-Pro CanisterMGT % dfx canister call CanisterMGT get_controllers '(principal "r7inp-6aaaa-aaaaa-aaabq-cai")'
Error: The Replica returned an error: code 4, message: "Only the controllers of the canister r7inp-6aaaa-aaaaa-aaabq-cai can control it.
Canister's controllers: r7inp-6aaaa-aaaaa-aaabq-cai
Sender's ID: rrkah-fqaaa-aaaaa-aaaaq-cai"
11.创建canister分配过去的cycle记得回收,我在测试的时创建一堆canister之后,退出环境之后才记得没有回收cycle,现在还在找办法找回来。