如何从以太坊迁移到 Solana:开发人员指南

以太坊开发者如何迁移到 Solana

了解在 Solana 上使用 Solidity 编程所需的一切知识

以太坊向 Solana 迁移指南

这篇文章是关于什么的?

以太坊是近期最重要的创新之一。历史上第一次,我们有了一个为社会协调而建立的去中心化全球平台,它有可能彻底改变许多行业。尽管重要,但以太坊的运行环境--以太坊虚拟机(EVM)--目前的状态并不是为消费级应用而构建的。它是一个单线程、基于Gas的网络,费用不稳定。相比之下,Solana 是一个高吞吐量、低延迟的网络。它提供了一个并行化的基础设施,收费低且可预测。它直接解决了 EVM 的局限性,并对其原始设计进行了改进,使其成为希望构建可扩展高效应用程序的开发人员的理想选择。

本文是一份全面的迁移指南,适用于希望在 Solana 上构建应用程序的 EVM 开发人员。它涵盖了两者之间的根本区别,研究了以太坊和 Solana 的共识机制、交易处理方式以及用于开发智能合约的语言。然后,文章介绍了 Solana 的账户模型,该模型提出了一种更统一、更多元的账户方法。文章还探讨了 Solang 和 Neon EVM,这两个对 Solidity 友好的工具增强了 Solana 的开发体验。

基本差异

本节将探讨以太坊和 Solana 这两个旨在为全球协调创建去中心化状态机的区块链之间的关键差异。然而,二者在共识机制、交易处理方法和智能合约开发语言方面存在显著差异。深入了解这些基本差异,就能发现 Solana 作为高性能全局状态机相对于以太坊的独特优势。

共识机制

共识机制是每个区块链网络的基础。它们负责决定如何验证交易,以及如何以安全、高效和去中心化的方式将区块添加到区块链中。以太坊和 Solana 都是权益证明(PoS)网络。虽然它们在 PoS 方面有着共同的基础,但由于确认规则不同,它们达成共识的方法也不尽相同。

以太坊使用GasperCasper the Friendly Finality Gadget (Casper-FFG)LMD-GHOST 分叉选择算法组合。这种组合形成了确保以太坊安全的共识机制。

Casper 是一个基于 PoS 的终结(finality)系统,可将区块的确认状态升级为"终结"。在以太坊上,当三分之二的总质押投票赞成纳入一个区块,并在此基础上构建另一个区块时,该区块即被视为最终完成。由于最终确定需要三分之二的总质押同意一个区块是规范的(或权威的),因此攻击者如果不拥有或操纵三分之二的总质押,并通过slashing破坏至少三分之一的总质押,就无法创建另一条最终确定的链。Caspers 允许新加入者放心地与规范链同步。

LMD-GHOST 是 "最新消息驱动的贪婪最重观测子树 "的缩写。它是一种决定遵循哪个以太坊分叉的算法。它通过选择获得网络验证者最多支持(即 "权重")的分叉(因此称为 "贪婪最重子树")来实现这一目的。然后,它会确保只考虑来自每个验证者的最新信息。每次向以太坊提交新区块时,验证者都会使用该规则来决定它是否应成为规范链的一部分。

相比之下,Solana 使用历史证明(PoH) 哈希值来达成共识。尽管 PoH 的名字令人困惑,但它不是一种共识算法。它是一种在对抗网络中证明时间的方法。更具体地说,它是一种加密时间戳函数,允许节点在不相互通信的情况下就事件顺序达成一致。领导者(即向分类账本 追加条目的验证者)对区块应用时间戳,以证明自上一个区块以来已经过去了一段时间。

添加这些时间戳可以建立历史记录,因为它们可以证明数据存在于特定时间。历史记录是使用顺序抗预瞄散列值函数创建的,其中每个散列值都取决于其前一个散列值。可验证延迟函数 (VDF)是生成这些哈希值的关键。它们确保每个哈希值都建立在上一个哈希值的基础上,并包含自上一个哈希值以来所经过的时间。VDF 的集成为散列过程引入了时间维度,使得在 Solana 上创建可验证的时间戳事件序列成为可能。

在确定了 Solana 的 "历史证明 "机制如何通过加密时间戳创建可靠的事件序列之后,了解该机制如何与 Solana 的共识机制 Tower BFT 相结合至关重要。Tower Byzantine Fault Tolerance (BFT) 是 Solana 对传统拜占庭容错共识模型的变体,并针对 PoH 进行了优化。Tower BFT 使用 PoH 创建的历史记录作为参考框架。该框架使验证者能够高效、准确地对账本状态进行投票。Tower BFT 的功能是让验证者进行长期锁定的投票,随着投票数量的增加,验证者的承诺也会越来越多。有了 PoH,验证者无需相互交流,就能更快、更明智地决定账本的状态。将 PoH 和 Tower BFT 结合起来,可以加快共识的达成,并增强网络的安全性和可靠性。这样就形成了一个高吞吐量、可扩展的区块链环境,交易可以快速得到确认。

交易处理和执行环境

区块链的效率和可扩展性主要取决于其交易处理能力和执行环境。这些因素决定了交易的执行速度和网络运营的成本效益。区块链的交易处理方法及其执行环境的细微差别对开发人员的体验有很大影响。

以太坊的运行环境是一个确定性的单线程堆栈机器,被称为以太坊虚拟机(EVM)。它的行为就像一个数学函数。也就是说,当给定一个输入时,EVM 会产生一个确定的输出。将以太坊定义为具有状态转换函数是很有帮助的:f(S, T) = S'。给定一个旧的状态(S)和一组新的有效交易(T),EVM 会产生一个新的有效输出状态(S')。对于相同的交易集,EVM 将始终实现相同的最终状态。这对于保持整个网络节点的一致性至关重要。

EVM 按顺序处理交易。顺序处理可确保每个交易的执行环境都能准确反映出网络到那时为止的状态。顺序执行可实现精确的状态变化和Gas成本计算。这种可预测性可让开发人员和用户清楚地了解交易结果和成本。

