Loading...
Loading...
Compare original and translation side by side
awaitcanister_inspect_messageawaitcanister_inspect_messagemopscore = "2.0.0"ic-cdk = "0.19"candid = "0.10"mopscore = "2.0.0"ic-cdk = "0.19"candid = "0.10"canister_inspect_messageinspect_message2vxsx-faePrincipal.isAnonymous(caller)msg_caller() != Principal::anonymous()awaitawaitpre_upgradepre_upgradeic-stable-structurespersistent actorletvarfreezing_threshold# Check current settings (mainnet)
icp canister settings show backend -e ic
# Increase freezing threshold for high-value canisters
icp canister settings update backend --freezing-threshold 7776000 -e ic # 90 daysicp canister settings update backend --add-controller <backup-principal> -e icfetchRootKey()fetchRootKey()fetchRootKey()fetchRootKey()try/finallyDropcall_on_cleanupcanister_inspect_messageinspect_message2vxsx-faePrincipal.isAnonymous(caller)msg_caller() != Principal::anonymous()awaitawaitpre_upgradepre_upgradeic-stable-structurespersistent actorletvar# 检查当前设置(主网)
icp canister settings show backend -e ic
# 为高价值Canister提高冻结阈值
icp canister settings update backend --freezing-threshold 7776000 -e ic # 90天icp canister settings update backend --add-controller <backup-principal> -e icfetchRootKey()fetchRootKey()fetchRootKey()fetchRootKey()try/finallyDropcall_on_cleanupawaitawaitfinallyDropawaitawaitfinallyDropshared(msg)init()import Principal "mo:core/Principal";
import Set "mo:core/pure/Set";
import Runtime "mo:core/Runtime";
shared(msg) persistent actor class MyCanister() {
// --- Authorization state ---
// transient: recomputed on each install/upgrade from msg.caller (the controller)
transient let owner = msg.caller;
var admins : Set.Set<Principal> = Set.empty();
// --- Guards ---
func requireAuthenticated(caller : Principal) {
if (Principal.isAnonymous(caller)) {
Runtime.trap("anonymous caller not allowed");
};
};
func requireOwner(caller : Principal) {
requireAuthenticated(caller);
if (caller != owner) {
Runtime.trap("caller is not the owner");
};
};
func requireAdmin(caller : Principal) {
requireAuthenticated(caller);
if (caller != owner and not Set.contains(admins, Principal.compare, caller)) {
Runtime.trap("caller is not an admin");
};
};
// --- Admin management ---
public shared ({ caller }) func addAdmin(newAdmin : Principal) : async () {
requireOwner(caller);
admins := Set.add(admins, Principal.compare, newAdmin);
};
public shared ({ caller }) func removeAdmin(admin : Principal) : async () {
requireOwner(caller);
admins := Set.remove(admins, Principal.compare, admin);
};
// --- Endpoints ---
public shared ({ caller }) func publicAction() : async Text {
requireAuthenticated(caller);
"ok";
};
public shared ({ caller }) func adminAction() : async () {
requireAdmin(caller);
// ... protected logic
};
};shared(msg)init()import Principal "mo:core/Principal";
import Set "mo:core/pure/Set";
import Runtime "mo:core/Runtime";
shared(msg) persistent actor class MyCanister() {
// --- 授权状态 ---
// 瞬态:每次安装/升级时从msg.caller(控制器)重新计算
transient let owner = msg.caller;
var admins : Set.Set<Principal> = Set.empty();
// --- 防护函数 ---
func requireAuthenticated(caller : Principal) {
if (Principal.isAnonymous(caller)) {
Runtime.trap("anonymous caller not allowed");
};
};
func requireOwner(caller : Principal) {
requireAuthenticated(caller);
if (caller != owner) {
Runtime.trap("caller is not the owner");
};
};
func requireAdmin(caller : Principal) {
requireAuthenticated(caller);
if (caller != owner and not Set.contains(admins, Principal.compare, caller)) {
Runtime.trap("caller is not an admin");
};
};
// --- 管理员管理 ---
public shared ({ caller }) func addAdmin(newAdmin : Principal) : async () {
requireOwner(caller);
admins := Set.add(admins, Principal.compare, newAdmin);
};
public shared ({ caller }) func removeAdmin(admin : Principal) : async () {
requireOwner(caller);
admins := Set.remove(admins, Principal.compare, admin);
};
// --- 端点方法 ---
public shared ({ caller }) func publicAction() : async Text {
requireAuthenticated(caller);
"ok";
};
public shared ({ caller }) func adminAction() : async () {
requireAdmin(caller);
// ... 受保护的逻辑
};
};finallycatchfinallyimport Map "mo:core/Map";
import Principal "mo:core/Principal";
import Error "mo:core/Error";
import Result "mo:core/Result";
// Inside the persistent actor class { ... }
// otherCanister is application-specific — replace with your canister reference.
let pendingRequests = Map.empty<Principal, Bool>();
func acquireGuard(principal : Principal) : Result.Result<(), Text> {
if (Map.get(pendingRequests, Principal.compare, principal) != null) {
return #err("already processing a request for this caller");
};
Map.add(pendingRequests, Principal.compare, principal, true);
#ok;
};
func releaseGuard(principal : Principal) {
ignore Map.delete(pendingRequests, Principal.compare, principal);
};
public shared ({ caller }) func doSomethingAsync() : async Result.Result<Text, Text> {
requireAuthenticated(caller);
// 1. Acquire per-caller lock — rejects concurrent calls from same principal
switch (acquireGuard(caller)) {
case (#err(msg)) { return #err(msg) };
case (#ok) {};
};
// 2. Make inter-canister call
try {
let result = await otherCanister.someMethod();
#ok(result)
} catch (e) {
#err("call failed: " # Error.message(e))
} finally {
// Runs in cleanup context even if the callback traps — changes here persist.
releaseGuard(caller);
};
};finallycatchfinallyimport Map "mo:core/Map";
import Principal "mo:core/Principal";
import Error "mo:core/Error";
import Result "mo:core/Result";
// 在persistent actor类内部 { ... }
// otherCanister为应用特定的Canister引用——替换为你的Canister引用。
let pendingRequests = Map.empty<Principal, Bool>();
func acquireGuard(principal : Principal) : Result.Result<(), Text> {
if (Map.get(pendingRequests, Principal.compare, principal) != null) {
return #err("already processing a request for this caller");
};
Map.add(pendingRequests, Principal.compare, principal, true);
#ok;
};
func releaseGuard(principal : Principal) {
ignore Map.delete(pendingRequests, Principal.compare, principal);
};
public shared ({ caller }) func doSomethingAsync() : async Result.Result<Text, Text> {
requireAuthenticated(caller);
// 1. 获取按调用者锁定——拒绝来自同一主体的并发调用
switch (acquireGuard(caller)) {
case (#err(msg)) { return #err(msg) };
case (#ok) {};
};
// 2. 发起跨Canister调用
try {
let result = await otherCanister.someMethod();
#ok(result)
} catch (e) {
#err("call failed: " # Error.message(e))
} finally {
// 即使回调触发陷阱也会在清理上下文中运行——此处的变更会保留。
releaseGuard(caller);
};
};// Inside persistent actor { ... }
// Method variants must match your public methods
system func inspect(
{
caller : Principal;
msg : {
#adminAction : () -> ();
#addAdmin : () -> Principal;
#removeAdmin : () -> Principal;
#publicAction : () -> ();
#doSomethingAsync : () -> ();
}
}
) : Bool {
switch (msg) {
// Admin methods: reject anonymous to save cycles on Candid decoding
case (#adminAction _) { not Principal.isAnonymous(caller) };
case (#addAdmin _) { not Principal.isAnonymous(caller) };
case (#removeAdmin _) { not Principal.isAnonymous(caller) };
case (#doSomethingAsync _) { not Principal.isAnonymous(caller) };
// Public methods: accept all
case (_) { true };
};
};// 在persistent actor内部 { ... }
// 方法变体必须与你的公共方法匹配
system func inspect(
{
caller : Principal;
msg : {
#adminAction : () -> ();
#addAdmin : () -> Principal;
#removeAdmin : () -> Principal;
#publicAction : () -> ();
#doSomethingAsync : () -> ();
}
}
) : Bool {
switch (msg) {
// 管理员方法:拒绝匿名调用以节省Candid解码的Cycle
case (#adminAction _) { not Principal.isAnonymous(caller) };
case (#addAdmin _) { not Principal.isAnonymous(caller) };
case (#removeAdmin _) { not Principal.isAnonymous(caller) };
case (#doSomethingAsync _) { not Principal.isAnonymous(caller) };
// 公共方法:接受所有调用
case (_) { true };
};
};guardErruse ic_cdk::{init, update};
use ic_cdk::api::msg_caller;
use candid::Principal;
use std::cell::RefCell;
thread_local! {
static OWNER: RefCell<Principal> = RefCell::new(Principal::anonymous());
static ADMINS: RefCell<Vec<Principal>> = RefCell::new(vec![]);
}
// --- Guards (for #[update(guard = "...")] attribute) ---
// Must return Result<(), String>. Err rejects the call.
fn require_authenticated() -> Result<(), String> {
if msg_caller() == Principal::anonymous() {
return Err("anonymous caller not allowed".to_string());
}
Ok(())
}
fn require_owner() -> Result<(), String> {
require_authenticated()?;
OWNER.with(|o| {
if msg_caller() != *o.borrow() {
return Err("caller is not the owner".to_string());
}
Ok(())
})
}
fn require_admin() -> Result<(), String> {
require_authenticated()?;
let caller = msg_caller();
let is_authorized = OWNER.with(|o| caller == *o.borrow())
|| ADMINS.with(|a| a.borrow().contains(&caller));
if !is_authorized {
return Err("caller is not an admin".to_string());
}
Ok(())
}
// --- Init ---
#[init]
fn init(owner: Principal) {
OWNER.with(|o| *o.borrow_mut() = owner);
}
// --- Endpoints ---
#[update(guard = "require_authenticated")]
fn public_action() -> String {
"ok".to_string()
}
#[update(guard = "require_admin")]
fn admin_action() {
// ... protected logic — guard already validated caller
}
#[update(guard = "require_owner")]
fn add_admin(new_admin: Principal) {
ADMINS.with(|a| a.borrow_mut().push(new_admin));
}
#[update(guard = "require_owner")]
fn remove_admin(admin: Principal) {
ADMINS.with(|a| a.borrow_mut().retain(|p| p != &admin));
}
ic_cdk::export_candid!();guardErruse ic_cdk::{init, update};
use ic_cdk::api::msg_caller;
use candid::Principal;
use std::cell::RefCell;
thread_local! {
static OWNER: RefCell<Principal> = RefCell::new(Principal::anonymous());
static ADMINS: RefCell<Vec<Principal>> = RefCell::new(vec![]);
}
// --- 防护函数(用于#[update(guard = "...")]属性) ---
// 必须返回Result<(), String>。Err会拒绝调用。
fn require_authenticated() -> Result<(), String> {
if msg_caller() == Principal::anonymous() {
return Err("anonymous caller not allowed".to_string());
}
Ok(())
}
fn require_owner() -> Result<(), String> {
require_authenticated()?;
OWNER.with(|o| {
if msg_caller() != *o.borrow() {
return Err("caller is not the owner".to_string());
}
Ok(())
})
}
fn require_admin() -> Result<(), String> {
require_authenticated()?;
let caller = msg_caller();
let is_authorized = OWNER.with(|o| caller == *o.borrow())
|| ADMINS.with(|a| a.borrow().contains(&caller));
if !is_authorized {
return Err("caller is not an admin".to_string());
}
Ok(())
}
// --- 初始化 ---
#[init]
fn init(owner: Principal) {
OWNER.with(|o| *o.borrow_mut() = owner);
}
// --- 端点方法 ---
#[update(guard = "require_authenticated")]
fn public_action() -> String {
"ok".to_string()
}
#[update(guard = "require_admin")]
fn admin_action() {
// ... 受保护的逻辑 —— 防护已验证调用者
}
#[update(guard = "require_owner")]
fn add_admin(new_admin: Principal) {
ADMINS.with(|a| a.borrow_mut().push(new_admin));
}
#[update(guard = "require_owner")]
fn remove_admin(admin: Principal) {
ADMINS.with(|a| a.borrow_mut().retain(|p| p != &admin));
}
ic_cdk::export_candid!();CallerGuardDroplet _ = CallerGuard::new(caller)?use std::cell::RefCell;
use std::collections::BTreeSet;
use candid::Principal;
use ic_cdk::update;
use ic_cdk::api::msg_caller;
use ic_cdk::call::Call;
// other_canister_id is application-specific — replace with your canister reference.
thread_local! {
static PENDING: RefCell<BTreeSet<Principal>> = RefCell::new(BTreeSet::new());
}
struct CallerGuard {
principal: Principal,
}
impl CallerGuard {
fn new(principal: Principal) -> Result<Self, String> {
PENDING.with(|p| {
if !p.borrow_mut().insert(principal) {
return Err("already processing a request for this caller".to_string());
}
Ok(Self { principal })
})
}
}
impl Drop for CallerGuard {
fn drop(&mut self) {
PENDING.with(|p| {
p.borrow_mut().remove(&self.principal);
});
}
}
#[update]
async fn do_something_async() -> Result<String, String> {
let caller = msg_caller();
if caller == Principal::anonymous() {
return Err("anonymous caller not allowed".to_string());
}
// Acquire per-caller lock — rejects concurrent calls from same principal.
// Drop releases lock even if callback traps.
let _guard = CallerGuard::new(caller)?;
// Make inter-canister call
let response = Call::bounded_wait(other_canister_id(), "some_method")
.await
.map_err(|e| format!("call failed: {:?}", e))?;
let result: String = response.candid()
.map_err(|e| format!("decode failed: {:?}", e))?;
Ok(result)
// _guard dropped here → lock released
}CallerGuardDroplet _ = CallerGuard::new(caller)?use std::cell::RefCell;
use std::collections::BTreeSet;
use candid::Principal;
use ic_cdk::update;
use ic_cdk::api::msg_caller;
use ic_cdk::call::Call;
// other_canister_id为应用特定的Canister引用——替换为你的Canister引用。
thread_local! {
static PENDING: RefCell<BTreeSet<Principal>> = RefCell::new(BTreeSet::new());
}
struct CallerGuard {
principal: Principal,
}
impl CallerGuard {
fn new(principal: Principal) -> Result<Self, String> {
PENDING.with(|p| {
if !p.borrow_mut().insert(principal) {
return Err("already processing a request for this caller".to_string());
}
Ok(Self { principal })
})
}
}
impl Drop for CallerGuard {
fn drop(&mut self) {
PENDING.with(|p| {
p.borrow_mut().remove(&self.principal);
});
}
}
#[update]
async fn do_something_async() -> Result<String, String> {
let caller = msg_caller();
if caller == Principal::anonymous() {
return Err("anonymous caller not allowed".to_string());
}
// 获取按调用者锁定——拒绝来自同一主体的并发调用。
// 即使回调触发陷阱,Drop也会释放锁。
let _guard = CallerGuard::new(caller)?;
// 发起跨Canister调用
let response = Call::bounded_wait(other_canister_id(), "some_method")
.await
.map_err(|e| format!("call failed: {:?}", e))?;
let result: String = response.candid()
.map_err(|e| format!("decode failed: {:?}", e))?;
Ok(result)
// _guard在此处被丢弃 → 锁释放
}use ic_cdk::api::{accept_message, msg_caller, msg_method_name};
use candid::Principal;
/// Pre-filter to reduce cycle waste from spam.
/// Runs on ONE node. Can be bypassed. NOT a security check.
/// Always duplicate real access control inside each method or via guard attribute.
#[ic_cdk::inspect_message]
fn inspect_message() {
let method = msg_method_name();
match method.as_str() {
// Admin methods: only accept from non-anonymous callers
"admin_action" | "add_admin" | "remove_admin" | "do_something_async" => {
if msg_caller() != Principal::anonymous() {
accept_message();
}
// Silently reject anonymous — saves cycles on Candid decoding
}
// Public methods: accept all
_ => accept_message(),
}
}use ic_cdk::api::{accept_message, msg_caller, msg_method_name};
use candid::Principal;
/// 预过滤以减少垃圾消息的Cycle浪费。
/// 在单个节点上运行。可以被绕过。不是安全检查。
/// 始终在每个方法内部或通过guard属性重复实现真正的访问控制。
#[ic_cdk::inspect_message]
fn inspect_message() {
let method = msg_method_name();
match method.as_str() {
// 管理员方法:仅接受非匿名调用者
"admin_action" | "add_admin" | "remove_admin" | "do_something_async" => {
if msg_caller() != Principal::anonymous() {
accept_message();
}
// 静默拒绝匿名调用——节省Candid解码的Cycle
}
// 公共方法:接受所有调用
_ => accept_message(),
}
}