写点什么

Substrate 合约书之合约语言框架

用户头像
Patract
关注
发布于: 2021 年 04 月 26 日
Substrate 合约书之合约语言框架

前言


「Substrate 合约书」用于介绍 Substrate 中与 Wasm 合约体系相关的一系列知识。本书由 Patract (https://patract.io/)主导编写, Aten 负责。本书仓库位于 https://github.com/patractlabs/substrate-contracts-book,欢迎有志之士一起为本书做出贡献。


本书当前主要以 Pallet-Contracts (即 Wasm 合约)作为主体进行介绍。因此本书内容包含:


  • 运行合约的合约平台(模块)

    Pallet-Contracts

  • 编写合约的语言

    ink!

    Ask!

    Solang

  • 帮助合约开发的工具们

    Redspot

    Europa

    Elara


其中由于 Pallet-EVM(即 EVM/Solidity 体系的合约)在以太坊生态中已有比较丰富的资料,故不会在本合约书中当做重点讲解;而另外的合约体系如 Pallet-Actor,或 Libra 移植合约平台等皆处于比较早期的研究阶段,因此也不会作为本书的重点。

1.合约语言框架


与“合约模型”的概念相对应的运行平台,我们可以衍生出合约语言框架的概念,即编写能运行于平台上的代码。


合约语言框架: 是对能编写运行于合约模型的平台上的语言或者框架的总称。例如 ink! ,它是基于 Rust 语言编写的 eDSL 框架,赋予 Rust 可以写合约的特性。而 Solidity 是合约语言,原因是为了合约而被创建的语言。


上一章节中我们已经拆分出了“合约沙盒”与“合约模型”的结构层次,实际上一些新型的合约平台并不像 EVM “发明”了一套新的 Solidity 合约语言,而是选择一些语言(如 Rust、C++、AssemblyScript 等),基于这门语言在“hack 语法解析”,“宏”等维度上给现有语言添加上合约相关的约束、功能逻辑等。因此许多情况下会以“框架”、“库”等形态存在,如 Frame Contract Pallet 的 Rust 合约语言框架 ink!。而这种“框架、库”并不是平时程序开发中使用库的维度,有一些合约语言框架需要修改到编译器维度,因此我们更倾向称呼为“语言框架”。

2.合约语言(框架)与合约模型的对应关系


如上图所示,上半部分是 EVM 与 Solidity 之间的关系。由于 EVM/Solidity 提出的时间比较早,因此其模型与通常的计算机虚拟机与语言的模型关系是一致的而下半部分是在分离了合约模型后,语言框架部分的对应关系。这里重点介绍下半部分,上半部分读者可以根据自己在以太坊合约开发的经验对比后文的介绍进行分析。


在编译形语言的体系中,简单定义以下名词(非严谨说法,仅为了后文介绍而定义):


  • S 语言:程序员编写代码的原语言(Source),例如将 C++ 编译成汇编,那么 C++ 就是原语言 S。

  • T 语言:S 语言通过编译器编译后生产的语言(Target),例如将 C++ 编译成汇编,那么汇编就是目标语言 T。


前文已经介绍了运行合约体系的环境,可以在逻辑上拆分为“合约模型”与“合约虚拟机”。前者管理合约的业务逻辑,后者管理如何运行合约的代码。相对应的,合约语言部分同样可以拆解为“合约语言框架”和“编译到合约虚拟机的 S 语言”。


根据这种定义,Solidity 显然是一种同时具备“合约语言框架”和“S 语言”特性的产物:


1.Solidity 具备图灵完备的语言体系,因此 Solidity 是一门“语言”(相对于 BTC 脚本而言)。

2.Solidity 的语法中具备很多合约特性的关键字,对应于“合约语言框架”的概念,如:


这些概念并非是为了支持 Solidity 这门语言能执行逻辑而存在的,而是为了服务于 Ethereum 合约虚拟机的业务逻辑而存在的。


  • mapping:合约存储的典型案例

  • msg.sender, msg.value:等与合约调用相关的变量

  • view,pure 等修饰符

  • call,delegate_call 等与合约调用相关关键字


而合约语言框架实现目的是为了与合约模型的业务逻辑所对应,什么样的合约模型就需要什么样的合约语言框架。


  • EVM 底层是 k/V 类型的存储,因此 Solidity 中设计的 Mapping 不能遍历,除非附带多余存储。

  • EVM 合约交互的合约模型设计为合约调用合约的模式,那么 Solidity 提供了 call,delegate_call。对应到其他合约体系也同理。


另一方面,合约语言框架是架设到 S 语言上的附加功能,因此合约语言框架提供的功能最终也是编译到 S 语言对应的 T 语言上。而区块链所需要的一些特性,如执行确定性的要求、不允许调用操作系统的接口等,会在这个层面上设置约束。因此合约语言框架除了本身对 S 语言提供了合约模型的功能以外,还需要对语言产生一定的约束。这一步也是开发和理解合约语言框架中比较困难的地方,也正如此,造成了合约开发者使用合约模型框架的过程中很多别扭的情况。


Solidity 或者 Move 等语言就是针对合约平台开发的新语言,纵使其语法有很多模拟其他语言的痕迹,但是在做约束的这点上可以做到对合约开发者比较友好。而对现有语言加上合约语言框架功能的合约语言,在这一点上很难处理的很好,而这也是造成合约开发者较难使用框架功能的原因之一。


以 ink! 举例而言:


1.在区块链中应该避免使用 float,因为浮点数可能产生非确定性行为。因此在合约/Runtime 开发中,若需要使用浮点数,或者出现溢出数字乘除的情况,需要引入定点数来处理。因此在实现上, ink! 的合约可以引入 Substrate Runtime 提供的定点数的库来处理。

2.由于 pallet-contracts 的合约模型与 EVM 基本相同,因此 pallet-contracts 的合约存储也是由 K/V 构成,那么合约模型框架需要处理标准库里提供的各种集合类型(例如 Vec, Map 等)。因此 ink! 将标准库中可能用到的集合类型重写了一遍,添加了能将集合元素类型处理成 K/V 数据的过程。在 ink! 的合约存储中,若涉及集合类型,只能使用 ink! 标准库提供的类型。


而另一方面由于 ink! 的返回值需要导出 Metadata 令第三方处理,而当前的 Metadata 的接口只实现给了标准库中的集合,因此 ink! 方法的返回值的集合只能使用标准库的集合类型。便出现以下代码:



#[ink::contract]mod test { // 引入 ink 实现的 Vec use ink_storage::collections::Vec as StorageVec; // 引入标准库的Vec use ink_prelude::vec::Vec; #[ink(storage)] pub struct Test { owners: StorageVec<AccountId>, // 只能使用 ink的Vec } impl Test { #[ink(message)] pub fn get_owners(&self) -> Vec<AccountId> { // 将 ink 实现的 Vec 转换为 标准库实现的 Vec self.owners.iter().map(Clone::clone).collect() } }}
复制代码


因此总结而言,在模型结构上,开发者需要理解到:


  • 合约语言框架与合约模型是对应关系,语言框架的特性会与模型一一匹配。


  • 合约语言框架对 S 语言提供了针对合约模型的业务功能,同时也根据合约业务逻辑的需求对 S 语言有约束。


上面已经提到 Solidity 是为写合约而设计的特性,因此许多与合约相关的功能可以设计为关键字。若一门语言并不是针对合约被设计,则需要基于这门合约设计对应于合约模型的“合约语言框架”。在这门语言上添加新的功能,因此一般情况下比较难以深入结合语言。因此最后做到的成果与这门语言提供的能拓展语法树的能力相关。


如果一门语言提供修改、添加语法树的接口(宏、插件等)越灵活,合约语言框架更多功能更容易实现。如果语言提供这类可扩展性的功能越少,合约语言框架只能考虑修改编译器,扩展需要的语法支持合约模型,这会导致该合约语言框架变成原语言的一种“方言”。


因此前者可以以库、框架的形态存在,后者则成为了一门新的语言,这也是我们将这种模型称为“合约语言框架”的原因。

3.pallet-contracts 与对应的合约语言框架


上文已经解释清楚合约语言框架的模型,接下来我们可以把合约模型框架套在 Substrate 的 Wasm 合约系统上。


显然,ink! 这整套系统的实现,是与 pallet-contracts 的合约模型相对应的。ink! 3.0 通过过程宏(2.0 通过声明宏)的系统,将对应于 pallet-contracts 的功能逻辑引入到了 Rust 当中。因此这套系统里的 S 语言就是 Rust,而 T 语言就是 Wasm 字节码。ink! 在一个辅助工具 cargo-contract 的帮助下,将使用了 ink! 框架的 Rust 代码编译成为了合约的 Wasm 字节码。而 Wasm 字节码在链上运行的环境就是 wasmi,将来也会引入 Wasmtime 等 JIT 形式的执行环境。


而由于 pallet-contracts 的执行环境是 Wasm,因此能够编译成 Wasm 字节码的语言配套符合 pallet-contracts 合约模型的合约语言框架,都可以编写能运行在 pallet-contracts 合约平台上的合约。所以对于 pallet-contracts 而言,完全可以设计不同语言的合约体系,为开发者开发 Wasm 合约提供多种选择。

当前支持运行 pallet-contracts 的合约语言框架除了 ink! 之外,还有以下项目:


  • Ask!,由 Patract 主导开发,是在 AssemblyScript 语言上的合约语言框架(当前正在开发中)。

  • Solang,由 hyperledger-labs 主导开发,支持将 Solidity 编译到 pallet-contracts 的 Wasm 的工具。


注解:

Aten 的 GitHub:https://github.com/atenjin

About Patract

Patract 为波卡 Wasm 合约生态的平行链和 DApp 开发提供解决方案。我们帮助社区平行链设计和开发链上合约模块和 Runtime 支持,并且为 DApp 开发者提供覆盖开发、测试、调试、部署、监控、数据提供和前端开发等阶段的全栈工具和服务支持。

How to join Patract

1.对于合约开发者,可以访问官网 (https://patract.io),熟悉测试链和工具套件。欢迎加入官方开发群:

Elementhttps://app.element.io/#/room/#PatractLabsDev:matrix.org

Discordhttps://discord.gg/wJ8TnTfjcq

 

2.对于将要集成 Wasm 合约功能的平行链项目方,或者使用 Wasm 合约开发的 DApp 项目方,商务合作欢迎联系 santry@patract.io

 

3.对于用户,欢迎加入:

Telegramhttps://t.me/patract

Twitterhttps://twitter.com/PatractLabs

欢迎关注“Patract 开放平台”官方公众号

 

4.对于求职者,我们在招聘区块链开发工程师、前端/全栈开发工程师、产品经理、开发者运营等岗位,可以联系 sean@patract.io

用户头像

Patract

关注

还未添加个人签名 2021.03.12 加入

Patract的使命是加速智能合约行业向Wasm技术栈的转变。

评论

发布
暂无评论
Substrate 合约书之合约语言框架