以太坊通过采用单线程执行模式简化了智能合约的执行环境。由于每笔交易都是按顺序处理的,这就为开发人员提供了可预测状态变化的优势。可预测的状态变化使以太坊的开发环境对新开发者来说可以说更容易上手,因为他们可以把更多精力放在智能合约的逻辑上,而不是复杂的执行上。然而,这种简单性也给可扩展性带来了挑战。顺序处理限制了网络的吞吐量。这会导致潜在的拥堵、更长的交易等待时间以及高需求时更高的Gas费用。由于每笔交易都会消耗Gas,开发人员不得不对Gas效率进行优化。优化的目的是改善整体用户体验,消除拥堵时间,因为低效代码会导致普通用户无法使用应用程序。

Solana 开发人员不必像以太坊开发人员那样担心优化Gas使用。Solana 被设计成一个高吞吐量、低延迟的状态机,即Solana 虚拟机(SVM)。SVM 的一个重要组件是 Sealevel。它是一个并行处理交易的运行时引擎。在 Solana 上,每个交易都会告诉运行时在执行时要读取或写入哪些部分的状态。运行时会并行处理不冲突的交易和读取相同状态的交易。Sealevel 通过将交易工作量分配给验证器硬件上的多个线程来优化智能合约的执行。因此,当验证器在一个内核上处理一个交易时,另一个内核上可以同时处理另一个交易。

Solana、比特币和以太坊的平均交易费用

来源:Visa 对高性能区块链网络 Solana 的深入研究

Solana 与生俱来的并行性大大降低了交易成本。Solana 交易有两种费用:基本费用和优先费用。基本费用固定为每个签名 5000 lamports;大多数交易只有一个签名。优先权费 是可选的,允许交易确定自己与其他交易的优先权。优先级费用较高的交易将由调度程序进行非确定性优先排序。交易费用通常低于 0.001 美元,无投票权的平均费用在0.000005 至 0.00007 SOL 范围内,由于最近使用优先权费用的增加,上限比通常要高。按目前 SOL 的价格约 98.96 美元计算,这笔费用约为 0.000494 美元至 0.006968 美元。

比较 Solana 和以太坊的费用市场

来源:Visa 对高性能区块链网络 Solana 的深入研究

这些费用也是可预测的。Solana使用本地化收费市场来管理需求。区块空间的结构设计可防止任何单个活动热点(如被炒作的 NFT 铸币)主宰区块空间并提高整个网络的费用。只有试图访问特定高需求热点的交易才会看到费用增加。本地化收费市场允许优先收费,而不会导致全面爆发的Gas战争。这种本地化与以太坊等基于Gas的网络不同,在以太坊网络中,交易是按顺序处理的,全球拥堵会导致费用波动。

以太坊是一种单线程运行环境,一次只处理一个合约。它目前没有利用现代多核硬件的优势,导致验证器硬件利用率不足。维持节点低硬件要求的愿望增加了以太坊交易处理的局限性。相比之下,Solana 的并行处理能力允许 Solana 利用验证机的所有可用内核处理更多交易。再加上本地化的收费市场,Solana 是一种性能更强的全局状态机。

智能合约语言

以太坊智能合约开发的主要语言是 Solidity。这是一种静态类型的大括号语言,旨在创建在以太坊上运行的智能合约。它深受 JavaScript、C++ 和 Python 的影响,熟悉这些语言的开发人员很容易上手。

Yul是一种针对EVM兼容区块链优化的中间低级语言。Yul 为开发人员提供了对字节码执行的更大控制,在微调 Gas 消耗和管理其他底层操作方面具有很高的效率。开发人员可以直接用 Yul 编码或将其作为编译目标,从而创建更高效的合约。

Vyper是一种流行的 Pythonic 语言,强调简单性和安全性。与 Solidity 相比,Vyper 故意减少功能,以降低复杂性和潜在的安全漏洞。Vyper 的设计理念首先强调可读性和可审计性。这种方法激发了类似语言的开发,如 Fe,以及编译为 Vyper 的语言,如 Dasy

Rust是在Solana平台上开发智能合约(俗称程序)的通用语言。它是一种速度快、内存效率高的语言,性能与C++不相上下。这些特点使其适合为高吞吐量、低延迟网络开发应用程序。Rust 丰富的类型系统和所有权模型可确保内存和线程安全,从而迫使开发人员构建可靠、安全的应用程序。

虽然对于系统编程新手来说,Rust 的学习曲线比较陡峭,但这也换来了创建健壮高效代码的能力。大多数 Rust 开发都是使用 Anchor。Anchor 是一个有主见的框架,它通过减少模板、执行各种标准安全检查和简化(去)序列化过程来简化程序开发。这使得高级 Rust 知识对于入门变得不那么重要。就上下文而言,Anchor 文档建议用户熟悉Rust Book的前九章(即熟悉 Rust 基础知识)。Solana Playground多个 Anchor 教程,可以帮助你入门。

虽然 Rust 是首选,但开发人员并不局限于 Rust。C、C++ 和任何针对 LLVM 的 BPF 后端的语言(即任何可以编译成 BPF 字节码)都可以使用。对于那些熟悉 Vyper 等 Pythonic 语言的人来说,Seahorse Lang 允许开发人员用 Python 编写程序。在这里,开发人员既能获得 Python 的易用性,又能保留与用 Rust 编程相同的安全保证。你可以从 Seahorse UniversitySeahorse Cookbook 等几本有用的 Seahorse 教程开始在 Solana 上编程。

从以太坊迁移到 Rust 的开发人员并不局限于 Rust。虽然由于 Anchor 和 Seahorse 等框架广受欢迎并有安全保证,建议学习和使用这些框架,但这并不总是可行的。由于SolangNeon Labs最近开发了编译器和与 EVM 兼容的开发者环境,开发者可以在程序开发中使用 Solidity。Solidity 在 Solana 上有一个家园。要开始 Solidity 程序开发,我们需要深入了解 Solana 编程模型的细微差别。了解Solana的账户模型对开发人员的过渡至关重要,因为它是程序开发和网络交互的基础。

了解账户模型

以太坊将账户分为两大类:外部拥有账户(EOA)和合约账户。EOA 是用户的标准账户类型。它们由私钥控制,可以持有以太币余额、发送交易并与合约账户交互。另一方面,合约账户的不同之处在于其操作由嵌入其中的智能合约代码控制。合约账户不能自主发起交易,只能响应从 EOA 或其他合约账户收到的交易。

