#单元测试 #互联网计算机 #ICP #智能合约 #区块链

rustic

互联网计算机上构建canister的库

11个版本

0.1.9 2024年3月13日
0.1.8 2024年1月16日
0.1.7 2023年11月16日
0.1.6 2023年9月18日
0.0.0 2017年4月7日

#85 in 魔法豆

MIT/Apache

77KB
1.5K SLoC

Rustic框架

Rustic是一个用于在互联网计算机上开发canister的框架。

设计目标

  • 简洁性。在大多数情况下,开发者不需要了解所有实现细节。
  • 遵循最佳实践。这个库应该结合最新的canister开发知识。
  • OpenZeppelin风格。应该实现其他链(如ETH)的大部分使用模式。

特性

  • 访问:访问控制等同于OpenZeppelin Ownable2Step + 管理员列表
  • 访问-角色:访问控制等同于OpenZeppelin AccessControl
  • 审计事件:审计用事件
  • 备份:备份数据
  • 缓存:将频繁读取的数据缓存到堆中以提高性能
  • 认证:认证查询
  • 工厂:canister工厂
  • https:具有指标等的canister的https接口
  • 检查:更新方法的循环直方图
  • 生命周期:canister生命周期管理
  • 日志:canister在堆中的日志
  • 稳定-日志:在稳定内存中的canister日志
  • 暂停:等同于OpenZeppelin Pausable
  • 支付:支付辅助工具
  • 重入性:等同于OpenZeppelin ReentrancyGuard
  • 测试:单元测试辅助工具
  • 代币:可替换和非可替换代币

用法

开始之前

设置环境变量 RUSTIC_USER_PAGE_END。此值不应在升级过程中更改!一旦设置了用户页面范围,就永远不能更改!确保为未来的升级留下足够的空间。要合理,不要设置值过高,因为你即使为空页面范围支付存储费用。

基本用法

查看示例。

稳定内存

互联网计算机的canister有4GB的堆内存,在canister升级期间会被清除,另外还有96GB的稳定内存,可以在canister升级期间保留。出于健壮性和可升级性的原因,我们应该几乎总是优先选择稳定内存。

rustic 框架提供了一个简单的方式来使用稳定内存,通过 ic-stable-structures 包。Rustic 使用以下内存映射

  • 前 64 页被 rustic 预留用于使用。
  • 用户页从 USER_PAGE_START 开始,即第 64 页。
  • 用户可以在页面上存储具有已知有限大小的数据结构(即不能无限增长)直到 RUSTIC_USER_PAGE_END,它在环境变量中定义。
  • 剩余的内存由 MEMORY_MANAGER 管理,可用于存储无界数据结构(例如 StableBTreeMapStableVec)。
  • MEMORY_MANAGER 可以使用 MemoryId 动态分配内存。每个数据结构的 MemoryId 必须是唯一的。用户可用的 MemoryId 范围是 [0,223],而 [224,255] 范围被 rustic 预留。

初始化和升级后钩子

该模块必须在主应用程序的初始化钩子中进行初始化。在所有其他内容之前必须首先初始化 rustic 模块。

# use ic_cdk::init;
#[init]
pub fn init () {
    rustic::rustic_init();

    // init code for your canister
}

该模块有一个升级后方法,你必须在升级后钩子中调用。

# use ic_cdk::post_upgrade;
#[post_upgrade]
pub fn post_upgrade () {
    rustic::rustic_post_upgrade(false, true, false);

    // post upgrade code for your canister
}

导出 Candid

export-candid 允许使用在 ic-cdk v0.11 中引入的机制进行 candid 导出。然而,由于此机制的工作方式,candid 需要导出两次,一次是在你的应用程序中,一次是从 rustic 库中。

要从 rustic 导出 candid,使用带有 export-candid 功能的 rustic,并在你的主 canister 中注释掉 ic_cdk::export_candid!() 以避免冲突。然后生成一次 wasm,并使用 candid-extractor 提取 candid。

然后从你的主应用程序导出 candid,禁用 export-candid 功能,将 ic_cdk::export_candid!() 添加到你的主 canister 中,重新编译并再次提取 candid。

手动合并这两个 candid 文件,以获取你的 canister 的最终 candid。

生命周期

当使用 lifecycle 功能(默认启用)时,在新 canister 的升级后钩子中,通过调用 rustic::rustic_post_upgrade 来调用 lifecycle_on_upgrade 方法。对于 semver,你需要指定你是否想要进行主要/次要/补丁级别版本的提升。如果稳定内存布局发生了变化(确保你测试了兼容性,因为这不是 rustic 检查的),则提升稳定内存版本。如果你提升主要版本,则次要/补丁级别将被忽略,并将从 0 开始。

注意事项

永远不要在你的应用程序中使用预升级钩子。这个特性应该被认为是已弃用的。

注意事项

  1. 在单元测试期间不执行更新保护(或从 canister 内部的任何调用)。此行为与 Solidity 修改器保护不同。以下代码展开为
    # use candid::Principal;
    # use rustic::access_control::only_owner;
    // This exported function contains the guard
    // #[export_name = "canister_update transfer_ownership"]
    fn transfer_ownership_0_() {
        ic_cdk::setup();
        let r: Result<(), String> = only_owner();
        if let Err(e) = r {
            ic_cdk::api::call::reject(&e);
            return;
        }
        ic_cdk::spawn(async {
            let (new_owner,) = ic_cdk::api::call::arg_data(ic_cdk::api::call::ArgDecoderConfig {
                decoding_quota: None,
                skipping_quota: Some(10000usize),
                debug: false,
            });
            let result = transfer_ownership(new_owner);
            ic_cdk::api::call::reply(())
        });
    }
    // This internal function does not contain the guard
    pub fn transfer_ownership(new_owner: Option<Principal>) {
        // implemantation of transfer_ownership
    }

为了使守卫同时适用于内部和外部调用,rustic-macros 包包含一个名为 modifiers 的宏,该宏同时适用于内部和外部调用。

  1. 访问控制仅在应用级别。请注意,还有一个系统级别的 controller,它可以执行可以器升级。

已知问题

许可证

MIT

依赖关系

~3.5–5.5MB
~99K SLoC