以太坊/SOLANA迁移权威指南
以太坊的单线程、基于gas的脆弱环境无法支持高吞吐量需求。Solana 是市场上性能最高的区块链。本文提供了有关 Solana 架构的全面指南、它与以太坊的比较,并提出了 EVM 开发人员如何开始在 Solana 上进行开发。
一键发币: SOL | BNB | ETH | BASE | Blast | ARB | OP | POLYGON | AVAX | FTM | OK
以太坊是近年来最重要的创新之一。 历史上第一次,我们拥有了一个为社会协调而构建的去中心化全球平台,有可能彻底改变许多行业。 尽管很重要,但以太坊的运行时环境以太坊虚拟机(EVM)在当前状态下并不是为消费级应用程序构建的。 它是一个单线程、基于 Gas 的网络,费用不稳定。 相比之下,Solana 是一个高吞吐量、低延迟的网络。 它提供了一个并行基础设施,费用低廉且可预测。 它直接解决了 EVM 的局限性并改进了其原始设计,使其成为寻求构建可扩展且高效应用程序的开发人员的一个令人信服的选择。
本文是针对想要在 Solana 上进行构建的感兴趣的 EVM 开发人员的综合迁移指南。 它涵盖了两者之间的根本区别,研究了以太坊和 Solana 的共识机制、它们如何处理交易以及用于开发智能合约的语言。 然后,它涵盖了 Solana 的账户模型,该模型提出了一种更加统一和多方面的账户方法。 本文还探讨了 Solang 和 Neon EVM,这两个 Solidity 友好的工具可增强 Solana 开发体验。
1、以太坊和Solana的根本差异
本节探讨以太坊和 Solana 之间的关键差异,这两个区块链旨在创建用于全球协调的去中心化状态机。 然而,两者在共识机制、交易处理方法以及用于智能合约开发的语言方面存在显着差异。 对这些根本差异的更深入理解揭示了 Solana 作为高性能全局状态机相对于以太坊的独特优势。
1.1 共识机制
共识机制支撑着每个区块链网络。 他们负责确定如何验证交易以及如何以安全、高效和去中心化的方式将区块添加到区块链中。 以太坊和 Solana 都是权益证明 (PoS) 网络。 虽然它们在 PoS 上有共同的基础,但由于确认规则不同,它们达成共识的方法也有所不同。
以太坊使用 Gasper,它是 Casper theFriendly Finality Gadget (Casper-FFG) 和 LMD-GHOST 分叉选择算法的组合。 这种组合形成了保护以太坊的共识机制。
Casper 是一个基于 PoS 的最终确定系统,它将区块的确认状态升级为“最终确定”。 当总权益的三分之二投票赞成包含该区块时,该区块就被视为在以太坊上最终确定,并且另一个区块将建立在该区块之上。 由于最终性需要总权益的三分之二同意一个区块是规范的,因此攻击者在不拥有或操纵总权益的三分之二并通过削减销毁至少三分之一的情况下无法创建替代的最终链。 Caspers 允许新进入者自信地与规范链同步。
LMD-GHOST 是“最新消息驱动贪婪最重观察子树”的缩写。 它是一种决定遵循以太坊哪个分支的算法。 它通过选择从网络验证者那里获得最多支持(即“权重”)的分叉(因此是“贪婪最重子树”)来实现这一点。 然后,它确保仅考虑来自每个验证器的最新消息。 每次向以太坊提出新区块时,验证者都会使用此规则来决定它是否应该成为规范链的一部分。
相比之下,Solana 使用历史证明 (PoH:Proof of History) 哈希来达成共识。 尽管名字令人困惑,PoH 并不是一种共识算法。 这是在对抗性网络中证明时间的一种方法。 更具体地说,它是一种加密时间戳功能,允许节点在不相互通信的情况下就事件顺序达成一致。 领导者(即,将条目附加到分类帐的验证器)将时间戳应用于块,以证明自上一个块以来已经过去了一段时间。
添加这些时间戳会建立历史记录,因为它们证明数据在特定时间存在。 历史记录是使用顺序抗原像散列函数创建的,其中每个散列取决于其前一个散列。 可验证延迟函数(VDF)对于生成这些哈希值至关重要。 他们确保每个哈希值都建立在上一个哈希值的基础上,并包含自那时以来经过的时间。 VDF 的集成为哈希过程引入了时间维度,从而可以在 Solana 上创建可验证的时间戳事件序列。
确定了 Solana 的历史证明机制如何通过加密时间戳创建可靠的事件序列后,了解它如何与 Solana 的共识机制 Tower BFT 集成至关重要。 Tower 拜占庭容错 (BFT) 是 Solana 传统拜占庭容错共识模型的变体,针对 PoH 进行了优化。 Tower BFT 使用 PoH 创建的历史记录作为参考框架。 该框架使验证者能够高效、准确地对账本状态进行投票。 Tower BFT 的功能是让验证者进行长期锁定的投票,随着投票的增加,他们会变得更加坚定。 由于 PoH,验证者可以对账本状态做出更快、更明智的决策,而无需相互通信。 PoH和Tower BFT的结合可以加速共识并增强网络的安全性和可靠性。 其结果是一个高吞吐量、可扩展的区块链环境,可以快速确认交易。
1.2 事务处理和执行环境
区块链的效率和可扩展性主要取决于其交易处理能力和执行环境。 这些因素决定了交易执行的速度以及网络上操作的成本效率。 区块链的交易处理方法及其执行环境的细微差别会显着影响开发人员的体验。
以太坊的运行时环境作为确定性的单线程堆栈机运行,称为以太坊虚拟机(EVM)。 它的行为就像一个数学函数。 也就是说,EVM 在给定输入时会产生确定性输出。 将以太坊定义为具有状态转换函数是有帮助的:f(S, T) = S’。 给定旧状态 (S) 和一组新的有效交易 (T),EVM 会生成新的有效输出状态 (S’)。 给定相同的交易集,EVM 将始终达到相同的最终状态。 这对于保持网络节点之间的一致性至关重要。
EVM 按顺序处理交易。 顺序处理可确保每笔交易在准确反映截至该点的网络状态的环境中执行。 顺序执行允许精确的状态变化和天然气成本计算。 这种可预测性使开发人员和用户能够清楚地了解交易结果和成本。
以太坊通过采用单线程执行模型简化了智能合约执行环境。 这为开发人员提供了可预测状态变化的优势,因为每个事务都是按顺序处理的。 可预测的状态变化使得以太坊的开发环境可以说更适合新开发人员,因为他们可以更多地关注智能合约逻辑而不是执行的复杂性。 然而,这种简单性给可扩展性带来了挑战。 顺序处理限制了网络的吞吐量。 这会导致潜在的拥堵、更长的交易等待时间以及高需求期间更高的汽油费。 由于每笔交易都会消耗 Gas,开发人员被迫优化 Gas 效率。 进行优化是为了改善整体用户体验并消除拥塞时间,低效的代码可能会导致应用程序对普通用户无法使用。
Solana 开发人员不必像以太坊开发人员那样担心优化 Gas 使用。 Solana 被设计为高吞吐量、低延迟状态机,称为 Solana 虚拟机 (SVM)。 SVM 的一个重要组成部分是 Sealevel。 它是一个并行处理事务的运行时引擎。 在 Solana 上,每个事务都会告诉运行时它将在执行时读取或写入状态的哪些部分。 运行时并行处理非冲突事务和读取相同状态的事务。 Sealevel 通过在验证器硬件上的多个线程之间分配交易工作负载来优化智能合约的执行。 因此,当验证器在一个核心上处理一笔交易时,另一笔交易可以同时在另一个核心上处理。
Solana 天生的并行性显着降低了交易成本。 Solana 交易有两项费用:基本费用和优先费用。 每个签名的基本费用固定为 5000 个 lamp; 大多数交易只有一个签名。 优先权费用是可选的,允许交易优先于其他交易。 具有较高优先级费用的交易由调度程序不确定地确定优先级。 交易费用通常低于 0.001 美元,平均无投票权费用在 0.000005 至 0.00007 SOL 范围内,由于最近使用优先费的增加,上限高于平常。 按照当前 SOL 价格约 98.96 美元计算,该费用大约相当于 0.000494 美元至 0.006968 美元。
这些费用也是可以预见的。 Solana 使用本地化费用市场(localized fee markets)来管理需求。 区块空间的结构是为了防止任何单独的活动热点(例如,大肆宣传的 NFT 铸币厂)主导区块空间并提高整个网络的费用。 只有尝试访问特定高需求热点的交易才会增加费用。 本地化的费用市场允许优先收费,而不会导致全面的gas战争。 这种本地化与以太坊等基于gas的网络不同,在以太坊中,交易是按顺序处理的,全球拥堵会导致费用波动。
以太坊是一种单线程运行时环境,一次仅处理一个合约。 它目前没有利用现代多核硬件,导致验证器硬件利用率不足。 对节点保持较低硬件要求的愿望增加了以太坊的交易处理限制。 相比之下,Solana 的并行处理能力允许 Solana 通过利用验证器的所有可用核心来处理更多交易。 与本地化的费用市场相结合,Solana 是一个性能更高的全球状态机。
1.3 智能合约语言
以太坊上智能合约开发的主要语言是 Solidity。 它是一种静态类型语言,旨在创建在 EVM 上运行的智能合约。 它深受 JavaScript、C++ 和 Python 的影响,使熟悉这些语言的开发人员很容易上手。
Yul 是一种针对 EVM 兼容区块链优化的中低级语言。 Yul 为开发人员提供了对字节码执行的更大控制,证明在微调 Gas 消耗和管理其他低级操作方面非常高效。 开发人员可以通过直接在 Yul 中编码或将其用作编译目标来创建更高效的合约。
Vyper 是一种流行的 Pythonic 语言,强调简单性和安全性。 它故意比 Solidity 拥有更少的功能,因为它的目的是降低复杂性和潜在的安全漏洞。 Vyper 的设计理念首先强调可读性和可审核性。 这种方法启发了类似语言(例如 Fe)和编译为 Vyper 的语言(例如 Dasy)的开发。
Rust 是 Solana 上智能合约(俗称程序)开发的通用语言。 它是一种快速、内存高效的语言,性能可与 C++ 相媲美。 这些特性使其适合开发高吞吐量、低延迟网络的应用程序。 Rust 丰富的类型系统和所有权模型确保了内存和线程安全,迫使开发人员构建可靠且安全的应用程序。
虽然 Rust 对于系统编程新手来说有一个陡峭的学习曲线,但代价是创建健壮且高效的代码的能力。 大多数 Rust 开发都是使用 Anchor 完成的。 Anchor 是一个固执己见的框架,它通过减少样板文件、执行各种标准安全检查以及简化序列化(反)序列化过程来简化程序开发。 这使得高级 Rust 知识对于入门来说不再那么重要。 对于上下文,Anchor 文档建议用户熟悉 Rust Book 的前九章(即熟悉 Rust 基础知识)。 Solana Playground 有几个 Anchor 教程可帮助你入门。
虽然 Rust 是首选,但开发人员并不局限于此。 可以使用 C、C++ 以及任何针对 LLVM BPF 后端的语言(即任何可以编译为 BPF 字节码的语言)。 对于熟悉 Vyper 等 Python 语言的人来说,Seahorse Lang 允许开发人员使用 Python 编写程序。 在这里,开发人员可以获得 Python 的易用性,同时保留与使用 Rust 编码相同的安全保证。 一些有用的 Seahorse 教程(例如 Seahorse University 和 Seahorse Cookbook)可帮助你立即开始在 Solana 上进行编程。
从以太坊迁移到 Rust 的开发人员并不局限于 Rust。 虽然由于 Anchor 和 Seahorse 等框架的流行和安全保证,建议学习和使用它们,但这并不总是可行。 得益于 Solang 和 Neon Labs 最近开发的编译器和与 EVM 兼容的开发环境,开发人员可以在程序开发中使用 Solidity。 Solidity 在 Solana 上有一个家。 要开始 Solidity 程序开发,我们需要更深入地了解 Solana 编程模型的细微差别。了解 Solana 的帐户模型对于开发人员进行这一转变至关重要,因为它构成了程序开发和网络交互的基础。
2、了解账户模型
以太坊将账户分为两大类:外部账户(EOA)和合约账户。 EOA 是用户的标准帐户类型。 它们由私钥控制,可以持有以太币余额、发送交易并与合约账户交互。 另一方面,合约账户的独特之处在于它们的操作由嵌入其中的智能合约代码控制。 它们无法自主发起交易,只能响应从 EOA 或其他合约账户收到的交易。
Solana 采用更统一的账户模型,将账户视为持久存储数据的多层面容器。 这种模型允许任何账户都是一个程序,模糊了账户和智能合约之间的传统界限。 与代码和状态合并到单个帐户的以太坊不同,Solana 程序是无状态的。 这意味着它们不会在内部存储任何状态。 相反,他们操作所需的所有数据都存储在单独的帐户中,并通过交易引用传递。 通过引用传递帐户允许进行一种可以与不同帐户交互的通用程序部署。 Solana 的帐户模型将代码和数据分开,形成更高效、模块化的开发环境。 这对于想要与多个协议交互而不在不同程序之间移动资产的用户来说是有利的。 Solana 上的一切都是一个帐户。
Solana 帐户可以概括为可执行帐户和不可执行帐户。 简而言之,可执行帐户是能够运行代码的帐户。 不可执行帐户用于数据存储,但无法执行代码。 这是因为它们不存储任何代码。
可执行帐户是保存程序的帐户。 程序可以拥有额外的帐户、从其他帐户读取或记入其他帐户、修改数据或借记其拥有的帐户。 可执行帐户可以进一步细分为链上(on-chain)或本机(native)程序。 链上程序是部署到网络的用户编写的代码。 它们可以通过其升级权限进行升级,该权限通常是部署它们的帐户。 本机程序是可执行帐户的特殊子集,类似于以太坊的预编译合约,但具有更广泛的功能。 这些程序集成到 Solana 的核心中,为验证器运行提供必要的功能。 本机程序的一个示例是系统程序(system program)。 该程序负责创建新帐户、分配帐户数据、将帐户分配给程序、从其拥有的帐户转移lamport以及支付交易费用。 可以在此处找到本机程序的完整列表。
无论可执行还是不可执行,所有帐户都具有相同的字段:
- lamports 字段,用于在 SOL 中跟踪帐户本机余额的
- data字段,指账户存储的原始数据字节数组
- owner字段,指示可以修改此帐户的程序
- signer字段,用于交易时指示账户是否可以批准交易
- writable字段,用于指定是否可以修改帐户的数据。 这是为了通过将交易中包含的帐户标记为只读或可写来促进并行处理
- executable字段,指示帐户是否存储程序
- rent epoc字段,指示帐户欠租金的下一个纪元
租金(rent)是一种存储成本,用于使帐户在 Solana 上保持活动状态并确保它们保存在验证器内存中。 这要求账户保持最低余额才能保持活跃。 租金通过确保网络最终恢复未使用或资金不足的账户来减少状态膨胀。 最近的更新使得主网上没有支付租金的帐户。 相反,所有帐户在创建时都必须免租(rent-exempt)。 如果账户的最低余额相当于两年的租金,则该账户可以免租。 Test Drive 和 Solana CLI 的租金子命令等工具可用于估计帐户免租金所需的 SOL 数量。 这与以太坊的资源分配系统不同,在以太坊的资源分配系统中,除非明确清除,否则存储将持续存在。 Solana 的方法为状态存储提供了更可预测的成本结构,同时减少了状态膨胀。
3、地址和程序派生地址 (PDA)
所有帐户均通过其地址(唯一的 32 字节公钥)进行识别。 这与以太坊的数据模型不同,以太坊的地址为 20 字节。 Solana 使用 ed25519(即使用 SHA-512 和 Curve22519 椭圆曲线的 EdDSA 签名方案)来生成地址。 帐户必须是 ed25519 曲线上的点才能拥有有效的密钥对。
程序派生地址 (PDA:Program Derived Address) 是使用bump(即,将输出偏离曲线的值)在曲线外生成的帐户。 PDA 需要三个主要部分:其父程序的地址、一组种子和一个bump。 种子是一个可以是任意值的字符串数组。 然而,大多数开发人员会在父程序中创建与状态变量相关的特定种子,以创建哈希图式的数据结构。 因此,通过使用 SHA-512 散列函数对程序 ID、种子和bump进行散列来创建 PDA。
PDA 偏离曲线的目的是只有派生 PDA 的程序才能代表 PDA 进行签名。 这通过以编程方式生成交易签名来简化交易流程,以便无需信任的 dApp 可以在无需干预的情况下顺利运行。
4、与帐户交互
以太坊交易是由 EOA 发起的状态更改操作。 智能合约无法独立发起交易; 他们只能对它们做出反应。 合约的代码规定了这些反应,交易执行的成本以gas为单位。 用户必须提供足够的以太币来支付这笔汽油费。
以太坊交易通常执行单个操作,例如调用特定的智能合约函数。 尽管单个事务可能会导致合约内的多个状态更改,但所有更改都仅限于该单个合约调用的范围。
在 Solana 上,一笔交易包含一组要读取或写入的帐户、一个或多个签名以及一个或多个指令。 指令(instruction)是单个程序调用的指令。 它是执行逻辑的最小单位,也是最基本的运算单位。 指令指定执行程序、涉及的所有帐户以及操作数据。 程序解释指令中的数据并对指定的帐户进行操作。 这种结构使单个事务能够以原子方式跨多个程序执行一系列操作。 这意味着所有指令要么一起成功,要么一起失败。 Solana 的交易流程与以太坊的模型不同,在以太坊的模型中,交易通常与单个智能合约或 EOA 操作相关联。
跨程序调用 (CPI:Cross Program Invokations) 使一个程序能够在同一事务期间调用另一个程序。 在 CPI 期间收取的每个计算单元的帐户数据字节数为 250。按 200,000 个单元计算,这大约为 50MB。 CPI 允许在程序之间调用时使用以编程方式生成的签名。 这类似于以太坊智能合约高效且原子地调用另一个合约。 然而,调用程序将暂停,直到被调用程序完成对指令的处理。 可重入性仅限于直接自递归,上限为固定深度 4。这可以防止程序从中间状态调用另一个程序而不知道它稍后可能会被回调的情况。
Solana 交易遵守大小限制以提高效率,类似于以太坊的 Gas 限制。 然而,重点是数据大小。 Solana 交易遵循 IPv6 最大传输单元 (MTU) 标准,以保证可靠的数据传输。 预留必要的报头空间后,1232 字节可用于数据包数据。 Solana 引入了版本化事务来支持多种事务格式以克服大小限制。 除了传统格式(即原始事务格式)之外,还发布了版本 0 以支持地址查找表 (ALT)。 ALT 将地址存储在链上类似表的数据结构中,其中每个地址都使用 1 字节 u8 索引进行索引。 这显着减少了交易大小,因为每个账户只需要 1 个字节而不是 32 个字节。
5、Solang
Solang 是 Solana 和 Polkadot 的 Solidity 编译器。 它的目标是实现与 EVM Solidity 编译器 0.8 版本的源文件兼容性,尽管有一些变化以与 Solana 和 Polkadot 的架构保持一致。
Solang 的一个重要方面是它对 LLVM(一种强大的编译器基础设施)的使用。 LLVM 的灵活性使 Solang 能够在未来扩展对其他编程语言的支持,从而促进更轻松的实现和编译。 这一特性与 Solang 的目标一致,即简化开发人员向 Solana 或 Polkadot 的过渡,并扩大 Solidity 开发的范围。 Solang 正在不断开发,重点是增强兼容性、效率和用户友好性。 对于希望跨链运用技能的以太坊开发人员来说,了解 Solang 及其注意事项至关重要。
5.1 安装Solang
有几种安装 Solang 的方法。 在 Mac 上,用户可以使用Brew:
brew install hyperledger/solang/solang
安装 Solang 的另一种方法是使用 Solang 容器。 如果你更喜欢使用 Docker,那么这是完美的选择,因为新镜像会自动在这些容器上可用。 有一个 v.0.3.3 标签和一个最新标签:
docker pull ghcr.io/hyperledger/solang:latest
安装和使用 Solang 的更简单方法之一是通过 Ancor。 首先,确保你的系统上安装了 Rust 和 Node.js。 Windows 用户还需要为 Linux 设置 Windows 子系统。 现在,我们需要安装 Solana工具套件。 在 Mac 和 Linux 上,可以使用以下命令完成此操作:
sh -c "$(curl -sSfL https://release.solana.com/v1.17.13/install)"
如果你喜欢不同的软件版本,请将“v1.17.13”替换为适当的版本标签。 或者,你可以使用以下符号通道名称之一:stable、beta 或 edge。 根据你的系统,可能需要更新 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 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 扩展正常工作。
5.2 创建一个新项目
使用命令 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;
}
}
该程序有一个构造函数来创建程序,将状态变量值初始化为 true,并记录“Hello, World!” 到程序日志。 flip
函数在调用时更新状态变量。 get
函数返回状态变量的当前值。 这看起来就像一个普通的 Solidity 智能合约,但有一些注意事项。 最显着的区别是注解的使用。
5.3 Solang注解
注解用于帐户管理。 请注意 @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,则必须提供种子和bump。 @seed
注解指定用于派生PDA的种子,并且可以是字符串文字或具有十六进制“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.accounts
向量中找到。 例如,你可以使用 tx.accounts.ichigo
来访问声明为 @account(ichigo)
的帐户。 这将返回 AccountInfo
内置结构。 该结构遵循我们在“理解账户模型”部分中概述的相同结构。 命名有点不同:
key
:账户的地址或公钥。 它的类型是addresslamports
:账户的lamport余额。 它的类型为 uint64data
:帐户的数据。 它的类型是bytesowner
:帐户的所有者。 它的类型是addressrent_epoch
:账户租金到期的下一个纪元。 它的类型为 uint64is_signer
:指定账户是否签署了交易。 它的类型是boolis_writable
:指定该账户在本次交易中是否可写。 它的类型是boolexecutable
:指定该帐户是否是一个程序。 它的类型是bool
5.4 Solang局限性
Solang与传统以太坊开发的主要不兼容之处是:
msg.sender
在 Solana 上不可用。 通过账户模型,Rust 合约可以访问各种数据账户,我们会考虑哪些数据账户是调用者? 在很多情况下,我们无法将单个帐户识别为调用者。 此外,运行时没有获取调用者帐户的机制- 没有
ecrecover()
函数,但存在检查 ed25519 签名的signatureVerify()
函数。 - Try-catch 语句不起作用。 如果任何外部调用或合约创建失败,运行时将停止执行并恢复整个事务。
- 错误定义和带有错误消息的恢复尚不工作
- 通过函数调用传输原生值不起作用。
- 许多 Yul 内置插件不可用。 Solang 确实支持大多数内置函数; 然而,内存和链操作没有实现。
- 目前不支持 ERC-20 格式。 SPL 代币是根据Token Program定义的。
Token Promgra
是 Solana 创建、铸造、转移和销毁代币的原生方式。 应将 spl_token.sol文件复制到源代码树中,并在需要使用 SplToken 库时导入
此外,SVM 的寄存器是 64 位宽,这意味着 64 位整数(即 uint64 和 int64)优于 256 位整数。 类型宽于 64 位的操作(例如 uint256 或 int256)会被拆分为多个操作。 这使得它们速度更慢并且消耗更多的计算单元。
关于地址,必须使用如下语法指定地址字符串:
address"36VtvSbE6jVGGQytYWSaDPG7uZphaxEjpJHUUpuUbq4D"
不支持以太坊的十六进制语法,例如 0xE0f5206BBD039e7b0592d8918820024e2a7437b9
。 Solana 上的所有余额和值都是 64 位宽。 这意味着地址的内置函数(即 .balance()
、 .transfer()
和 .send()
使用 64 位整数。
虽然 Solang 为 EVM 开发人员提供了一条进入 Solana 生态系统的令人兴奋的途径,但它有一些限制,需要仔细考虑。 向 Solana 基于账户的模型的转变不仅仅是语法上的,它代表了智能合约逻辑的根本性变化。 Solang 不提供某些功能、编码模式和 EVM 特定功能。 开发人员无法将以太坊智能合约移植到 Solang 并期望它无需重大调整即可运行。 缺失的功能,例如缺乏正确的错误定义和带有错误消息的恢复,可能是非常糟糕的。 然而,重要的是要认识到 Solang 是一个不断发展的工具,每次更新都旨在改善 Solana 上的 Solidity 开发人员体验。 Solang 有几个明显的优势可以让这种体验变得更好。
5.5 Solang的优点
尽管存在这些限制,仍有许多内置功能和设计选择可以改善开发人员的体验。 例如,用 Solang 开发的合约可以与 Anchor 程序交互。 这可以通过从 Anchor 程序的 IDL 生成 Solidity 接口来完成。 IDL 代表“接口描述语言”。 本质上,它是一个包含程序所有规范的 JSON 文件。 它包括与 Anchor 程序交互所需了解的所有内容。
Anchor 在使用其框架开发程序时会自动生成 IDL。 这与以太坊上的 ABI 非常相似。 要从 IDL 生成 Solidity 接口,请使用以下命令: solang idl [-output directory] [IDL file].
。 现在,可以使用 import "...";
来导入文件。
Solang 提供了 Solana 库,这是一系列用于 Solidity 合约与 Solana 特定指令交互的库。 Solang 提供了用于铸造、销毁和转移代币的 SPL 代币库。 它可以被认为是 ERC-20 和 ERC-721 的等效项。 Solang 还提供系统指令库,以便开发人员可以与 Solana 的系统程序进行交互。
Solang 还包括一些可以通过 solana 导入的内置功能。 这包括 AccountMeta
和 AccountInfo
结构。 AccountMeta
结构用于指定应在外部调用(即 CPI)中将哪些帐户传递给被调用者。 AccountMeta
具有以下结构:
pubkey
:账户的地址或公钥。 它的类型是地址is_writable
:指定被调用者是否可以写入该帐户。 它是 bool 类型is_signer
:指定被调用者是否可以假定该帐户签署了交易。 它是 bool 类型
如果在外部调用中省略 accounts
参数,Solang 编译器会自动生成一个 AccountMeta
数组。 仅当该函数声明为外部函数时才有效。 如果没有,则必须根据 IDL 中指定的帐户顺序手动创建 AccountMeta
数组。 如果某个调用不需要 accounts
,则传入一个空向量: {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 还具有以下内置函数:
- minimum-balance:计算创建特定帐户所需的最低余额
- create-program-address:根据提供的种子和凹凸为程序地址创建 PDA
- try-find-program-address:根据提供的种子和凹凸查找程序地址的 PDA
Solang 通过为编译器提供丰富的工具集来弥合 Solana 和以太坊之间的差距,从而简化 EVM 开发人员的过渡。 尽管存在局限性,Solang 仍提供了多种功能来增强开发人员体验,包括与 Anchor 程序交互、访问 Solana 特定库和内置函数的能力。 集成 IDL 和代币标准等熟悉的概念进一步简化了学习曲线。 不必担心或优化gas是一个受欢迎的补充。 Solang 代表 Solana 和以太坊之间的互操作性迈出了重要一步。 对于希望扩展到 Solana 高性能世界的 EVM 开发人员来说,Solang 是一种可能的工具。
6、Neon EVM
对于希望在 Solana 上构建的 Solidity 开发人员来说,Solang 并不是唯一的选择。 Neon EVM 将自己描述为世界上第一个可并行 EVM。 它是 Solana 上完全兼容的以太坊环境。 对于任何希望以开发人员友好的方式在 Solana 上扩展以太坊 dApp 的人来说,这是一个协同解决方案。 开发人员可以部署他们的 dApp,而无需重新配置智能合约,并使用他们喜欢的工具用他们喜欢的语言编写。
6.1 Neon EVM架构
Neon EVM 由三个主要组件组成:Neon EVM 程序、Neon Proxy 和 Neon DAO。
Neon EVM 是一个 Solana 程序,它接受类似以太坊的交易,并根据 EVM 的规则在 Solana 上处理它们。 这些针对 Neon EVM 的类似以太坊的交易被称为 Neon 交易。 它们是以太坊 JSON-RPC API 的 JSON RPC 方法的子集。
Neon 代理允许以太坊开发人员以最小的更改将他们的 dApp 移植到 Neon。 它将 EVM 交易打包到 Solana 交易中,充当 Neon Operators 的容器化解决方案。 这些运营商运行 Neon 代理服务器,接受 NEON 支付,并在 Solana 生态系统中使用 SOL 进行付款。 NEON 既是一种实用工具,也是一种治理代币:Neon 运营商收集它来支付交易执行所需的 Gas 费,并且所有者可以参与 Neon DAO。
Neon DAO 是一种社区驱动的治理模型,旨在授权 NEON 代币持有者参与 Neon EVM 的决策过程。 它由用户、运营商、贡献者、核心和应用程序开发人员组成,他们共同审议治理规则和协议的演变。 DAO 通过各种专注于生态系统、开发和安全的去中心化组件进行运作。 每个大会都促进各自领域的协作决策和提案审查。 以生态系统为中心的大会负责监督生态系统的可持续增长,管理赠款和倡议资金。 以开发为重点的大会处理 Neon 计划的技术升级和紧急干预措施。 以安全为重点的大会保护 Neon 的金库和 Neon 项目免受潜在威胁。
6.2 Neon EVM兼容性
Neon EVM 通过以下方式促进 Solana 上的 EVM 交互:
- 实现以太坊的大部分 JSON-RPC API 方法
- 利用特殊代理来处理以太坊调用
- 适应 Solana 架构带来的差异和限制
与 Neon EVM 交互类似于与任何其他 EVM 交互。 开发人员可以使用熟悉的 RPC API 方法定向到 Neon 代理,从而促进无缝的开发体验。 主要特点包括:
- 与 Solidity 和 Vyper 智能合约以及 Metamask、Foundry 和 Remix 等标准以太坊开发工具兼容
- 逐字支持大多数以太坊操作码
- 接受类型 0/遗留以太坊交易请求。 目前不支持 EIP-1559 事务
当然,Neon EVM 要在 Solana 上正常运行,需要进行某些调整。 显著的差异包括:
- Neon EVM 支持 evm.code 上定义的所有预编译合约。 但是,包含以下调用的 Solidity 合约将不会被执行:bigModExp、bn256Add、bn256ScalarMult 和 bn256Pairing。 Neon EVM 需要实施 Solana 系统调用来支持未来的这些合约
- 虽然大多数操作码均受逐字支持,但 COINBASE、PREVRANDAO(FKA 困难)、GASLIMIT、BASEFEE 和 GAS 操作码不受支持。 这些被称为变体操作码,适用于 Neon EVM。
- Gas消耗和费用计算与以太坊不同。 由于 Solana 作为结算层的作用,它们通常会降低成本
- 由于gas计算不同,Solidity的
transfer()
和send()
方法在Neon EVM中不是重入安全的 - Solana 的账户模型影响智能合约存储,具有不同的存储功能以及可执行和非可执行账户的访问权限
- Neon EVM 使用版本化交易,将单笔交易使用的最大账户数量限制为 64 个
- Neon EVM 使用 Solana 的伯克利数据包过滤器 (BPF),堆内存限制为 256 KB。 这限制了合约调用的大小,并且需要优化策略来有效管理内存使用
- 基于时间的函数(例如
block.number
和block.timestamp
)的行为有所不同。 强烈警告开发人员在 Neon EVM 上进行开发时不要使用它们 - 虽然 Neon EVM 提供了熟悉的兼容环境,但 EVM 开发人员必须了解并适应关键差异,才能成功迁移并在 Solana 上进行构建。
6.3 连接到 Neon RPC
开发人员可以使用 Chainlist 轻松连接到 Neon RPC。 在这里,开发人员可以连接到 Neon EVM 主网或 Devnet。 在主网或 Devnet 模式上单击“连接钱包”,然后在弹出钱包时单击“批准”。
用户在将交易发送到 Neon EVM 之前应选择最佳运营商。 展开卡详细信息以查看每个网络的可用 RPC 端点:
如果您选择的运营商与钱包连接期间提供的默认运营商不同,则需要手动连接。 Neon EVM 文档提供了有关使用 Foundry、Hardhat、Remix 和 Truffle 连接到代理的综合指南。 我们将在部署部分介绍如何连接 Foundry。
连接后,开发人员可以使用 Neon Faucet 获取 NEON 或其他 ERC-20 测试代币。 开发人员还可以使用 request_neon
端点以编程方式请求代币:
curl -i -X POST \
-d '{"wallet": "Your wallet", "amount": 1}' \
'http://localhost:3333/request_neon'
此命令发送 POST 请求以将代币接收到指定的钱包地址。
6.4 交易生命周期和 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 价格。 如果符合条件,代理运营商会为 Neon 账户标记指定数量的无 Gas 交易
- 对于这些交易,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)
}
6.5 NeonPass
NeonPass 是用于在 Solana 和 Neon EVM 之间传输代币的工具。 它允许 Solana 的关联代币账户和 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 代币能够无缝过渡。
6.6 Neon EVM部署
开发人员可以使用 Hardhat、Foundry、Truffle 和 Remix 部署到 Neon EVM。 由于使用 Solang 最简单的方法是通过 Anchor,因此所有测试都是在 TypeScript 中完成的。 然而,我们最终可以通过 Foundry 测试所有 Solidity maxis 的 Solidity dApp 并将其部署到 Solana。
首先,确保你有一个连接到 Neon EVM Devnet 的 EVM 兼容钱包。 然后,克隆 Neon 的示例 Foundry 项目并进入项目目录:
git clone https://github.com/neonlabsorg/neon-tutorials
cd neon-tutorials/foundry
然后,安装 Foundryup(Foundry 工具链安装程序)并运行 Foundryup 以安装最新的(每晚)预编译的二进制文件(即,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 响应,其中的 URL 指向 Neon 的 Devnet 浏览器 BlockScout 上的合约地址。 你还可以将 .env 文件配置为使用 NeonScan 而不是 BlockScout,后者也支持 devnet。
6.7 Neon EMV优点和局限性
想要类似以太坊开发体验的开发者应该选择 Neon EVM。 它是一个与 EVM 兼容的环境,可以发送类似以太坊的交易并使用熟悉的工具。 NeonPass、对大多数 EVM 操作码的支持以及无 Gas 交易的提供等功能使向 Solana 的过渡变得更加容易。
然而,Neon EVM 并不完美。 它存在一定的局限性,例如改变智能合约逻辑以适应 Solana 的基础设施、在 Solana 的伯克利数据包过滤器 (BFP) 和帐户模型的约束下运行,以及对操作码和预编译合约有一定的限制。 了解并掌握这些细微差别对于成功从 EVM 兼容环境部署到 Solana 至关重要。
7、已经完成迁移的加密项目
要在非 EVM 链上部署大型以太坊协议,必须完成多项复杂的任务。 即,聘请非 Solidity 工程师从头开始重建他们的代码库,寻找值得信赖的审计合作伙伴,重新审计新的代码库,并调整治理合约以执行 DAO 决策。 这些壮举可能需要花费数百万美元并需要数月的专注工作。 这对大多数人来说是不可行的。 然而,这种事以前也发生过。
Helium 是一个 LoRaWAN 网络,旨在创建一个去中心化的无线基础设施来支持物联网 (IoT) 设备。 这是通过热点、小型低功耗设备(类似于微型蜂窝塔)来完成的,这些设备可以远距离连接到其他热点。
Helium 开发团队最初在自己的第 1 层 (L1) 区块链上提议迁移到 Solana。此举将使 Helium 能够实现更长的正常运行时间、更好的可组合性和更快的用户体验,同时保持高水平的安全性和安全性。 使用成本低。 社区以压倒性的票数支持了该提案,Helium 于 2023 年 4 月迁移到 Solana。Helium 的首席运营官 Scott Sigel 将这次迁移描述为一件无聊的事情 - 网络或 Helium 的基础设施没有出现任何问题。 这是工程师的梦想。 该团队还在其文档中的一系列指南中概述了整个迁移过程。 Helium 的迁移取得了巨大成功,使社区受益匪浅。
这种迁移不是一个孤立的事件。 全球首个去中心化 GPU 渲染平台 Render Network 于 2023 年 11 月成功将其核心基础设施从以太坊升级到 Solana。社区投票赞成 RNP-002 迁移到 Solana。 Render 的创始人 Jules Urbach 将这次迁移描述为一个分水岭时刻。 他表示:“随着我们继续构建可扩展和去中心化的元宇宙基础设施,Solana 令人难以置信的交易速度、低成本以及对网络规模架构的承诺使其非常适合渲染网络。”
Render 的迁移并没有对其用户产生过度的负面影响。 用户可以使用 Render 的升级助手将资金从以太坊转移到 Solana。 用户连接他们的以太坊钱包,指明他们希望迁移的 RNDR 数量,然后等待他们的代币被传送到指定的 Solana 钱包。
Maker 是一个典型的以太坊项目。 他们的目标是通过 Maker 平台释放去中心化金融的潜力,这是一个旨在增强经济赋权和平等进入全球金融市场的包容性平台。 该平台由用于管理 Maker 项目的 MakerDAO 和 DAI 的 Maker 协议组成,DAI 是“世界上第一个公正的货币和领先的去中心化稳定币。”Maker 的创始人 Rune Christensen 在推特上谈到了使用 Solana 代码库的分支来开发 Maker 应用链:
迁移本质上是复杂的。 尽管将项目的整个基础设施从以太坊迁移到 Solana 会产生金钱、声誉和时间成本,但项目仍在选择 Solana。 使用 Solang 和 Neon EVM 等工具进行迁移并不一定很复杂。 与 Helium 的迁移一样,它可能很无聊,并且对用户的资金几乎没有负面影响,就像Render Network一样。 Solana 是市场上性能最高的区块链,专为网络规模而构建。 这就是建设的地方,越来越多的项目正在意识到这一点。
8、结束语
Solidity 是智能合约开发的通用语言。 自诞生以来,EVM 一直是占主导地位的智能合约环境。 然而,它也有其弱点。 Web 规模的消费级应用程序需要高吞吐量、低延迟的网络来支持其操作。 以太坊的单线程、基于gas的脆弱环境无法支持像 Helium 这样的高吞吐量去中心化物理基础设施网络(DePIN)项目。
为了应对这些挑战,Solana 成为一个强大的替代方案。 充分利用 Solana 真正优势的最佳方法是在 Solana 的基础上进行实际构建。 随着最近的发展,Solang 和 Neon EVM 等工具允许感兴趣的 EVM 开发人员使用熟悉的工具和语言进行迁移。 本文提供了有关 Solana 架构的全面指南、它与以太坊的比较,并提出了 EVM 开发人员如何开始在 Solana 上进行开发。 Solana 是市场上性能最高的区块链。 它正在获得动力、份额和信誉良好的消费级应用程序。 当你今天可以享受快速、可扩展的区块链的好处时,为什么要等待以太坊扩展呢?
原文链接:How to Migrate From Ethereum to Solana: A Guide for Devs
DefiPlot翻译整理,转载请标明出处