Solana 采用了更统一的账户模型,将账户视为持久存储数据的多元容器。这种模型允许任何账户成为程序,模糊了账户与智能合约之间的传统界限。以太坊将代码和状态合并到一个账户中,而 Solana 程序则不同,它是无状态的。这意味着它们不会在内部存储任何状态。相反,它们需要操作的所有数据都存储在一个单独的账户中,并通过引用与交易一起传递。通过引用传入账户,可以实现与不同账户交互的通用程序部署。Solana 的账户模型将代码和数据分开,从而营造了一个更高效、更模块化的开发环境。这对于希望与多个协议交互而无需在不同程序间移动资产的用户来说非常有利。Solana 上的一切都是一个账户。

Solana 账户可概括为可执行账户和不可执行账户。简单地说,可执行账户是能够运行代码的账户。不可执行账户用于存储数据,但不能执行代码。这是因为它们不存储任何代码。

Solana上的程序

可执行账户是存放程序的账户。程序可以拥有其他账户、从其他账户读取数据或将数据记入其他账户、修改数据或借记自己拥有的账户。可执行账户可进一步细分为链上程序和原生程序。链上程序是部署到网络上的用户编写的代码。它们可以通过升级授权进行升级,升级授权通常是部署它们的账户。原生程序是可执行账户的一个特殊子集,类似于以太坊的预编译合约,但具有更广泛的功能。这些程序被集成到 Solana 的核心中,为验证器的运行提供必要的功能。系统程序 就是原生程序的一个例子。该程序负责创建新账户、分配账户数据、将账户分配给程序、从其拥有的账户中转移 lamports 以及支付交易费用。原生程序的完整列表可在 此处 找到。

不管是可执行程序还是非可执行程序,所有账户都有相同的字段:

  • 一个 lamports 字段,用于跟踪账户在 SOL 中的本地余额
  • 数据(data)字段,指账户存储的原始数据字节数组
  • 所有者(owner)字段,表示可以修改该账户的程序
  • 签名者字段(signer),用于交易,表明账户是否可以授权交易
  • 一个可写字段,用于指定是否可以修改账户数据。这是为了便于并行处理,将交易中包含的账户标记为只读或可写。
  • 可执行字段,表示账户是否存储程序
  • 租金纪元(epoch)字段,用于表示账户下一个纪元所欠租金

租金是一种存储成本,用于维持账户在 Solana 上的存活,并确保它们被保存在验证器内存中。这就要求账户保持最低余额以保持活跃。租金可确保网络最终收回未使用或资金不足的账户,从而减少状态臃肿。最近的更新使主网上不再有支付租金的账户。相反,所有账户在创建时都必须是免租金账户。如果账户保持相当于两年租金的最低余额,就可以免租。可以使用Test DriveSolana CLI 的租金子命令等工具来估算账户免租所需的 SOL 金额。这与以太坊的资源分配系统不同,在以太坊的资源分配系统中,除非明确清除,否则存储会持续存在。Solana 的方法为状态存储提供了更可预测的成本结构,同时减少了状态膨胀。

地址和程序派生地址(PDA)

ed2559 椭圆曲线

来源:Solana Cookbook的"Program Derived Addresses (PDAs) "中的 Generating PDAs section

所有账户都由其地址(唯一的 32 字节公钥)标识。这与以太坊的数据模型不同,后者的地址为 20 字节。Solana 使用 ed25519(即使用SHA-512Curve22519 椭圆曲线的 EdDSA 签名方案)生成地址。账户必须是 ed25519 曲线上的一个点,才能拥有有效的密钥对。

程序派生地址(PDA)是使用跳值(即使输出偏离曲线的值)在曲线外生成的账户。PDA 需要三个主要部分:父程序地址、一组种子和跳值。种子是一个字符串数组,可以是任意值。不过,大多数开发人员会创建与父程序中的状态变量相关的特定种子,以创建类似哈希表的数据结构。因此,PDA 是通过使用 SHA-512 哈希函数对程序 ID、种子和跳值进行散列而创建的。

PDA 脱离曲线的目的是,只有 PDA 衍生出的程序才能代表 PDA 签名。这样可以通过程序生成交易签名来简化交易流程,从而使无信任的 dApp 无需干预即可顺利运行。

与账户交互

以太坊交易是由 EOA 发起的改变状态的行为。智能合约不能独立启动交易,只能对交易做出反应。合约代码决定了这些反应,交易执行的成本以Gas计量。用户必须提供足够的以太币来支付这笔Gas费用。

以太坊交易通常执行单一操作,例如调用特定的智能合约功能。虽然单个交易可导致合约内多个状态变化,但所有变化都仅限于该单个合约调用的范围。

Solana交易

在 Solana 上,交易 包括一个可读取或写入的账户数组、一个或多个签名以及一个或多个指令。指令是单个程序调用的指令。它是执行逻辑的最小单元,也是最基本的操作单元。指令指定了执行程序、涉及的所有账户和操作数据。程序解释指令中的数据,并对指定账户进行操作。这种结构使单个交易能以原子方式在多个程序中执行一系列操作。这意味着所有指令要么一起成功,要么一起失败。Solana 的交易流程不同于以太坊的模式,在以太坊模式中,交易通常与单个智能合约或 EOA 操作相关联。

跨程序调用(CPI) 使一个程序能够在同一交易中调用另一个程序。CPI 期间每个计算单元收取的账户数据字节数为 250。以 200,000 个计算单元计算,大约为 50MB。CPI 允许在程序间调用时使用程序生成的签名。这类似于以太坊智能合约以原子方式高效调用另一个合约。不过,调用程序会停止运行,直到被调用程序完成指令处理。重入限制为直接自递归,上限为固定的 4 深度。这可以防止程序从中间状态调用另一个程序,而不知道以后可能会被重新调用的情况。

传统账户查询与地址查询表

为了提高效率,Solana 交易遵守大小限制,类似于以太坊的Gas限制。不过,重点是数据大小。Solana 交易遵循IPv6 最大转账单元(MTU) 标准,以保证可靠的数据转账。预留必要的报头空间后,1232 字节可用于数据包数据。Solana 引入了版本化交易,支持多种交易格式,以克服大小限制。除了传统格式(即原始交易格式),发布的第 0 版还支持 Address Lookup Tables (ALTs)。ALTs 将地址存储在链上一个类似表格的数据结构中,其中每个地址都使用 1 字节 u8 索引进行索引。由于每个账户只需要 1 个字节,而不是 32 个字节,因此大大减少了交易大小。

