5 个稳定版本

22.0.1 2023 年 10 月 5 日
22.0.0 2023 年 9 月 29 日
21.0.3 2023 年 9 月 4 日
21.0.1 2023 年 9 月 1 日

#1818 in 神奇豆子

Download history 124/week @ 2024-03-15 81/week @ 2024-03-22 80/week @ 2024-03-29 52/week @ 2024-04-05 124/week @ 2024-04-12 96/week @ 2024-04-19 91/week @ 2024-04-26 52/week @ 2024-05-03 112/week @ 2024-05-10 110/week @ 2024-05-17 53/week @ 2024-05-24 41/week @ 2024-05-31 31/week @ 2024-06-07 59/week @ 2024-06-14 60/week @ 2024-06-21 26/week @ 2024-06-28

182 每月下载量
用于 5 个crate(直接使用2个)

Apache-2.0

1MB
22K SLoC

合约模块

合约模块为运行时提供部署和执行 WebAssembly 智能合约的功能。

概述

本模块扩展了基于 frame_support::traits::fungible 特性的账户,以实现智能合约功能。它可以与基于 frame_support::traits::fungible 的其他模块一起使用。这些“智能合约账户”具有实例化智能合约和调用其他合约和非合约账户的能力。

智能合约代码存储一次,以后可以通过其 code_hash 恢复。这意味着可以从相同的 code 实例化多个智能合约,而无需每次都复制代码。

当调用智能合约时,通过代码哈希检索其相关代码并执行。此调用可以更改智能合约账户的存储条目,实例化新的智能合约或调用其他智能合约。

最后,当账户被回收时,其相关代码和智能合约账户的存储也将被删除。

权重

发送者必须为每次调用指定一个 Weight 限制,因为所有由智能合约调用的指令都需要权重。调用后未使用的权重将被退还,无论执行结果如何。

如果达到权重限制,那么所有调用和状态变更(包括余额转移)仅在当前调用合约级别被回滚。例如,如果合约A调用B,而B在调用中途耗尽权重,那么B的所有调用都将被回滚。假设合约A正确处理错误,A的其他调用和状态变更仍然持续。

一个 ref_time Weight 定义为在运行时参考机器上的一个皮秒执行时间。

回滚行为

合约调用失败不会级联。当子调用中发生失败时,它们不会“冒泡”,调用仅在特定合约级别回滚。例如,如果合约A调用合约B,而B失败,A可以决定如何处理该失败,要么继续执行,要么回滚A的变更。

链外执行

一般来说,合约执行需要是确定的,以便所有节点在执行时得出相同的结论。为此,我们禁止任何可能引起不确定性的指令。最显著的是任何浮点运算。话虽如此,有时合约在链外执行,因此不受共识约束。如果代码仅由单个节点执行,并且被其他参与者隐式信任,则属于此类情况。可信任的执行环境就属于这种情况。为此,我们允许在以下约束下执行不确定代码以用于链外使用

  1. 任何合约都不能从不确定代码中实例化。执行代码的唯一方法是使用来自确定性合约的代理调用。
  2. 希望使用此功能的代码需要依赖于 pallet-contracts 并直接使用 bare_call()。这确保默认情况下 pallet-contracts 不暴露任何不确定性。

如何使用

可以通过将 Determinism::Relaxed 传递给 upload_code() 将不确定代码部署到链上。确定性合约可以委托调用到它,如果且仅当它使用 bare_call() 并传递 Determinism::Relaxed 给它时。**当合约由链上事务调用时,切勿使用此参数**。

接口

可调用函数

这些在 参考文档 中有记载。

合约公开的接口

每个合约都是一个类似于这样的 WebAssembly 模块

(module
    ;; Invoked by pallet-contracts when a contract is instantiated.
    ;; No arguments and empty return type.
    (func (export "deploy"))

    ;; Invoked by pallet-contracts when a contract is called.
    ;; No arguments and empty return type.
    (func (export "call"))

    ;; If a contract uses memory it must be imported. Memory is optional.
    ;; The maximum allowed memory size depends on the pallet-contracts configuration.
    (import "env" "memory" (memory 1 1))

    ;; This is one of many functions that can be imported and is implemented by pallet-contracts.
    ;; This function is used to copy the result buffer and flags back to the caller.
    (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
)

所有可导入函数的文档可以在 这里 找到。

用法

此模块执行WebAssembly智能合约。这些合约可以用任何编译为Wasm的语言编写。然而,使用专门针对此模块的语言将使事情变得更容易。其中一种语言是ink!。它允许使用Rust编程语言编写基于WebAssembly的智能合约。

调试

合约可以通过debug_message API向客户端发送消息。这在ink!中通过ink_env::debug_message()暴露。

这些消息被收集到一个内部缓冲区中,并发送到RPC客户端。是否以及如何向用户展示这些消息取决于各个客户端。

此缓冲区也作为调试消息打印出来。为了在节点控制台上看到这些消息,需要将runtime::contracts目标的日志级别提高到至少debug级别。然而,由于区块生产的噪声,这些消息很容易被忽视。在控制台上观察它们的良好起点是在Substrate存储库的根目录中使用以下命令行

cargo run --release -- --dev -lerror,runtime::contracts=debug

这会将runtime::contracts的日志级别提高到debug,并将所有其他目标的日志级别提高到error,以防止它们在控制台上进行垃圾邮件。

--dev:使用开发链规范--tmp:使用临时存储链数据(退出时删除链状态)

宿主函数跟踪

对于合约作者来说,这是一个有用的调试工具,可以查看哪些宿主函数被调用,使用了哪些参数,以及结果如何。

为了在节点控制台上看到这些消息,需要将runtime::contracts::strace目标的日志级别提高到trace级别。

示例

cargo run --release -- --dev -lerror,runtime::contracts::strace=trace,runtime::contracts=debug

不稳定接口

受希望在开发新合约接口时采用迭代方法的需求驱动,本模块包含不稳定接口的概念。类似于Rust夜间编译器,它允许我们添加新接口,但将其标记为不稳定,以便合约语言可以在我们稳定它们之前进行实验并给出反馈。

为了访问在runtime.rs中标记为#[unstable]的接口,需要将pallet_contracts::Config::UnsafeUnstableInterface设置为ConstU32<true>。**很明显,任何生产运行时都不应该使用此功能:除了可能发生变化或删除之外,这些接口可能没有适当的权重,因此被视为不安全**。

新接口通常作为不稳定接口添加,可能需要经过几个迭代才能晋升为稳定接口。

许可证:Apache-2.0

依赖项

~21–42MB
~665K SLoC