5月18日,Patract Labs 的CTO,Aten 受邀做客 Parity 官方组织的社区活动 Substrate Seminar,详细讲解了如何使用 Patract 全栈工具开发波卡 Wasm 合约。实操内容请看详细教程。

Patract Labs 自联合波卡生态项目率先发起 Open Platform,已有超过15家项目加入 Patract Open Platform。他们或融合 Wasm 开发工具和去中心化存储网络来支持 Web3 开发者轻松部署自己的 DApp 和网站;或使用 Redspot 合约开发脚手架进行保密智能合约的整合;或支持开发者使用 Europa 沙盒进行项目的节点交互的模拟;或使用 Ask! 合约语言及开发工具部署 Wasm 合约。

在本次全球瞩目、超过2,000多人同时在线观看的 Substrate Seminar 技术分享中,Aten 老师从 Patract Labs 所有产品的设计思路,以及 Patract 的产品是如何工作的等方面进行了细致的解说。以下为主要内容:

大家好,我们是 Patract Labs,我是团队的技术负责人,Aten。在 Polkadot 生态系统中,分享我们在 Wasm 方面所做的工作是非常荣幸的。Patract Labs 是一个技术实验室,在 Polkadot 生态里构建 Wasm 智能合约,我们专注于为 Pallet-Contracts,ink!以及其它相关技术提供支持 。

使命

正如 Patract Labs 官网写的标题:Patract 的使命是加速智能合约行业向Wasm技术栈的转变。Polkadot 是一个伟大的平台,它给开发者提供了无限的想象空间,让人们可以构建出自己的区块链。区块链领域中很多业务场景在合约的概念下能够更好的实现,而且合约的可组合性更强,因此在Polkadot 平台上的合约平台会具备更强的活跃度。

Parity 的主要工作在于 Substrate 和 Polkadot,因此在合约的部分工作相对而言没有很多。而在 Wasm 合约部分,合约开发者距离合约平台之间有较大的鸿沟,因此 Patract Labs 的工作是成为连接合约开发者与合约平台之间的桥梁。
以太坊花了5、6年的时间,逐渐构建起了丰富的合约开发者生态,一个新的合约开发者进入以太坊,它有丰富的工具,丰富的案例去实践,模仿与创造。而对于 Polkadot 的Wasm 合约生态,这块东西基本是空白的。这就是我们 Patract 的工作,我们有自信在更短的时间内为 Polkadot 的 Wasm 合约开发者打造更完备的合约开发工具链。

Truffle Suite