Solang

img

Solang 是 Solana 和 Polkadot 的 Solidity 编译器。它的目标是实现源文件与 EVM 的 Solidity 编译器 0.8 版本的兼容性,尽管为了与 Solana 和 Polkadot 的体系结构保持一致做了一些改动。Solang的一个重要方面是它使用了LLVM这一强大的编译器基础架构。LLVM 的灵活性使 Solang 能够在未来扩展对其他编程语言的支持,从而简化实现和编译工作。这一特性符合 Solang 的目标,即简化开发人员向 Solana 或 Polkadot 的过渡,并扩大 Solidity 的开发范围。Solang 正在持续开发中,重点是提高兼容性、效率和用户友好性。对于希望掌握跨链技能的以太坊开发人员来说,了解 Solang 及其注意事项至关重要。

安装

有几种安装Solang的方法。在 Mac 上,用户可以通过 Brew 使用私人点选下载 Solang:brew install hyperledger/solang/solang。另一种安装 Solang 的方法是使用 Solang 容器。如果你更喜欢使用Docker,这就再好不过了,因为这些容器会自动提供新的镜像。有一个 v.0.3.3 标签和一个最新标签:docker pull ghcr.io/hyperledger/solang:latest

通过 Ancor 安装和使用 Solang 是最简单的方法之一。首先,请确保你的系统已安装 RustNode.js。Windows 用户还需要设置 Windows Subsystem for Linux。现在,我们需要安装 Solana's Tool Suite。在 Mac 和 Linux 上,可以使用以下命令完成安装:sh -c "$(curl -sSfL https://release.solana.com/v1.17.13/install) "

如果你喜欢不同的软件版本,请将 "v1.17.13 "替换为相应的版本标签。或者,你也可以使用以下符号通道名:稳定版、测试版或边缘版。根据系统情况,你可能需要更新 PATH 环境变量。如果遇到此信息,请复制并粘贴下面推荐的命令来更新 PATH。你可以通过运行 solana --version 来确认所需的 solana 版本。

在 Windows 系统中,以管理员身份打开 "命令提示符 "实例。复制并粘贴以下命令,将 Solana 安装程序下载到临时目录中:

cmd /c "curl https://release.solana.com/v1.17.13/solana-install-init-x86_64-pc-windows-msvc.exe --output C:\solana-install-tmp\solana-install-init.exe --create-dirs"

然后,复制并粘贴以下命令以安装最新版本的 Solana:C:\solana-install-tmp\solana-install-init.exe v1.17.13. 安装完成后,按 Enter 键。关闭命令提示符窗口,然后以普通用户身份重新打开一个新实例。运行 solana --version 确认所需的 solana 版本已安装。

接下来,安装 Anchor。建议使用 Anchor 版本管理器 (avm) 安装 Anchor。这可以通过 cargo 命令完成:cargo install -git https://github.com/coral-xyz/anchor avm --locked --force.然后,安装并使用最新版本:

avm install latest
avm use latest

# Verify the installation
avm –version

Anchor 0.28 版允许开发人员直接使用 Solang 构建。开发人员可以使用以下命令创建一个新的 Solang 项目:anchor init project_name -solidity.这会创建一个新的 Solang 程序和一个测试文件,演示如何通过客户端与程序交互。

如果你使用 Visual Studio Code,可以考虑安装 Solang 扩展,以协助语法高亮显示。禁用任何其他使用中的 Solidity 扩展,以确保 Solang 扩展正常工作。

创建新项目

使用 anchor-init project_name -solidity 命令创建一个新项目。这会创建一个以项目名称命名的新目录。Solidity 标志告诉 Anchor 我们要使用 Solang。项目的 ./solidity 目录将提供一个启动程序。合约将如下所示:

@program_id("F1ipperKF9EfD821ZbbYjS319LXYiBmjhzkkf5a26rC")
contract test {
    bool private value = true;

    @payer(payer)
    constructor() {
        print("Hello, World!");
    }

    /// A message that can be called on instantiated contracts.
    /// This one flips the value of the stored `bool` from `true`
    /// to `false` and vice versa.
    function flip() public {
            value = !value;
    }

    /// Simply returns the current value of our `bool`.
    function get() public view returns (bool) {
            return value;
    }
}

该程序的构造函数用于创建程序,将状态变量value初始化为true,并在程序日志中记录 "Hello, World!"。调用flip函数时会更新状态变量。get 函数返回状态变量的当前值。这看起来就像一个普通的 Solidity 智能合约,但有一些注意事项。最大的不同是使用了注解。

注解

注解用于账户管理。请注意@program_id("...")注解。如果事先知道程序的链上地址,该注解用于指定程序的链上地址。如果要通过外部调用来调用合约,程序必须使用@program_id注解,或者使用{program_id: ... }参数来调用:

@program_id(“...”);

contract Foo {
    function hello() public pure {
        print(“Hello”);
    }
}

contract Foo2 {
    function bye() public pure {
        print(“Bye”);
    }
}

contract Bar {
    function new_foo() external {
        Foo.new();
    }
}

contract Bar2 {
    function new_foo(address new_foo_id) external {
        Foo2.new{program_id: new_foo_id}();
    }
}

当我们创建一个新项目时,所提供的示例合约在构造函数上带有 @payer 注解。该注解定义了为程序的数据账户初始化付费的账户。@payer(payer)语法声明了一个名为payer的账户,每次调用构造函数时都需要使用该账户。

合约实例化时,需要一个程序账户来保存可执行代码,一个数据账户来保存状态变量。数据账户可以用客户端代码创建,然后传入调用构造函数的交易中。或者,数据账户也可以由构造函数创建。至少必须提供 @payer 注解。如果数据账户是 PDA,则必须提供种子和碰撞。种子@seed注解指定了派生 PDA 的种子,可以是字符串或格式为hex "1234"的十六进制字符串。如果种子注解位于参数之前,则必须指向字节地址或固定长度字节数组类型的参数。@bump 注解指定了用于生成偏离曲线地址的值。它必须是 bytes1 类型的单字节。可选的 @space 注解可用于指定数据账户的大小。它是一个 uint64 表达式,可以是一个常量,也可以使用构造函数参数之一。根据 Solang 文档,@space 至少应是运行 solang -v 命令时给出的大小:

