#smart-contracts #standard #concordium #blockchain #testing #interface

no-std concordium-std

为Concordium区块链在Rust语言中编写智能合约的标准库

20个版本 (10个主要更新)

10.1.0 2024年4月4日
10.0.0 2024年2月22日
9.0.2 2024年2月7日
8.1.0 2023年10月18日
0.3.1 2020年12月21日

#1008 in 魔法豆

Download history 284/week @ 2024-04-16 1710/week @ 2024-04-23 211/week @ 2024-04-30 51/week @ 2024-05-07 65/week @ 2024-05-14 101/week @ 2024-05-21 115/week @ 2024-05-28 145/week @ 2024-06-04 1033/week @ 2024-06-11 416/week @ 2024-06-18 85/week @ 2024-06-25 45/week @ 2024-07-02 75/week @ 2024-07-09 710/week @ 2024-07-16 246/week @ 2024-07-23 185/week @ 2024-07-30

每月1,222次下载
用于concordium-cis2

MPL-2.0 许可证

460KB
7K SLoC

为在Rust编程语言中编写Concordium区块链智能合约提供高级接口的标准库。

链接

维护者使用

库的高级设计

库旨在提供易于使用的API来使用链API,同时确保用户不会出错。库暴露了两种类型的API。高级API牺牲了一些性能和代码大小,但几乎允许用户假装合约状态是内存状态,而不是实际的外部数据库。低级API以Rust-like方式暴露了链提供的API,这样用户就不必直接处理指针。

高级API的设计广泛使用了Rust功能来防止错误,并假设不会同时使用低级API。也就是说,高级API维护了可能会被低级API破坏的不变量。这些不变量通过封装(即隐藏实现细节)和类型系统来维护,包括广泛使用生命周期。

低级API暴露了一个键值存储,这也是链暴露的接口。键是任意字节数组,值也是字节数组。该API以条目为中心构建。当查找键时,返回一个条目。然后可以使用Read和Write特性来读取和写入此条目。

高级API以“分配器”为中心构建。它暴露了三个高级集合

  • 一个映射
  • 一个集合
  • 一个“盒子”,即一个间接值。

支持嵌套映射,以及集合作为映射中的值。

高级API不是直接与字节数组交互,而是与正常的Rust结构化类型交互。这些类型到字节数组的序列化(对于键和值)是自动处理的。API保持一个不变性,即如果存储了类型T的值,则可以在以后检索并反序列化。由于低级API可以用于在任意键上写入任意数据,这可能会违反这一不变性。

高级API维护的第二个不变性是,在映射上有活动迭代器时,不能修改映射的结构(即不允许插入或删除)。这是通过生命周期和Rust中可变引用的唯一性实现的,可变引用不能与不可变引用(指向同一对象,有关生命周期传递的详细信息见)共存,类似于标准Rust集合,例如BTreeMap。由于节点API暴露的API不允许修改正在迭代的树的部分,因此这种限制是必要的。

这些不变性使得在代码的许多地方,如果在操作可能会失败但由不变性保证的情况下,我们中断程序。例如,当在映射中查找值时,我们假设反序列化不会失败。

另一个值得注意的项目是使用Drop来确保数据写入合约状态。具体案例是条目的修改。例如,当我们查找映射中的条目时,我们可能得到一个对条目内部存储的值的&mut引用。然后可以使用它来修改值。但是所有这些都是在内存中发生的,并在某个时候必须写入实际的合约状态。一个选项是要求用户使用诸如commit之类的函数来完成此操作,但这非常容易出错,尤其是在API看起来非常接近正常集合API的情况下。因此,我们使用Drop实现来将更改提交到合约状态。有一些优化,以避免在明确没有更改的情况下写入合约状态。但这不是非常精确(它是一个过度近似,即它经常将其视为已修改),因此,如果不想写入状态,通常不应该获取&mut引用。

这种使用Drop的做法依赖于上面列出的第二个不变性,即由于生命周期限制,不允许重叠修改映射。如果不是这样,我们可能会面临不一致的状态视图,内存中的值与查找的值不同。

高级API布局

高级状态的根本存储在空键中,即 &[]。这通常是一个Rust结构体或其它某种结构化类型。在其内部可能有映射、集合或盒子。这些通过 分配器 分配。分配器决定存储映射的下一个空闲位置。分配器自身必须存储在合约状态中,并存储在位置 [0,0,0,0,0,0,0]。该位置的存储值是一个64位整数,被解释为位置(以小端字节计)。创建新的映射会查找分配器值,并更新它。

高级API依赖于它是唯一一个更新此分配器的API。 通过其他方法修改值将导致不可预测的结果。

因此,每个映射(和集合)都与一个位置 相关联,这是一个64位整数。映射中的键值对(K,V)随后存储在由 (小端)与 K 的序列化组合而成的键中(在合约状态中)。

依赖关系

~1.6–3.5MB
~58K SLoC