如图,这是 Truffle Suite 官网(https://www.trufflesuite.com/),Truffle 在以太坊中的地位大家都知道。我们可以看到 Truffle Suite 的工具是围绕着计划、编译、构建、发布的开发流程设计,因此出现了 Truffle,Ganache 等产品。这些产品互相协作,围绕着合约开发的工作流给予开发者帮助。

Patract 的12条产品线

Patract 的产品设计思路同样参照 Truffle:为了提供一个最基本的节点环境,我们启动了一条测试链 Jupiter,在这个环境中使用 zkMega 模块提供了部分零知识的功能。

对于开发者,我们围绕着开发、调试、测试、部署的开发流:

  • Redspot 是类似 Truffle 的产品,是一个让合约开发项目化的工具。其覆盖开发、调试等合约开发的整个流程。
  • Europa 是类似 Ganache 的产品,是一个提供合约运行环境的模拟节点,且它在运行过程中记录了很多合约执行中的信息。
  • Metis 是类似 OpenZepplin Contracts 的项目,目的是为了给开发者生态树立模板合约的编写方式。
  • Ask! 是我们正在开发的,基于 AssemblyScript 的合约语言。它与 ink!的定位一致用于编写 Wasm 合约,不过由于其是基于 AssemblyScript 也就是 TypeScript 的变种,因此可能会更受开发者欢迎。
  • Himalia 是针对合约设计的与链进行交互的多种语言的 SDK 的总称,类似 Web3.js 等,当前 Polkadot.js 是由 Parity 官方设计,但是其它语言比较缺乏,例如 Go,Java 等,Himalia 的这一类库就是为此服务,这个项目目前还在开发当中。
  • Leda 是反馈链上的合约信息的监控设施,例如当前合约的 rent 机制会导致合约被销毁,因此需要有监控链上数据的设施。该项目当前只在计划当中,因为 ink!和 pallet-contracts 都还没有成熟,不能广泛用于生产环境中。
  • Carpo 是将开发者端的所有产品都集合成为服务的 WebIDE,将会给开发者提供一站式的合约开发过程。Ethereum 中有 Remix ,但是它使用的 WebIDE 不能给开发者很好的体验,主要是它不具备很好的语言服务系统。Substrate-playgound 是一个比较好的案例,但是它缺乏“针对合约开发”设计的功能。因此我们认为 Carpo 作为针对合约开发流程设计,且体验能接近于本地开发的产品,会对合约开发的生态带来极大的帮助。Carpo 当前同样只是在计划当中,不过我们预计这个项目在7月左右就会启动。
  • Elara 是横跨开发者与用户场景的产品,用于提供节点的服务,类似于 Infura。Elara 经过独特的设计可以在更少的资源下提供更多连接的节点服务,近期我们正在重构这个部分,将来它会更加健壮。
  • 对于用户端,我们的产品主要有 PatraScan 和 PatraStore。其中 PatraScan 将会和 Polkadot 生态中的其它浏览器采用不同的设计思路,获得很全面的信息的展示。而 PatraStore 采用了类似 DApp Platform 的概念,能让用户一站式的体验各类 DApp,目前主要是为了展示示例类的 DApp。

我们正在逐步构建 Patract 对 Wasm 合约以及整个产品的文档( https://patractlabs.github.io/Substrate-contracts-book/en/),以帮助开发者一站式的入门并深入 Wasm 合约开发。接下来让我挑选几个场景结合讲一下我们的产品是如何工作的。

Patract 数据服务层

首先我们来介绍 Elara 及 PatraScan 体系。事实上,我们的这些和数据相关的服务都是构建在 Patract 数据服务层上。数据服务的核心是将链上的所有数据导出到外部数据库中,包括区块、交易,及最重要的状态数据。Parity 组织下有一个 Substrate-archive 的项目,它的目的是与 Substrate-backed 支持的链一起运行,将所有块、状态和交易数据索引到 PostgreSQL 中( https://github.com/paritytech/Substrate-archive)。

Patract 早期在构建数据服务的时候,使用的是这个项目。当时在使用这个项目的过程中遇到不少问题,因此我们团队的成员给这个项目提交了许多代码。而随着 Patract 工作的开展,我们认为 Substrate-archive 在许多点上已经不再满足于我们的需求。因此 Patract 不再采用 Substrate-archive 作为我们数据服务的数据导出组件,而是自己重新写了一个 Archive 项目。


在数据服务层的基础上,我们当前在进行中的项目有2个,Elara 和 PatraScan。其中 Elara 是类似于 Infura 的节点服务提供组件,它的目标是为了用尽量少的资源提供更多的节点连接服务。

前段时间 Parity 的节点出现了异常,一段时间内停止了服务,那时候可用的 endpoints 是 Onfinality 和我们 Elara 的服务。在那个过程中,我们用比较少量的资源承载了大量的访问请求,便是基于如图所示的架构。

在 Elara 的体系中,用户通过 endpoint 访问的过程中,并不是直接和节点相连,也不是通过负载均衡分配到了不同的节点上,而是同我们的 Elara middleware(中间件)相连。Elara middleware 设计为可以平行扩展的形态,在这个组件中,Elara 管理了从用户端发起的服务,包括查询和订阅的请求,并根据 Elara 的策略对访问请求进行分类。

其中订阅类的请求由 Elara 进行托管,并且由 Elara middleware 向节点订阅,这样由 Elara 去接收节点每个区块的状态更新通知,从而根据这次区块的变更情况,通知 Elara 托管的订阅请求。查询类的请求,由 Elara 转发到 Patract 的数据服务层,进行数据的查询,而不是对节点本身产生查询。通过这种方式,我们将用户访问请求的连接与计算压力转移到 Elara 上,从而拥有更强的扩展性。并且通过这种方式,我们并不需要构建节点集群而浪费大量的资源。

PatraScan ‍vs 其他浏览器

接着看右边,右边是我们的 PatraScan 的构建思路。当前的所有浏览器基本采用订阅 event,然后根据 event 信息查阅链的数据,构建出浏览器的数据库,展示给用户。而 PatraScan 是采用直接查询导出的链的状态,来展示数据。其中前者是强业务关联,而后者是弱业务关联。

例如当链上出现了一次转账的场景,对于其它的浏览器,它的做法一般为:

  • 首先需要知道链上有转账的这种业务逻辑,且转账交易发生后,会打印一条 transfer 的 event。并且理解到到转账交易发生发,发送者的余额和接收者的余额都会发生变化。
  • 订阅这种 event 。
  • 当遇到 transfer 这种 event 的时候,需要向链查询发送者与接收人的余额,更新自己的数据库。

我们把这种方式叫做强业务相关的浏览器,因为它需要根据每个 event 都需要做出不同的处理,例如转账需要更新发送者和接收者的余额,staking 需要查询更多和 staking 相关的数据。这种方式更像是 “pull” 的行为。


而对于 PatraScan , 我们并不需要根据链的业务逻辑针对不同的情况都需要处理,而是考虑如何合理的展示链上的状态数据。例如对于转账操作而言,其链上数据是会根据转账的逻辑而变化的,这个数据导出后,当查询某个地址的余额时,我们就直接展示了这个地址的状态,不需要根据交易的情况分别去更新对应的数据库。这种方式更像是 “push” 的行为。


PatraScan & Apps

然后我们和 Apps 相比,实际上 PatraScan 的模式和 Apps 的模式比较接近。如图所示,如果把 Apps 的结构拆分为业务和视图,那么对于 Apps 来说,实际上它是把对链上状态的重新组合的过程,也就是业务逻辑放在用户端,和视图合并在一起。因此 Apps 能够直接和链访问,并对链上状态进行展示。


对于 PatraScan 而言,在结构上是相当于我们将 Apps 的业务逻辑移动到了后端上,在后端组合了链上的状态数据,在前端的视图进行展示。PatraScan 组合链上状态的部分我们叫做 PatraScan API ,可以用于不同的场景下。PatraScan 的产品逻辑正在设计当中,而 Scan 的底层基础设施已经完成了大部分,在产品逻辑设计完成后,便可以进行 Scan 的开发。


PatraScan 相当于是我们 Patract 数据服务层的一种使用手段,在 PatraScan 的项目完成后,我们便可以设计更多的基于数据层的产品。例如在以太坊生态中对于 DeFi 有很多数据的展示服务,这些都是对链上数据的二次开发过程。我们预计下一个和数据服务层相关的产品是和 GraphQL 有关的项目。

Ask!

接下来介绍的部分便是 Patract 的 Ask! 合约语言。Pallet-contracts 的设计是和合约语言无关的,又由于有许多种语言都可以编译到 Wasm,因此我们可以基于不同的语言设计能运行在 pallet-contracts 上的合约语言。


那为什么在已经有 ink! 的情况下,我们还需要基于其它语言去设计呢?这是由于不同的语言是会存在不同的编程范式的,在不同的业务场景下,可能需要不同的编程范式的支持。不存在只使用一门语言就可以比较好的描述所有的业务场景的情况。


举个比较典型的例子,Solidity 的语言设计中存在继承的功能,而 Rust 的语言设计中没有继承,而是采用 trait 加上组合模式 的手段。因此在不同的业务场景下,就适合采用不同的语言体系编写不同的合约。而另一方面,合约调用合约的过程在模型上其实更像是微服务之间的调用过程,因为合约比较重要的特点是合约的资源只和当前的这个合约有关系,也就是一个合约是一个独立的单元,这是因为合约的状态存储只和这个合约的实例有关系。


微服务之间只用约定调用间的接口规范,而微服务各自的实现方式是不会做限定的。合约也一样,不同的合约完全可以采用不同的语言实现,只要合约调用合约的接口是统一的就行。因此 Ask! 在设计之初在合约调用这一块就是和 ink! 所兼容的。


Ask! 的整体设计思路与模型与 ink! 是比较接近的。我们来看看这个 Ask! 和 ink! 的对比表格。

基于语言的比较。ink! 是基于 Rust 的,Ask! 是基于 AssemblyScript 的(也叫 AS)。AS 是 TypeScript的变种,因此其编程范式和 TypeScript (TS)比较接近,而且 TS 也是当前相当受欢迎的编程语言,拥有广泛的开发人员。

ink! 和 Ask! 在原语言的基础上,都是通过修改原文件中的 AST(Abstract Syntax Tree) 达到合约的特性,这点两者相同。

两者实现修改 AST 的方式不一样。这主要是由于不同语言的编译器提供的特性不一样。ink! 采用的是程序宏的形式,而 Ask! 采用的是编译器插件,这意味着想要编译 Ask! 合约,需要使用 Ask! 提供的整套编译环境,而不能像 ink! 一样,只使用 Rust 编译器也能编译。两者为合约语言提供的基本元素。比较来看这两者的设计是一致的,都是模仿 Solidity 。

存储设计的比较。由于整个 Substrate 体系都是基于 k/v 的状态设计,因此 ink! 在存储模型上设计了 SpreadLayout/PackedLayout 的概念。Ask! 在此没有做什么特别的创新,实现思路是类似的。可以视为是另一种实现。

重用代码的方式。重用代码在合约的概念下分为2种模型,跨合约调用,以及复用已有的代码。

a. 跨合约调用。ink! 尝试使用 trait_definition 的方式来定义接口,但是这块当前实现的不够好。在Ask! 我们采用 interface 的概念,这个概念和 solidity 的设计类似。

b. 代码复用。由于 ink! 基于 Rust,因此它只能采用组合的方式。而 Ask! 可以用继承的方式。但是ink! 对复用当前支持十分有限。比较典型的案例是,例如如果已经有一个 Erc20 合约标准实现,就像 OpenZepplin-contracts 的合约,如果想在这个标准合约的基础上添加一个 issue 方法。

那么在 ink! 中,当前只能将整个合约复制过来,然后添加上这个方法。除非 ink! 在合约组合上做好的设计。而又由于合约的资源是和这个合约绑定的,所以在组合的语义上这块很难设计。而对于 Ask! 来说,它可以设计类似和 Solidity 的继承的方式,继承于这个 erc20 合约并在其基础上扩展行的方法。

About Patract


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