$ solang compile --target solana -v examples/solana/flipper.sol
...
info: contract flipper uses at least 17 bytes account data
…

如果程序没有构造函数,这些注解可以与空构造函数配对。Solang 文档提供了 以下示例

@program_id("Foo5mMfYo5RhRcWa4NZ2bwFn4Kdhe8rNK5jchxsKrivA")
contract Foo {

    @space(500 + 12)
    @seed("Foo")
    @payer(payer)
    constructor(@seed bytes seed_val, @bump bytes1 bump_val) {
        // ...
    }
}

函数注解用于声明外部函数的必要账户:

  • @account(foo) 将 foo 账户声明为只读账户
  • @mutableAccount(bar)bar 账户声明为可变账户
  • @signer(fizz)fizz 账户声明为只读签名账户
  • @mutableSigner(buzz)buzz账户声明为可变签名账户

在构造函数中使用 @payer 注解声明的账户可在构造函数内部访问。使用函数注解声明的账户可在tx.account向量中使用。例如,使用 tx.accounts.ichigo 访问 @account(ichigo) 声明的账户。这将返回 AccountInfo 内置结构。该结构与我们在 "了解账户模型 "一节中概述的结构相同。命名略有不同:

  • key:账户的地址或公钥。其类型为地址
  • lamports:账户的lamports余额。类型为 uint64
  • data:账户数据。类型为bytes
  • owner:账户所有者。类型为 address
  • rent_epoch:账户租金到期的下一个纪元。类型为 uint64
  • is_signer:指定账户是否签署了交易。类型为 bool
  • is_writable:指定账户在此交易中是否可写。类型为 bool
  • executable:指定账户是否是一个程序。类型为 bool

限制

Solang 与传统的以太坊开发不兼容的地方主要有以下几点:

  • msg.sender在Solana上不可用。在账户模型中,Rust 合约可以访问各种数据账户,我们会将其中哪个账户视为调用者呢?在很多情况下,我们无法将某个账户确定为调用者。此外,运行时没有获取调用者账户的机制
  • 没有ecrecover()函数,但有一个signatureVerify()函数用于检查ed25519签名。
  • Try-catch 语句不起作用。如果任何外部调用或合约创建失败,运行时将停止执行并还原整个交易。
  • 错误定义和带错误信息的还原尚未运行
  • 使用函数调用转账附加 value 值不起作用
  • 许多 Yul 内置程序不可用。Solang 确实支持大多数内置程序;但内存和链操作尚未实现。
  • 目前不支持 ERC-20 格式。SPL 代币是根据代币程序定义的。代币程序是 Solana 创建、铸造、转移和销毁代币的原生方式。应将spl_token.sol文件复制到你的源代码树中,并在需要的地方导入以使用SplToken

此外,SVM 的寄存器为 64 位宽,这意味着 64 位整数(即 uint64int64)比 256 位整数更受欢迎。对于超过 64 位的操作,如uint256int256,会被分割成多个操作。这将导致运算速度变慢,并消耗更多的计算单元。

关于地址,必须使用address "36VtvSbE6jVGGQytYWSaDPG7uZphaxEjpJHUUpuUbq4D "语法指定地址常量。不支持以太坊的十六进制语法(例如0xE0f5206BBD039e7b0592d8918820024e2a7437b9)。Solana 上的所有余额和数值都是 64 位宽。这意味着地址内置函数(即 .balance().transfer().send())使用 64 位整数。

虽然 Solang 为 EVM 开发人员进入 Solana 生态系统提供了一个令人兴奋的途径,但它也有一些需要仔细考虑的局限性。向 Solana 基于账户的模型的转变不仅仅是语法上的,它代表着智能合约逻辑的根本性变化。Solang 无法提供某些功能、编码模式和 EVM 特有功能。开发人员不能将以太坊智能合约移植到 Solang 中,并期望它能在不做重大调整的情况下正常工作。功能缺失,如缺乏适当的错误定义和带错误信息的还原,可能会造成相当大的影响。不过,重要的是要认识到,Solang 是一个不断发展的工具,每次更新都旨在改善 Solana 上 Solidity 开发人员的体验。Solang 有几个明显的优势可以改善这种体验。

优势

尽管存在这些限制,但仍有许多内置功能和设计选择可改善开发人员的体验。例如,在 Solang 中开发的合约可以与 Anchor 程序交互。这可以通过从 Anchor 程序的 IDL 生成 Solidity 接口 来实现。IDL 是 "接口描述语言 "的缩写。从本质上讲,它是一个 JSON 文件,包含了程序的所有规范。它包含与 Anchor 程序交互所需的一切信息。在使用 Anchor 框架开发程序时,Anchor 会自动生成 IDL。这与以太坊上的 ABI 非常相似。要从 IDL 生成 Solidity 接口,请使用以下命令:solang idl [-output directory] [IDL file]。现在,可以使用 import "..."; 语法导入该文件。

Solang 提供了 Solana 库,这是一系列用于 Solidity 合约与 Solana 特定指令交互的库。Solang 提供了SPL 代币库,用于代币的铸造、燃烧和转移。它可以被视为ERC-20ERC-721 的等价物。Solang 还提供了系统指令库,以便开发人员与 Solana 的系统程序进行交互。

Solang 还包括一些可通过solana导入的内置功能。其中包括 AccountMetaAccountInfo 结构。AccountMeta 结构用于指定哪些账户应在外部调用(即 CPI)中传递给被调用者。AccountMeta 结构具有以下结构:

  • pubkey:账户的地址或公钥。其类型为 address
  • is_writable:指定被调用者是否可以写入该账户。类型为 bool
  • is_signer:指定收款人是否可以认为该账户签署了交易。类型为 bool

如果在外部调用中省略了 accounts 参数,Solang 编译器会自动生成一个 AccountMeta 数组。这只有在函数被声明为外部函数时才有效。否则,必须根据 IDL 中指定的账户排序手动创建 AccountMeta 数组。如果某次调用不需要账户,则传入一个空向量:{accounts:[]}.Solang 文档提供了一个可靠的示例,说明如何建立 AccountMetas 数组:

