原文: Comparing Modern Smart Contracting Environments
免责声明
虽然写完这篇文章后,我加入了 NEAR 团队,从事他们的 SDK 和其他 Rust 工具的工作。但在写这篇文章的时候,我与文章中的任何一个生态系统都没有利益关系,为了保证当前的客观性,我不会根据任何一个生态系统的新信息来更新文章。
为什么要比较智能合约?
智能合约可以被认为是区块链上的程序。在一个去中心化的区块链上运行代码可以创造什么价值?当你创建一个标准合同时,你会依靠经纪人和律师来创建和处理纠纷。在这种情况下,你必须支付大笔款项,并信任第三方来确保协议的执行。现在,让我们将其与程序化智能合约进行比较。假设区块链的虚拟机没有漏洞,不仅任何个人可以用代码创建合同,而且解释合同的结果也应该总是确定的。这对企业非常重要,因为他们可以减少成本和时间,同时处于一个无信任和确定性的环境中。
智能合约正在得到普及和使用,因为这些系统是无需许可的。这个价值在 DeFi 中表现得淋漓尽致,因为它允许任何人创建金融工具,同时也允许任何人使用它们。这方面的一个很好的例子是 Aave,这是一个代币借贷协议,允许任何用户通过向借贷池提供流动性来赚取利息,用抵押品借入代币,或者进行闪电贷款(这需要在交易结束后偿还贷款)。所有这些功能都比银行更快、更少的权限需求,因为银行至少要强制要求你有一个银行账户,你的信用足够好,并且要经过审批程序。
智能合约的替代品是特定应用的区块链。这些是具有自定义逻辑和自己的验证器的独立链,允许通过其共识机制和验证逻辑实现更多可配置的安全性。有两个主要框架用于开发特定应用的区块链,它们是 Substrate 和 Cosmos-SDK。
❤️ 译者注:Substrate 和 Cosmos-SDK 是一个构建区块链的开发框架,可以让开发者在很短的时间开发一个高可用的区块链应用,而不需要过多的关心一些底层的技术。
不要把特定应用的区块链与智能合约混为一谈,这很重要,因为它们有特定的用例和缺点。智能合约有一些局限性,那就是只有当交易被发送到智能合约时,智能合约才会被执行,它们受限于所运行的虚拟机或环境,网络参数不能被配置以适应每个应用程序的需求。
虽然特定应用的区块链可以解决这些问题,但它也有一些明显的缺点,比如说:
- 为了让用户足够信任,在应用程序推出之前,需要有一个围绕着它的社区和生态系统,以确保有足够的验证者和安全。
- 与部署智能合约相比,启动区块链需要更多的经验和开发成本。
- 这些应用程序在其当前状态下是不可组合的。目前存在的跨链通信协议处于非常原始的状态,需要大量的开销,IBC 在 2021 年 2 月中旬刚刚推出了Cosmos-SDK Stargate 的升级版(尽管该协议与网络无关),而 XCMP,也就是 Polkadot 的同类产品,仍在开发中。
⚠️ 译者注:最近发生了一起 Solana 网络的跨链协议漏洞,损失超 3 亿美元。https://www.theblockbeats.info/flash/66015
智能合约或应用程序能够与其他智能合约直接交互,这使得它们能够被模块化,以避免重复无用的工作还可以协同工作。
由于智能合约可以更快、更容易地被用作去中心化应用的构建模块,并具有最低的进入门槛,它们使个人能够轻松地创造价值,并扩大更多的可能性。
虽然以太坊在智能合约领域占主导地位,并将在不久的将来继续占主导地位,但也有一些非常有趣和可扩展的替代品。在写这篇文章的时候,进行一次简单的 ERC20 转账需要花费 19 美元,或者通过 Uniswap 进行代币互换需要花费 58 美元,这对普通消费者来说是不可行的。当对财务有更高的要求时,无许可或各种廉价争论就没有那么重要了,这就是为什么本文将介绍和比较替代方案。
这篇文章将涵盖哪方面
本文将主要关注 Solidity、NEAR、ink!、CosmWasm 和 Solana 的开发者经验。下面列出了几个内容:
- 协议概述
- 初始化一个新的合约
- 可用的环境设置和工具
- 围绕智能合约语言和代码运行的虚拟机的技术细节
- 智能合约是如何融入协议的
- 编写合约逻辑和测试功能
本文不涉及的内容
主要是以开发者的视角,所以不会涉及任何协议的代币经济。
对比将以每个协议中构建和测试智能合约而结束,不包括在链上部署合约,从前端与它交互,或升级合约代码(即使协议支持)。代码中的跨合约调用也不会被涵盖,但我会在必要时描述它们如何交互。
将会构建什么合约代码来比较?
对于合约逻辑,我决定写一个财阀托管合约,任何人都可以购买一个路由,并把他们的内容放在链上(在这种情况下,就是一个字符串)。我们的想法是,每个路由的最高出价者将能够展示他们选择的任何内容,而这将在未来通过前端加载。
我选择使用这个任意的逻辑来比较不同的协议,因为它涵盖了你在大多数智能合约环境中可以做的以下常见的交互:
- 合约的初始化
- 从合约中查询数据(获取内容)
- 向合约发送代币并改变状态(购买路由)
- 从合同中转移代币
- 访问环境变量,如发送者地址、交易中发送的值和合约中锁定的代币
Solidity
Solidity 是一种智能合约语言,可编译为 EVM 字节码。它是迄今为止使用最多的智能合约语言。大多数网络提供对 EVM 的支持。为了将其与本文所涉及的协议联系起来,所有四个网络目前都有或正在努力实现对 EVM 的支持。
- Ethermint (Cosmos)
- Substrate EVM pallet (Polkadot)
- NEAR EVM
- Solana (very early stages and not open source)
❤️ 译者注:Solana 现已兼容 EVM。参见 https://neon-labs.org/
这些选项并不意味着是长期的解决方案,但有趣的是,即使是最近的现代网络也在努力实现对 EVM 的支持,以加入新的开发人员和迁移现有的应用程序。为任何基于 EVM 的链或兼容 EVM 的链构建合约代码都将非常相似,但在本节中,我们将假设代码将被部署在以太坊上。
兼容 EVM 链的两种主要语言是 Solidity 和 Vyper,还有 Yul 和 Yul+ 是中间语言选项。我选择在Solidity 中创建这个例子,因为到目前为止,它是这些选项中最常用的语言。
在开发智能合约时,Solidity 可能不是最现代或普遍可用的语言,但鉴于 Ethereum 和兼容 EVM 的链在生态系统中的主导地位,如果你想尽快创造价值,Solidity 将是首选语言。鉴于其受欢迎程度,你也将拥有最好的开发者工具和钱包支持。由于 Solidity 只能编译成 EVM 字节码,它将变得过时,因为大多数现代解决方案正在远离 EVM(而且有充分的理由)。
在这篇文章中,我们将使用 Truffle 作为编译和测试合约的框架。我要指出的是,对于开发,我使用了 Remix,因为它是开发 Solidity 智能合约事实上的 IDE,对于开发本文这种的快速项目来说,它使编写和测试更加容易。
这里有一个快速入门的例子。
现在,让我们看看合约是什么样子的,然后介绍一些重要的部分。这个例子的代码可以在这里找到。
该合约的第一部分描述了将被存储在链上的数据以及以何种格式存储。ContentRecord 结构定义了每个路由的存储值。
接下来要介绍的是函数修改器,这可能看起来很陌生。
modifier onlyOwner {
require(msg.sender == contractOwner, "Sender is not the contract owner");
_;
}
函数的 modifier 关键字可以给一个函数增加一些额外的验证或逻辑,它可以作为属性添加到任何函数中。在上面的代码中,修饰器是在执行函数之前检查消息发送者是否是合约所有者。_;
的语法只是代表这个函数的原有逻辑将在哪一步执行。
这些功能和逻辑是相当直接的,但有几个地方需要注意:
purchase
函数需要payable
修饰符,以表明它可以接收随交易转移的value
。- 当对映射进行查找时,任何不存在的键将返回一个零值,这没有空指针或可选值的概念。
withdraw
函数应用了我们上面创建的onlyOwner
修饰器,只允许合约所有者从合约中提款。资金并没有在合约中使用,所以在这种情况下,将所有的资金转出就可以了。getRoute
函数应用了view
修饰符,表示它不会修改任何状态。这个函数还指明它会返回内存中的字符串内容。
这部分的 Truffle 测试代码位于这里,可以通过以下方式运行:
truffle test
默认情况下,Truffle 测试框架将启动一个区块链开发环境,并预先分配账户以在测试中使用。这很好,因为你得到了一个完整的集成测试,与实际区块链上的交互相匹配。这些测试将与通过 Ethereum JSON-RPC API 指定的网络互动。几乎所有与链和合约的交互都要通过这个 API,无论好坏。因为一切都通过该 API,你甚至可以选择将后端换成你想要的任何支持以太坊 RPC-API 的网络(我在测试 Ethermint **时就是这样做的)。
太棒了,我们现在已经在 Solidity 中建立了合约 现在,我们将继续与其他的合约进行比较(我保证接下来的合约不会有这么大的问题)。
NEAR
NEAR 是一个质押证明和分片区块链。NEAR 的虚拟机执行通过其 Rust SDK 和 AssemblyScript SDK 生成的 Wasm 代码。对于那些不熟悉的人来说,Rust 通常被用来生成 Wasm 代码,因为它有很好的支持,是一种内存安全的语言,而且效率很高,开销很低。AssemblyScript 也常用于 Wasm,因为它是 TypeScript 的一个变体,同时也非常高效。他们建议使用 Rust 以获得更好的用户体验,但在这种情况下,两者都得到支持是非常好的,因为其他 Wasm VM 区块链通常只支持 Rust,它允许不熟悉 Rust 的人跟容易上手。
我已经在 GitHub 上建立了 Rust 和 AssemblyScript 两种合约的变体,但我们在本文中只涉及 Rust 合约,因为它最容易与其他合约进行交互。
NEAR 的分片设计允许其横向扩展,而不是通过增加验证器的硬件要求来限制去中心化的纵向扩展。他们的分片协议被命名为 Nightshade,与其他分片区块链相比,其主要区别在于有一条链管理每个区块中发生的所有交易,分片的验证者只需维护和验证在该分片中修改的交易和状态。就开发者的经验而言,分片的通常负面作用是,协议真的很难保持原子性,因为一个分片上的区块可能是孤岛化,而交易的另一部分已经提交到其他分片链上。Nightshade 旨在简化和解决与此相关的问题以及存储/状态膨胀,这是一个非常常见的瓶颈。
NEAR 的跨合约调用假定所有合约不在同一个分片上(以避免合约集中在同一个分片上,造成拥堵和),这意味着所有调用都将是异步的。虽然这增加了复杂性,但它也允许与多个合约进行并行交互,而不必同步等待每个合约的响应。虽然跨合约的交互目前有很多开销,但可以跨越多个区块,这对于提高个人用户的效率非常有用。
目前,NEAR 的分片计划是同质的,这意味着每个分片将有相同的运行时间来执行交易和相同的验证逻辑。这对于智能合约环境来说通常是必要的,而且还允许动态重新分片。在目前的协议设计中,没有任何选项可以用来创建一个经过调整以适应大型应用的利益相关者的链/分片,而 Cosmos 和 Polkadot 的设计很好的支持这一观点。
这里有几个例子可以上手使用 NEAR。
所有用来对比的合约的代码都在这里。现在来看看实际的合约代码!
现在,让我们来看看合约的一些细节,并将其与 Solidity 的等同语句联系起来。
- 用
#[near_bindgen]
标注的 ContentTracker 定义了合约和它的状态 ContentTracker
的默认实现等同于 Solidity 中的constructor
,在合约部署时被调用。purchase
函数用#[payable]
注释,相当于 Solidity 的payable
属性。- 代币转移,与
Promise::new(...).transfer(...);
行是上面提到的异步调用的例子。 - 转移可以设置定时,但合约并不等待结果来完成购买路由
- 数据结构
LookupMap<String, ContentRecord>
处理存储的键值查找,大致等同于 Solidity 中的mapping
如果你不熟悉 Rust 的语法,代码中可能有一些令人困惑的部分,但希望现在已经清楚了功能。测试这个合约就像在 near 的目录中运行 yarn test:cargo
一样简单。所有的测试都位于合约的同一个文件中,并且可以使用 testing_env!
宏来初始化环境变量,从而调用这些函数。也有一种方法可以通过初始化 JS 客户端和使用 jest 框架(或任何类似的框架)来做完整的 e2e 测试,但我认为这不在本文的讨论范围之内。
ink!
Ink! 是 Parity 的智能合约语言,可编译为 Wasm。你可以通过将这些智能合约部署到任何 Parachain 来使用更大的 Polkadot 生态系统,大致相当于一个分片,支持内置的合约托盘。社区中对这一计划存在不确定性,所以细节将是模糊的。广义上讲,智能合约是生态系统的一个较小的组成部分,而不是固有地包含在网络中。我选择接下来介绍这个,因为它与 NEAR 类似,有一个共享的安全模型,并以某种方式分片,但它的不同之处在于,Parachains 是异质的,意味着它们有自己的状态转换功能,这也被编译到 Wasm 并包含在中继链中。
应用程序能够配置状态转换逻辑以适应其利益相关者的需求是一个伟大的功能,但它通过链上拍卖获得 Parachain 插槽带来了不确定性。我不会太详细地介绍 Parachain,但只需注意部署代码的方法是用 Substrate 建立一个 Parachain/Parathread,用 ink! 构建智能合约(我们接下来会介绍),或者将基于 EVM 的合约部署到支持 EVM 托盘的 Parachain 上。
Parachain 运行机制和智能合约编译为 Wasm 的好处是,网络可以在不引入硬分叉的情况下进行升级。这些无分叉的升级有助于消除节点操作员所需的开销,这也允许更快速的更新。
所有部署在同一个 Parachain 上的智能合约都可以彼此同步交互。不幸的是,前面提到的 XCMP 协议还在开发中。通信只限于合约分片上的东西。不幸的是,这意味着如果智能合约在可组合方面有好处的话,那么智能合约从网络的分片结构中得到的好处将非常少。作为这个生态系统中的智能合约开发者,你需要确定会有支持合约托盘的 Parachains,并根据所需的网络参数选择一个,但也要考虑到部署在那里的项目生态系统。网络可以灵活地代表网络的利益相关者,但它在很大程度上控制着 Parachain 插槽的所有者,这可能更不稳定,更难预测。
这里有一个使用 ink! 合约代码的教程。合约代码的示例可以在这里找到,看起来像这样:
与 NEAR 合约相比它非常相似,但这里有一些需要注意的地方:
- 合约代码被封装在一个模块中
- 使用
#[ink(event)]
属性很容易生成事件,并且能够用#[ink(topic)]
索引日志。 - 是的,其他协议也有记录日志的能力,但具体来说,这里很容易定义和索引特定的字段数据,很干净。
- 与特定退出代码相关的错误
- 能够表达哪些值从存储中被懒惰地加载,以及默认的懒惰加载和内置存储类型的缓存
- 所有的类型都很准确地从 std 库中移植过来,这使得任何熟悉 Rust 的人都很容易使用它。
看了这个构建过程之后,我想给创建这个的团队击个掌,他们真的做了一件了不起的工作。没有 Rust 经验的人可能会在一些边界情况犯错,但总的来说,这是一个伟大的环境设置。他们有一个很好的开发者体验,因为它很容易阅读,有很好的错误处理,简单而有用的日志,很好的文档,符合语言习惯的 Rust 代码,而且优化的非常好。
CosmWasm
下一个要介绍的智能合约平台是 CosmWasm。CosmWasm 是一个 Cosmos-SDK 模块,类似于 Polkadot 中的合约托盘,它能让你将 Wasm 智能合约添加到任何用 Cosmos-SDK 构建的链上。 Cosmos 的网络拓扑结构与其他方案不同,因为它默认没有一个共享的安全模型,而且它没有分层结构,而是分成主权区,处理自己的验证器集。Cosmos-SDK 旨在创建特定应用的区块链,其中应用之间的互操作性是通过 IBC 实现的。这创造了一个环境,其中状态机可以与应用程序/网络的利益相关者相匹配,但它的代价是必须为网络安装可信的验证器,启动一个链(比部署一个合约的开销更大),并且鉴于 IBC 的开销和不稳定的性质,可组合性更差。
虽然 CosmWasm 可以插入任何链,但用于测试网的链是基于 wasmd 的,它是 Gaia(宇宙中心链)的一个分叉。目前还没有为此推出主网。
CosmWasm 是对 Cosmos 空间的一个很好的补充,与开发和启动一个 Cosmos 区相比,它允许智能收缩,并允许开发人员构建更小和更可组合的应用程序。另一点需要说明的是,在构建 Cosmos 区时,你需要使用 Go 作为编程语言,或者在 Tendermint 之上构建应用时,你需要手动满足 ABCI 接口。CosmWasm 确实为 Rust 开发者打开了大门,同时也减少了创建去中心化应用时需要构建的范围。
关于与 CosmWasm 的可组合性,一个有趣的观点是,为了避免重入攻击,明确地不允许同步合约调用,可以在事务返回中指定消息,在当前事务失败后执行。这使得某些交互更加困难,特别是当你依靠调用另一个合约的结果来最终确定一些状态时,但不允许的动机是合理的。
至于代码,如果你曾经写过 Cosmos-SDK 模块,它看起来会非常熟悉。CosmWasm 的教程可以在这里找到,合约的代码在这里。
首先要注意的是 cosmwasm/src/state.rs 中与状态的交互,这是帮助与状态交互的封装函数。
这定义了读取合约的特定状态(在上面的情况中是读取 config 数据)和键与值的映射。这可以与 Cosmos-SDK 应用程序的保持者进行比较。
接下来,错误被定义在 cosmwasm/src/error.rs。
消息类型的定义被保存在 cosmwasm/src/msg.rs 中。
其中 HandleMsg 枚举定义了状态转换逻辑的类型(Cosmos-SDK 中的 Msgs),QueryMsg 处理查询变体(Cosmos-SDK 中的 Queryer)。
最后,核心合约代码逻辑位于 cosmwasm/src/contract.rs 中。
这些函数将简单代表 Cosmos-SDK 中的处理程序。
关于合约,有几点需要注意:
- 没有默认的代币,所以这个合同将接受任何类型的代币。
- 为了简单起见,重新购买任何路由都需要相同的代币,因为我无法访问价格预言机。
- 当一个路由从以前的所有者那里回购时,发送信息包括在HandleResponse中,并将在同一交易中执行。
为 CosmWasm 编写智能合约比其他平台更繁琐。这部分是由于缺乏程序性宏的使用。目前还不清楚与 Cosmos-SDK 架构的相似性是基于技术上的动机,还是为了让 Cosmos 上的开发人员更熟悉,但如果是这样的背景,就会更容易理解。不幸的是,这并不是非常习以为常的 Rust 代码,对于一个人来说,并不是非常容易上手。复杂的是,内部类型几乎没有日志,所以当有些东西不清楚的时候,可能的做法是通过实例来寻找用法。
值得注意的是,还有 CosmWasm Plus,它是为 "真正的网络 "设计的,它的库与基本的 CosmWasm 库重叠,所以也许在深入研究之后,其动机也会更清楚。
测试合约是通过原生的 Rust 测试,类似于 NEAR 和 ink!,并且在文档中指出,如果测试通过,它们将在链上工作。
CosmWasm 是 Cosmos 生态系统的一个奇妙的组成部分,我希望这个项目能够继续改进,以获得更多的人采用。
Solana
Solana 是一个非常有趣的区块链,因为它的协议设计与所有替代方案有很大的不同。许多单独的要点将超出本文的解释范围,所以我将列出一些有趣的要点:
- 通过 LLVM 基础设施编译的 Rust 或 C/C++ 智能合约
- 编译成 BPF 字节码,加载到链上
- 基于同步时钟的共识机制,称为历史证明
- PoS 的 PoH 扩展
- 交易的时间戳与 VDF 样本同时存在
- 用本地计算取代低延迟的通信瓶颈
- 能够对交易进行亚秒级确认
- 智能合约的并行执行
- 反对基于 Wasm 智能合约的理由是它们目前都是单线程的
- 描述哪些状态是读/写的,并且可以根据类似于读写锁的语义来并行化
- 基于程序 ID 的类似 SIMD 的指令执行
- 存储优化,利用操作系统的优势
- 账户存储在内存映射的文件中
- 他们的 Cloudbreak 存储的性能接近于所有状态都可以保存在内存中的性能
- 不分片(愿景是在不分片的情况下尽可能快地扩展,然后只在必要时添加)。
如果这其中有很多是不熟悉的,只需知道这些策略和设计离区块链的轨道很远,而且这很了不起!这些东西肯定有取舍,但不可否认的是,Solana 协议给区块链生态系统带来了大量的创新。
至于智能合约生态,可以用 Rust 或 C 语言编写,并编译成 BPF,这是部署在链上的字节码。虽然支持 C 语言,但围绕 Rust 有更多的工具和资源,所以建议使用 Rust。开发 Solana 程序有两种主要方式:使用 Solana 工具的 vanilla Solana 程序或使用 Anchor,这是一个编写 Solana 程序的框架。在这篇文章中,我将使用 Anchor,因为它抽象了很多繁琐和古怪的逻辑,同时也更接近本文中所比较的替代方案。
在开始写程序之前,首先要浏览的是账户模型。它与其他区块链非常不同。Solana 的账户类似于 Linux 操作系统中的文件,在这个意义上,它们可以容纳任何任意的数据,还包含关于如何访问它们的元数据。与操作系统中的文件的区别在于以下几点:
- 文件有一个生命期,基于支付了多少兰姆波特(或代币)作为租金。
- 这些账户由一个 256 位的公钥来索引
- 这些账户有一个固定的大小,不能被调整大小。是的,你没看错,我稍后会更多地谈及其影响。
让我们来谈谈编写程序的问题。程序只是被标记为可执行的账户。Solana 依靠账户进行存储,因为它在并行交易时不能有一个底层存储。这意味着,在你部署你的程序之前,如果你想升级程序,你需要估计账户需要多大的容量(或者让它处于默认状态,它分配的数据比目前需要的多 2 倍),同时,在应用程序中使用的任何状态也需要根据估计进行预先分配和初始化。目前,短期内还没有计划允许可调整大小的账户,所以这是需要记住的重要一点。另一个重要的限制是,程序没有返回值,所以如果你需要从程序调用中获取一个结果,它需要被写入一个账户并在链外进行解释。
还需要注意的是,代币只是一个程序,其中的数据在一个账户中,每个代币账户有一个所有者。代币的面额只是由一个公钥来代表。
当执行一个事务时,事务中访问的所有账户都需要由用户预先指定。这给任何应用程序的开发者带来了障碍,因为他们需要知道在事务中使用的所有账户,如果考虑到应用程序之间的跨程序调用,这将变得更加困难。这使得可用性和可组合性更加困难,但也要记住,在计算、调用深度、事务大小限制和堆栈框架大小等方面也有更严格的限制,这往往迫使事务和/或函数被拆分。
现在来看看合约。不幸的是,鉴于 Solana 编程模型的限制,我们实际上无法建立与其他选项相同的功能,如果不将应用逻辑移到链外,或将功能修改为低效并受固定账户大小的限制。这种特定的合约逻辑需要两种东西中的一种:某种可以序列化为字节的键值映射,或者能够在持久化键值存储中存储值,其中值被单独存储。后者更受欢迎,并在上面的替代方案中使用,因为它意味着只加载使用的内容,并避免了大型序列化逻辑,但让我来谈谈为什么两者都不可能。
映射:
- HashMap 所需的 std::hash lib 的组件在 Solana 中不被支持,也不支持任何其他第三方的散列算法(我甚至试图在 Anchor 中专门添加对映射的支持)。
- 由于账户大小固定,用作存储的账户需要预先分配不合理的字节数来进行扩展。
- 对于(去)序列化和映射功能来说,计算上限会很快被触及。
- 有一个可增长的(String, ContentData)向量可以编译,但会遇到所有其他问题
键值存储:
- 没有对存储的访问,只有账户
- 将一个程序地址映射到一个有数据的账户需要链外应用逻辑,对于我们的示例应用来说,不会有一个精确的 "路由"。
由于限制,以及我在这方面的时间有限,我的演示合约将只允许初始化一个账户(代表一个路由),程序将拥有一个代币的金库,被锁定在程序中,直到有人购买它以获得更多相同面额的代币。鉴于 Solana 程序的调用没有返回值,也不会有路线的 getter 函数,这意味着这个逻辑将全部发生在链外。
这里可以查看 Anchor 介绍。
这里是合约代码:
为了解释正在发生的事情,所有将被访问的账户(类似于存储)在每次调用时都被注释为 #[derive(Accounts)] 的结构。这个合约的函数是 Initialize
,它为初始所有者初始化账户数据,而 Purchase
则允许任何人以更多的代币购买它。其他可以传入函数的数据,只是保存在内存中,并不被持久化的是函数参数。你可以在初始化和 Purchase
函数里面看到这些参数,以及持有交易所需账户的 Context。
接下来,合约的状态位于 ContentRecord
结构中,该结构被注解为 #[account],以表明它代表一个账户的数据布局。
当从程序中转移代币时,转移调用由程序签名,这个签名大致等同于用私钥的签名,只是只有程序可以生成它。
提取资金的功能在这里并不适用,因为没有合约所有者。这意味着如果没有提款功能,合约的范围就更小了,而且有资金锁在程序的金库里,任何人都无法获得。
测试合约是通过 mocha 测试框架完成的,与 anchor test 一起运行,并将处理部署合约和通过 solana-test-validator 与之交互,这包括在 Solana 安装中。这些测试很好,因为它是完全的 e2e 测试,并且大致相当于前端与程序的交互方式。
通过查看这个合约的测试,可能了解到对账户如何被初始化和连接到函数调用的更多信息。有一点需要注意的是,作为上下文,交易中包含的指令是在交易之前运行的,这对于能够创建交易中使用的账户来说是非常重要的。
总之,在目前的状态下,Solana 不是一个使人很容易跳进去建立一些东西的智能合约环境。Solana 可以提供巨大的可扩展性,但你必须在应用符合这个编程模型的情况下才会选择 Solana。我很难推荐别人从 Solana 开始,因为由于目前的协议设计,开发者的经验和局限性比其他方案差得多。
结论
就这样吧!如果你已经走到了这一步,感谢你的阅读!如果你有任何问题或想了解其他内容,请在评论中告诉我。如果有任何问题或你希望在评论中涉及的其他内容,请告诉我。你想知道更多关于其他智能合约语言/环境,让代码上链的部署过程,和/或从前端到这些合约的互动,以更好地了解整个堆栈吗?