function build_this() external {
    // When calling a constructor from an external function, the data account for the contract
    // 'BeingBuilt' should be passed as the 'BeingBuilt_dataAccount' in the client code.
    BeingBuilt.new("my_seed");
}

function build_that(address data_account, address payer_account) public {
    AccountMeta[3] metas = [
        AccountMeta({
            pubkey: data_account,
      is_signer: true,
      is_writable: true
        }),
    AccountMeta({
        pubkey: payer_account,
      is_signer: true,
      is_writable: true
    }),
    AccountMeta({
      pubkey: address"11111111111111111111111111111111",
      is_writable: false,
      is_signer: false
    })
  ];
  BeingBuilt.new{accounts: metas}("my_seed");

    // No accounts are needed in this call, so we pass an empty vector.
    BeingBuilt.say_this{accounts: []}("It's summertime!");
}

Solang 还具有以下内置函数

Solang 在 Solana 和以太坊之间架起了一座桥梁,它提供了一个具有丰富工具集的编译器,为 EVM 开发人员的过渡提供了便利。尽管有其局限性,但 Solang 提供了多项功能来增强开发者的体验,包括与Anchor程序交互、访问 Solana 特定库和内置函数的能力。集成 IDL 和代币标准等熟悉的概念进一步简化了学习曲线。无需担心或优化Gas是一个值得欢迎的新增功能。Solang 代表着向 Solana 和以太坊之间的互操作性迈出了重要一步。对于希望扩展到 Solana 高性能世界的 EVM 开发人员来说,Solang 是一个可行的工具。

Neon EVM

Neon EVM

Solang 并非 Solidity 开发人员在 Solana 基础上开发的唯一选择。Neon EVM 将自己描述为世界上第一个可并行化的 EVM。它是 Solana 上完全兼容的以太坊环境。对于希望以开发人员友好的方式在 Solana 上扩容以太坊 dApp 的人来说,这是一个协同解决方案。开发人员可以部署他们的 dApp,无需重新配置他们的智能合约,使用他们喜欢的工具,用他们喜欢的语言编写。

架构

Neon EVM 由三个主要组件组成:Neon EVM 程序、Neon 代理和 Neon DAO。

Neon EVM 是一个 Solana 程序,它接受类似以太坊的交易,并根据 EVM 的规则在 Solana 上进行处理。这些指向 Neon EVM 的类以太坊交易被称为 Neon 交易。根据以太坊 JSON-RPC API,它们是JSON RPC 方法的子集

Neon 代理允许以太坊开发者将其 dApp 移植到 Neon,只需做最小的改动。它将 EVM 交易打包成 Solana 交易,作为 Neon 运营商的容器化解决方案。这些运营商运行 Neon Proxy 服务器,接受 NEON 中的支付,并在 SOL 中的 Solana 生态系统内进行支付。NEON 既是一种实用工具,也是一种治理代币:Neon 运营商收集它来支付交易执行所需的Gas费用,而所有者可以参与 Neon DAO。

Neon DAO 是一种社区驱动的治理模式,旨在授权 NEON 代币持有者参与 Neon EVM 的决策过程。它由用户、运营商、贡献者、核心和应用程序开发人员组成,他们共同商议治理规则和协议的发展。DAO 通过各种去中心化的议会运作,重点关注生态系统、开发和安全。每个议会都为各自领域的协作决策和提案审核提供便利。以生态系统为重点的议会负责监督生态系统的可持续增长,管理用于补助金和倡议的资金。以发展为重点的议会负责Neon计划的技术升级和紧急干预。安全委员议会负责保护Neon金库和Neon计划免受潜在威胁。

EVM 兼容性

Neon EVM 通过以下方式促进Solana上的 EVM 互动:

与Neon EVM 交互与与其他 EVM 交互类似。开发人员可以使用熟悉的 RPC API 方法,定向到 Neon 代理,促进无缝开发人员体验。主要功能包括

  • 兼容Solidity Vyper智能合约,以及标准以太坊开发工具,如MetamaskFoundryRemix
  • 逐字支持大多数以太坊操作码
  • 接受 type 0 型/传统以太坊交易请求。目前不支持 EIP-1559 交易

当然,为了使 Neon EVM 在 Solana 上正常运行,某些调整是必要的。值得注意的差异包括

  • Neon EVM 支持 evm.code 上定义的所有预编译合约。但是,包含以下调用的 Solidity 合约将无法执行:bigModExpbn256Addbn256ScalarMultbn256Pairing。Neon EVM 要求执行 Solana 系统调用,以便将来支持这些合约

  • 虽然大多数操作码都是逐字支持的,但 COINBASEPREVRANDAO (FKA DIFFICULTY)GASLIMITBASEFEEGAS 操作码不支持。这些操作码被称为变体操作码,经调整后用于 Neon EVM。

  • Gas消耗和费用计算与以太坊不同。由于 Solana 作为结算层的作用,它们通常会降低成本

  • 由于Gas计算方法不同,Solidity 的transfer()send()方法在 Neon EVM 中不具备重入安全性。

  • Solana 的账户模型影响智能合约的存储,可执行账户和不可执行账户具有不同的存储功能和访问权限。

  • Neon EVM 使用版本控制交易,将单个交易中使用的账户最大数量限制为 64 个

  • Neon EVM 使用 Solana 的 Berkley Packet Filter (BPF),堆内存限制为 256 KB。这限制了合约调用的大小,因此需要优化策略来有效管理内存使用情况

  • 基于时间的函数,如 block.numberblock.timestamp 的行为有所不同。强烈建议开发人员在 Neon EVM 上开发时不要使用这些函数。

虽然 Neon EVM 提供了一个熟悉、兼容的环境,但 EVM 开发人员必须了解并适应其中的关键差异,才能成功迁移并在 Solana 上构建。

连接到 Neon RPC

链表

开发人员可以使用 Chainlist轻松连接到 Neon RPC。在这里,开发者可以连接到 Neon EVM 主网或 Devnet。在 Mainnet 或 Devnet 模态上点击连接钱包,然后在钱包弹出时点击授权

在向 Neon EVM 发送交易之前,用户应选择最佳运营商。展开卡详细信息,查看每个网络的可用 RPC 端点:

img

如果你选择的运营商与钱包连接时提供的默认运营商不同,则需要手动连接。Neon EVM 文档提供了使用 FoundryHardhatRemixTruffle 连接代理的全面指南。我们将在部署部分介绍如何连接 Foundry。

连接后,开发者可以使用 Neon Faucet 获取 NEON 或其他 ERC-20 测试代币。开发人员还可以使用 request_neon 端点以编程方式请求代币:

curl -i -X POST \
    -d '{"wallet": "Your wallet", "amount": 1}' \
    'http://localhost:3333/request_neon'

该命令向指定的钱包地址发送接收代币的 POST 请求。

交易生命周期和Gas费用

通过 Neon EVM 从 Solana 上的以太坊 dApp 执行交易有三个主要步骤:

  • 用户向 Neon RPC 端点发起已签名的类以太坊交易
  • 交易通过以太坊 API 传递给 Neon 代理。代理估算执行交易所需的Gas,通过将类以太坊交易包装成 Solana 交易来启动广播,并将包装后的交易发送给 Neon EVM。这将产生一个 Solana 收据和一个相应的 Neon EVM 交易收据。Neon 智能合约会解除交易包裹,验证用户签名,并从 Solana 存储中加载 EVM 状态。交易在 Solana 的 BPF 中执行。
  • Solana 和 Neon EVM 更新其状态以完成交易请求

这就是 Neon EVM 中从启动到执行的整个交易生命周期。开发人员可以访问 NeonScan 查看最新的交易和区块,并查询账户、代币、区块或交易哈希值。

开发者还可以发送无Gas交易。实现这项功能是为了支持 NEON 代币不足以支付初始交易费用的用户。开发者可以通过联系 info@neonevm.org,获得无Gas交易的入门包。开发者选择的代理运营商仍将处理这些交易,但交易费用由 Neon 承担。每个新 Neon 帐户通常至少可进行三次无Gas交易。

无Gas交易的流程如下:

  • 最终用户通过 dApp 发起交易

  • dApp 向其选择的代理运营商请求当前Gas价格。如果符合条件,代理运营商会为指定数量的无Gas交易标记 Neon 账户

  • 对于这些交易,dApp 将显示Gas成本为零

  • 最终用户签署无Gas费交易

  • 代理运营商执行交易,SOL Gas费用由 Neon 基金会支付

Neon EVM 文档提供了以下摘要,以演示如何申请无Gas交易:

try {
    // Get gasless transaction if user account is eligible
  const rawGasPrice = await axios.post(rpcApiUrl, {
    method: 'neon_gasPrice',
    params: [{ from: address }],
    jsonrpc: "2.0",
    id: new Date().getTime()
   })

   tx.gasPrice = rawGasPrice.data?.result;
    } catch (e) {
    //Else, get standard GAS price
    setError('Can\'t retrieve gas price for transaction')

    const rawGasPrice = await web3.eth.getGasPrice();

    tx.gasPrice = web3.utils.toHex(rawGasPrice);
} finally {
    setTx(tx)
}

NeonPass

NeonPass是在 Solana 和 Neon EVM 之间转移代币的工具。它允许在 Solana 的 Associated Token Accounts 和 Neon EVM 的 ERC-20 代币账户之间进行无缝资产转移。NeonPass 使用 Neon EVM 的接口合约和专用账户存储。Solana 程序库 (SPL) 代币被打包到 Neon EVM 工厂合约内的 ERC-20 接口中。这样,SPL 就可以存储在与 Solidity dApp 兼容的 ERC-20 代币账户中。NeonPass 允许在 Solana 和 Neon EVM 账户之间双向转移代币。与锁定和铸造新资产的传统桥接器不同,它可以在两种账户类型之间直接转移代币。这实现了 Solana 和 Neon EVM 代币的无缝过渡。

部署

开发者可以使用 Hardhat、Foundry、TruffleRemix 部署到 Neon EVM。由于使用 Solang 的最简单方法是通过 Anchor,因此所有测试都在 TypeScript 中完成。不过,我们最终可以通过 Foundry 为所有 Solidity maxis 测试并将 Solidity dApp 部署到 Solana。

首先,确保你有一个与 EVM 兼容的钱包连接到 Neon EVM Devnet。然后,克隆 Neon 的示例 Foundry 项目并进入至该项目:

git clone https://github.com/neonlabsorg/neon-tutorials
cd neon-tutorials/foundry

然后,安装 Foundry 工具链安装程序 Foundryup,并运行 foundryup 安装最新(nightly)的 预编译二进制文件(即 force、cast、anvil 和 chisel):

curl -L https://foundry.paradigm.xyz | bash
foundryup

安装所需的库:

forge install foundry-rs/forge-std --no-commit
forge install openzeppelin/openzeppelin-contracts --no-commit

现在,获取钱包账户的私钥。例如,在 Metamask 中,点击汉堡菜单并导航至账户详情 > 显示私钥,即可查看私钥。系统会提示你输入密码。点击确认即可访问账户私钥。切记不要与任何人共享此密钥;请采取必要措施保护它。

然后,创建一个.env文件,其中包含以下变量:

RPC_URL_DEVNET=https://devnet.neonevm.org
CHAIN_ID_DEVNET=245022926
RPC_URL_MAINNET=https://neon-proxy-mainnet.solana.p2p.org
CHAIN_ID_MAINNET=245022934
PRIVATE_KEY=
VERIFIER_URL_BLOCKSCOUT=https://neon-devnet.blockscout.com/api

将 <YOUR_PRIVATE_KEY> 替换为你的私人密钥,然后运行 source .env

要编译项目合约,请导航到 src 目录并运行 forge build。控制台应返回编译器运行成功。还可以使用 forge test 命令测试合约。要部署项目的合约,请运行以下命令:

forge create --rpc-url $RPC_URL_DEVNET --private-key $PRIVATE_KEY src/TestERC20/TestERC20.sol:TestERC20 --constructor-args "Test ERC20 Token" "TERC20" --legacy

你应该会看到类似下面的输出:

[⠰] Compiling...
No files changed, compilation skipped
Deployer: 0x4455E84Eaa56a01676365D4f86348B311969a4f4
Deployed to: 0x5537599aa2F97Dd60a66342522a465A7f2e40Ff9
Transaction hash: 0x6de9dab8a526cbac33008056d185b93dff725605efb791bf116b6bece4f0c486

要验证你的合约,请运行以下命令:

forge verify-contract --chain-id $CHAIN_ID_DEVNET  src/TestERC20/TestERC20.sol:TestERC20 --verifier-url $VERIFIER_URL_BLOCKSCOUT --verifier blockscout

将 <contract_address> 替换为智能合约的地址。你应该会收到一个OK响应,其中包含一个指向Neon的 Devnet 浏览器 BlockScout 上合约地址的 URL。你也可以配置你的.env文件,使用 NeonScan 而不是 BlockScout,后者也支持 Devnet。

优点和限制

想要获得类似以太坊开发体验的开发者应该选择 Neon EVM。它是一个与 EVM 兼容的环境,可以发送类似以太坊的交易,并使用熟悉的工具。NeonPass 等功能、对大多数 EVM 操作码的支持以及无Gas交易的提供,使得向 Solana 的过渡变得更加容易。然而,Neon EVM 并不完美。它存在一定的局限性,例如需要改变智能合约逻辑以适应 Solana 的基础设施,在 Solana 的伯克利数据包过滤器(BFP)和账户模型的限制下运行,以及对操作码和预编译合约有一定的限制。要从 EVM 兼容环境成功部署到 Solana,了解和驾驭这些细微差别至关重要。

过去的迁移方式

要在非 EVM 链上部署大型以太坊协议,必须完成几项复杂的任务。即,聘请非 Solidity 工程师从头开始重建代码库,寻找可信赖的审计合作伙伴,重新审计新代码库,以及调整治理合约以执行 DAO 决策。这些工作有可能耗资数百万美元,并需要数月的专注工作。这对大多数人来说是不可行的。不过,这种情况曾经发生过。

Helium是一个LoRaWAN网络,旨在创建一个去中心化的无线基础设施,以支持物联网(IoT)设备。该网络使用热点(类似于微型基站的小型低功率设备,可远距离连接其他热点)来实现。Helium开发团队最初使用的是自己的第一层(L1)区块链,但他们建议通过HIP 70转移到Solana。此举将使 Helium 实现更高的正常运行时间、更强的可组合性和更快的用户体验,同时保持高水平的安全性和低使用成本。社区以压倒性多数投票赞成该提议,Helium 于 2023 年 4 月迁移到 Solana。Helium首席运营官斯科特-西格尔(Scott Sigel)将这次迁移描述为一次无聊 的事件。网络或Helium的基础设施没有出任何问题。这是工程师的梦想。团队还在文档中的一系列指南中概述了整个迁移过程。Helium 的迁移取得了巨大成功,并为社区带来了巨大利益。

RNP-002的社区投票

来源:[RNP-002 社区投票]()Community Vote for RNP-002

这次迁移并非孤立事件。全球首个去中心化 GPU 渲染平台 The Render Network 于 2023 年 11 月成功将其核心基础设施从以太坊升级到 Solana。社区在 RNP-002上投票赞成迁移到 Solana。Render 的创始人 Jules Urbach 将这次迁移描述为一个分水岭。他表示:"Solana令人难以置信的交易速度、低成本以及对网络规模架构的承诺,使其成为Render网络的完美选择,因为我们将继续建设可扩展的去中心化元宇宙基础设施"。

Render 的迁移并没有对其用户产生过于负面的影响。用户可以使用Render的升级助手将资金从以太坊桥接到Solana。用户连接自己的以太坊钱包,注明希望迁移的 RNDR 数量,然后等待代币被传送到指定的 Solana 钱包。

Maker是一个典型的以太坊项目。他们旨在通过 Maker 平台释放去中心化金融的潜力,该平台是一个包容性平台,旨在增强经济赋权和平等进入全球金融市场的机会。该平台由管理Maker项目的MakerDAO和DAI的Maker协议组成,DAI 是 "世界上第一个无偏见的货币和领先的去中心化稳定币"。Maker创始人鲁恩-克里斯滕森(Rune Christensen)在推特上表示,将使用Solana代码库的分叉来开发Maker应用链:

Solana推特上的Maker应用链潜力

来源:Rune Christensen’s 推特

迁移本身就很复杂。尽管将项目的整个基础设施从以太坊迁移到 Solana 需要付出金钱、声誉和时间成本,但个目还是选择了 Solana。有了Solang和Neon EVM等工具,迁移本质上并不复杂。就像Helium的迁移一样,它可以是枯燥的,对用户的资金几乎没有不利影响,就像Render网络一样。Solana 是市场上性能最强的区块链,专为网络规模而打造。这里就是构建的地方,越来越多的项目正在意识到这一点。

结论

Solidity 是智能合约开发的通用语言。自诞生以来,EVM 一直是智能合约的主流环境。不过,它也有自己的弱点。网络规模的消费级应用需要一个高吞吐量、低延迟的网络来支持其运行。以太坊的单线程、易波动的 Gas 无法支持像 Helium 这样的高吞吐量去中心化物理基础设施网络(DePIN)项目。

为了应对这些挑战,Solana 成为了一个强大的替代方案。利用 Solana 真正优势的最佳方式是在 Solana 的基础上进行实际构建。随着最近的发展,Solang 和 Neon EVM 等工具允许感兴趣的 EVM 开发人员使用熟悉的工具和语言进行迁移。本文全面介绍了 Solana 的架构、它与以太坊的比较,以及 EVM 开发人员如何在 Solana 上开始开发。Solana 是市场上性能最强的区块链。它的发展势头越来越强劲,市场占有率越来越高,消费者级应用的声誉也越来越好。既然现在就可以享受快速、可扩展的区块链带来的好处,为什么还要等待以太坊的扩展呢?

如果你已经读到这里,谢谢你,Anon!请务必在下面输入你的电子邮件地址,这样你就不会错过有关 Solana 的最新消息。准备深入了解?加入我们的Discord,今天就开始在最具性能的区块链上构建未来。

更多资源

点赞 1
收藏 4
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

1 条评论

请先 登录 后评论
翻译小组
翻译小组
0x9e64...7c84
大家看到好的文章可以在 GitHub 提 Issue: https://github.com/lbc-team/Pioneer/issues 欢迎关注我的 Twitter: https://twitter.com/UpchainDAO