随着本白皮书的发布，Neon EVM 引入了多项功能，以统一 Solana 与部署在 Neon 上的 EVM dApp 之间的用户体验。此版本遵循Solana 网络扩展方法中描述的愿景，并描述了 Neon EVM 基础设施的发展方式。 本文描述的概念将会在 SDK 中实现，具体如下： Solana 签名支持：使 Solana 用户能够使用他们现有的 Solana 钱包签署 Neon EVM 交易，从而无需 EVM 签名和钱包。 链上内存池：引入链上系统，无需依赖外部工具即可高效地安排和执行 Neon EVM 交易。 关联 Neon 账户：创建抽象 Solana 地址的 Neon 账户，并实现 Solana 用户和 Neon EVM 之间的无缝交互。 Neon 交易的受控树：实现交易的原子和并行执行方法，管理状态变化并聚合复杂交互的结果。 基于意图的执行：提供一种处理交易意图的机制，尽管 Solana 存在局限性，但仍能提高 EVM 合约和 Solana 程序之间的互操作性。 点击此处阅读完整白皮书 挑战：为 Solana 用户解锁以太坊 dApp Solana 和以太坊是领先的 L1 区块链之一，各自都拥有活跃的 dApp 生态系统和性能优势。然而，这两个环境尚未相互交互，主要是由于编程语言、开发工具和基础设施的差异。 虽然 Neon EVM 允许 EVM dApp 为类似以太坊的用户访问 Solana 代币，但之前的解决方案涉及多个钱包的管理。在 Neon EVM 上执行交易需要使用 secp256k1 椭圆曲线的 EVM 签名，因此必须使用 MetaMask 等 EVM 钱包。这一要求对习惯于 Solana 中使用的 ed25519 签名的 Solana 用户以及无法使用 Phantom、Backpack 和 Solflare 等 Solana 钱包的用户来说是一个重大障碍。 通过下面提出的解决方案，EVM 开发人员可以通过减少碎片化和简化入职培训来原生地利用 Solana 用户群。 探索“Solana-Native”功能 白皮书介绍了 Solana 的各种原生功能。需要关注的五个关键功能包括： Neon EVM 中的 Solana 签名支持 为了消除 Solana 用户管理额外 EVM 钱包的需要，Neon EVM 修改了其交易验证流程以接受 Solana 的 ed25519 签名。此修改包括： 签名验证适配：调整 Neon EVM 以识别和验证 ed25519 签名。 自定义交易类型：利用交易类型属性定义与 Solana 签名兼容的自定义交易类型，并遵守 EIP-2718 的类型化交易信封。 从技术上讲，Solana 用户的 EVM 地址 (ethAddress) 是使用 Keccak-256 哈希函数应用于 Solana 公钥 (solanaPublicKey) 得出的。地址派生公式为： 此方法采用哈希的最后 20 个字节来形成与 EVM 兼容的地址，从而确保唯一且抗冲突的映射。然后使用 Neon EVM 中的 Solana 原生加密库来验证 Solana 签名，从而确保安全验证。 好处 Solana 用户可以使用现有的 Solana 钱包（例如 Phantom/Backpack/Solflare）签署 Neon 交易。此集成减少了用户与 Solana 上的 EVM dApp 交互所需的步骤，并将资产保留在熟悉的 Solana 钱包环境中。 使用 Solana 钱包无需转移资金，只需将 SOL 用作 gas 代币和 Solana 网络 RPC。从用户的角度来看，资产交换是通过发送 SPL 代币并接收所选资产的 SPL 代币来执行的。 创建关联 Neon 账户，统一资产管理 Neon EVM 通过整合 Solana 和 Neon EVM 生态系统来解决流动性分散问题，使用户能够与兼容 EVM 的 dApp 进行交互，而无需进行不必要的资产转移。以前，用户依靠 NeonPass 将 SPL 代币从 Solana 钱包转移到 Neon EVM 账户，这使资产管理变得复杂，流动性也变得分散。现在，随着 ERC20forSPL 合约的引入，这一过程得到了简化，该合约无缝连接了两个生态系统。 拥有 Solana 钱包的用户通过关联代币账户 (ATA) 管理其 SPL 代币。Neon EVM 使用 Keccak-256 哈希将其 Solana 公钥映射到与 EVM 兼容的地址，从而创建一个关联的 Neon 账户，其中包含与 EVM 兼容的地址、Solana 公钥、交易计数器、链标识符和内部余额的字段。 当用户与 Neon EVM 上的 dApp 交互时，ERC20forSPL 合约首先检查其内部余额。如果余额不足，它会查询 Solana 上用户的 ATA 以检索必要的 SPL 代币。此过程消除了 NeonPass 转账的需要，并允许用户在 Neon EVM 中操作，同时将资产保留在 Solana 上。 好处 简化资产管理：用户可以控制其 Solana 钱包中的资产，而无需中间转账（例如通过 NeonPass）。 降低交易成本：通过利用链上余额查询并避免冗余传输，用户在与 EVM dApp 交互时可以降低成本。 使用链上内存池实现高效的交易管理 高效的交易调度和执行管理对于流畅的用户体验至关重要。Neon EVM 实现了链上内存池来存储和管理已调度的 Neon 交易。这允许 Solana 用户直接从他们的 Solana 钱包调度 Neon EVM 交易，Neon 代理会代表他们检测和执行这些交易。 TreeAccount 是一个专用帐户，具有预定的 Neon 交易、余额和执行状态。它包括以下内容： 付款人的 EVM 地址：与 Solana 用户关联的 EVM 地址。 最后一个槽位：修改帐户的最后一个 Solana 槽位。 链 ID：链命名空间的标识符。 Gas Limits：允许执行的最大气体量。 余额：为执行交易而保留的资金。 交易清单：预定的 Neon 交易及其执行详情。 交易调度协议涉及用户向 Neon EVM 程序提交 Neon EVM 交易，该程序会验证并将其添加到链上内存池中。然后，Neon 代理会监控内存池并相应地执行交易。 对于超出 Solana 大小限制的较大交易，只有交易哈希存储在链上，完整交易会发送到链下 Neon 代理。这种方法可以实现可扩展性并高效处理较大的交易，而不会影响网络性能。 好处 链上内存池增强了可扩展性，确保了交易完整性，并保持了状态更改的原子性。用户受益于高效的交易管理，开发人员可以利用此系统创建更复杂、响应更快的 dApp 用于复杂交互的受控事务树 为了克服 Solana 的即时状态应用和对交易还原的缺乏支持（这阻碍了 EVM 合约和 Solana 程序之间的复杂交互），Neon EVM 引入了 Neon 交易的受控树。该机制通过在树结构中组织交易来管理复杂的交互场景，其中每个节点代表一个 Neon 交易，父子关系定义执行依赖关系。 主要特点包括： 原子交易：每个 Neon 交易都是原子的，在完成时存储状态变化。 并行执行：没有依赖关系的事务可以并行执行，优化性能。 结果聚合：合约可以生成多个交易，并在后续交易中聚合结果。 好处 这项创新使开发人员能够构建更复杂、更集成的应用程序，通过实现受控状态变化的原子和并行执行来克服限制。 它可以增强交互的稳健性，并通过防止由于单个错误而导致的整个交易失败来改善用户体验。 基于意图的条件交易执行 Neon EVM 采用了意图驱动执行，为用户和开发者提供了根据特定条件或市场状态执行交易的灵活性。意图被定义为指定执行条件的简单 EVM 代码，遵守静态调用规则（不能修改状态）。Neon 代理会在执行交易之前评估这些意图。如果条件不满足，则跳过交易，用户无需支付任何费用。 此功能可以实现： 自动交易策略：仅当市场条件满足特定标准时才执行交易。 条件操作：当链上发生特定事件时执行诸如代币交换之类的操作。 好处 基于意图的执行可以更好地控制交易执行，提高资源效率，并减少用户不必要的成本。它增强了 dApp 对实时情况的灵活性和响应能力。 结论 征求反馈和协作：这份白皮书启动了 Neon EVM 的 Solana 原生演进，寻求不同视角的意见，并促进以太坊和 Solana 社区的开发者参与。 在这里，我们发布了第一稿，并邀请构建者社区积极贡献并开始评估您 dApp 上的实施情况。白皮书可在此处访问。 白皮书的第一个功能——即将发布的 Solana 签名钱包 SDK——已付诸实践。Solana 签名功能在 EVM 端已完成，并在代理端部分实现——目前正在测试和审核中。此外，敬请期待即将发布的用例和演示版本！ 加入我们，在 Solana 上构建 dApp 的未来。立即探索 Neon EVM！ 关于 Neon EVM Neon EVM 是同类产品中的第一个，是 Solana 上的网络扩展，旨在将以太坊虚拟机 (EVM) 兼容性无缝集成到 Solana 的高性能生态系统中。通过在 Solana 的基础层内本地运行，Neon EVM 为以太坊开发人员提供了一种快速、高吞吐量的途径，可以在 Solana 上部署他们的 EVM dApp，而无需用 Rust 重写他们的合约。 有关 Neon EVM 和未来更新的更多信息，请访问neonevm.org并通过Twitter或Discord与社区联系。 原文：https://neonevm.org/blog/Enabling-Solana-Users-to-Access-EVM-dApps-Running-on-Solana
零知识简洁非交互式知识论证 (Beam-SNARK) 是一种开创性的方法，它允许人们在不透露任何其他信息的情况下证明陈述的真实性。但这为什么有用呢？ 零知识证明有广泛的应用场景，例如： 1. 关于私人数据的证明陈述： 确认某人的银行余额超过某个限额，但不透露具体金额。 核实银行在过去一年内没有与特定实体进行过交易。 匹配 DNA 样本但不透露完整的基因图谱。 显示高于一定值的信用评分但不透露详细信息。 2. 匿名授权 证明用户有权访问网站的限制区域，而无需分享其身份（例如登录凭据）。 确认在授权地区的居住权但不透露具体位置。 在不透露身份的情况下验证有效地铁通票的所有权。 3. 匿名支付： 无需关联身份即可进行付款。 不披露收入而纳税。 4. 外包计算： 委托复杂的计算，同时确保结果正确，无需重复工作。 将区块链模型从通用计算转变为一方计算、其他方验证的模型。 零知识证明的底层数学和密码学简直就是奇迹。自 1985 年开创性的论文“交互式证明系统的知识复杂性”以来，该领域已经活跃了四十多年。非交互式证明的引入在区块链环境中尤为关键。 在任何零知识证明系统中，都有两个关键参与者： 证明者：想要让验证者相信某个陈述的真实性的人。 验证者：无需获取任何额外知识即可检查证明者主张的有效性的人。 该系统必须满足三个核心属性： 完整性：如果陈述是真实的，则证明者可以说服验证者。 健全性：作弊的证明者无法让验证者相信错误的陈述。 零知识：交互仅揭示陈述是否真实，而不揭示其他任何内容。 Beam-SNARK 将这些原理应用于通用计算，为实际应用提供了一个优雅的解决方案。 证明的媒介 为了理解 Beam-SNARK，让我们从一个简单的例子开始，而不深入研究零知识或交互性。 假设我们有一个 10 位的数组，并且我们想要向验证者（例如，程序）证明所有位都设置为 1。 假设我们有一个长度为 10 的位数组，并且我们想要向验证者（例如程序）证明所有这些位都设置为 1。 验证者每次只能检查一位。为了验证该声明，验证者可以按随机顺序检查位： 一次成功检查后，验证者对该声明的信心为 10%。 如果某个位为 0，则该断言立即被推翻。 为了获得更高的置信度（例如 50% 或 95%），验证者必须执行更多检查，与阵列的大小成正比。这种方法对于大型数据集来说不切实际。 相反，我们可以利用具有独特属性的多项式。多项式在图形上显示为曲线，由数学方程定义。 上图曲线对应多项式：f(x) = x³ — 6x² + 11x — 6。多项式的次数由其 x 的最大指数决定，在本例中为 3。 多项式有一个优点，即如果我们有两个次数最多为 d 的不相等多项式，它们最多只能在 d 个点处相交。例如，让我们稍微修改一下原始多项式 x³ — 6x² + 10x — 5，并将其可视化为绿色： 如此微小的变化会产生截然不同的结果。事实上，不可能找到两个不相等的多项式，它们共享一条连续的曲线块（单点块的情况除外）。 此属性源自查找公共点的方法。如果我们想找到两个多项式的交点，我们需要使它们相等。例如，要找到多项式与x轴的交点（即f ( x ) = 0），我们使x ³ — 6 x ² + 11 x — 6 = 0相等，并且该等式的解将是这些公共点：x = 1、x = 2 和x = 3，您也可以清楚地看到，在上图上蓝色曲线与x轴线相交的位置，情况确实如此。 同样，我们可以将原始多项式和修改后的多项式相等来找到它们的交点。 所得到的多项式是 1 阶的，显然有一个解x = 1。因此只有一个交点： 对于任意次数为d 的多项式，任何此类方程的结果始终是次数最多为d的另一个多项式，因为没有乘法可以产生更高的次数。例如：5 x ³ + 7 x ² — x + 2 = 3 x ³ — x ² + 2 x — 5，简化为 2 x ³ + 8 x ² — 3 x + 7 = 0。代数基本定理告诉我们，次数为d 的多项式最多可以有d 个解（更多内容见下文），因此最多有d 个共享点。 因此，我们可以得出结论，在任意点处对任何多项式的求值类似于对其唯一身份的表示。让我们在x = 10 处求值示例多项式。 事实上，在所有要评估的x选择中，只有最多 3 个选择在这些多项式中具有相同的评估，而所有其他选择都会有所不同。 这就是为什么如果证明者声称知道某个多项式（无论其度数有多大），而验证者也知道的话，他们可以遵循一个简单的协议： 验证者为x选择一个随机值，并在本地评估多项式 验证者将x提供给证明者，并要求其计算相关多项式 证明者在x 处评估多项式，并将结果提供给验证者 验证者检查本地结果是否等于证明者的结果，如果是，则该语句具有很高的置信度 例如，如果我们考虑x的整数范围从 1 到 1⁰⁷⁷，则评估不同的点数为 1⁰⁷⁷ — d 。因此， x意外“击中”任何d个共享点的概率等于（这被认为是可以忽略不计的）： 注意：与低效的位校验协议相比，新协议仅需要一轮，并且对该声明具有压倒性的信心（假设 d 足够小于范围的上限，则几乎为 100%）。 这就是为什么多项式是 Beam -SNARK的核心，尽管也可能存在其他证明媒介。 原文：https://medium.com/@Moonchain_com/why-and-how-beam-snark-works-94f703cf1413
1. 引言 区块链互操作性是指链A与链B交互数据的能力。近年来区块链生态快速扩张，出现了大量具有不同属性的区块链网络，互操作性是区块链设计时的一个重要考虑指标。不具有互操作性，网络具有孤立于更大生态的风险，为此，激励了项目方研究和开发互操作性解决方案。每种互操作性解决方案具有不同的权衡和底层技术。本文由Polygon团队提供的解决方案，为Polygon zkEVM L2网络提供了原生的互操作性。 bridge为基础设施元素，允许L1与L2之间进行资产迁移和通信。从用户角度来看，bridge可在不改变资产数量或资产功能的情况下，将资产由网络A转移至网络B；bridge也可以在网络间发送data payload（即跨链消息传递）。 Polygon zkEVM这样的L2 rollups，其L2 State Transitions和交易的数据可用性 均由L1合约来保证，因此，若正确设计L2架构，可仅依赖于合约逻辑来同步bridge的两端，而不需要可信的链下relayer来跨网络同步bridge两端。需注意的是，本bridge方案必须在L2层包含相应的设计。 如图1所示，bridging interface为部署在L1和L2网络上的bridge合约，用户可用于： bridge assets（1）：在“origin”网络lock某资产，在“destination”网络的bridge合约中claim时，会mint出该资产的representative token。 bridge assets的逆操作（2）：burn某资产的representative token，然后在“origin”网络中unlock其原始资产。 跨链通讯channel（3）：在L1和L2之间相互发送data payload。 2. Exit Merkle trees bridge中包含了名为Global Exit Merkle Tree（GEMT）的默克尔树。在GEMT树中，每个叶子节点表示了特定网络的Exit Merkle Tree（EMT）.GEMT树中仅包含了2个叶子节点，一个对应为L1 EMT root，另一个对应为L2 EMT root。GEMT树的结构如图2所示： GEMT为固定只有2个叶子节点的常规默克尔树，而EMT为append only sparse Merkle Trees（SMT）且具有固定的depth（Polygon zkEVM中设计depth为32）。SMT为大量使用的默克尔树，可在链上高效使用——详情见附录A。 特定网络EMT的每个叶子节点，表示了从该网络往外 bridge某资产（或某资产的representative token）或 发送某消息 的意图。EMT每个叶子节点为如下参数的abi encoded packed structure的keccak256哈希值 uint8 leafType：0表示asset，1表示message。 int32 originNetwork：为原始资产所属的Origin Network ID。 address originAddress：若leafType=0，则为Origin network token address，其中“0x000…0000”保留为ether token address；若leafType=1，则为message的msg.sender。 uint32 destinationNetwork：为bridging的destination网络ID。 address destinationAddress：为目标网络中接收bridged asset的收款方地址。 uint256 amount：bridge的token或ether数量。 bytes32 metadataHash：为metadata哈希值。metadata将包含所转移资产信息或所转移message payload信息。 一旦某leaf添加到EMT中，将计算新的EMT root，以及新的GEMT root。GEMT root将在网络间同步，使得可在对方网络中证明leaf inclusion，并完成bridge操作。 3. 合约架构 大多数bridge架构都使用在双方网络上的智能合约来实现。但是，为同步二者的GEMT，有一部分bridge逻辑必须与L2 State管理架构进行集成。因此，为理解本brIdge方案，还需要考虑L2 State管理中涉及到的链下角色——如Sequencer、Aggregator以及PolygonZkEVM.sol合约。 此外，bridge架构中还包含以下元素： PolygonZkEVMBridge.sol合约：为bridging interface，允许用户与该bridge进行交互，并进行bridgeAsset/bridgeMessage/claimAsset/claimMessage等操作。每个网络都有一个PolygonZkEVMBridge.sol合约，并管理相应的EMT。 PolygonZkEVMGlobalExitRoot.sol合约：管理GEMT树，充当GEMT树的历史存储库，具体为： 2.1 存储GEMT树， 2.2 每次由PolygonZkEVM.sol合约更新新的EMT（实际为更新L2 EMT）时，会计算新的GEMT root， 2.3 每次由L1的PolygonZkEVMBridge.sol合约更新新的EMT（实际为更新L1 EMT）时，会计算新的GEMT root。 PolygonZkEVMGlobalExitRootL2.sol合约：为一种特殊的合约，用于跨网络同步GEMT和L2 EMT roots： 3.1 该合约中有storage slots来存储GEMT roots以及L2 EMT root。 3.2 该合约的特殊性在于：底层的zero-knowledge proving/verification系统可直接访问其storage slots，以确保由L1同步到L2（L1->L2）的GEMT的有效性，以及有L2同步到L1（L2->L1）的L2 EMT的有效性。 3.1 bridging数据流 图3展示了详细的bridge架构，以及为实现bridge操作的finality，双方网络是如何交互的。具体分为2种bridge操作： 由L1->L2的bridge操作 由L2->L1的bridge操作 3.1.1 由L1->L2的bridge操作 由L1->L2的bridge操作的基本流程为： （1）用户调用L1 PolygonZkEVMBridge.sol合约的bridgeAsset/bridgeMessage函数。取决于具体的asset类型，该函数的内部实现将有所不同。若该bridge请求有效，则合约将根据该请求的属性来填充新的L1 EMT leaf，将该叶子节点填充到EMT树中，并计算新的L1 EMT root。 （2）在bridgeAsset/bridgeMessage同一L1交易中，L1 PolygonZkEVMBridge.sol合约会调用L1 PolygonZkEVMGlobalExitRoot.sol合约来更新新的L1 EMT root，并计算新的GEMT root。 （3）L2 Sequencer将从 L1 PolygonZkEVMGlobalExitRoot.sol合约中获取新的GEMT root。 （4）在transaction batch execution之初，L2 Sequencer会将新的GEMT root存入 L2 PolygonZkEVMGlobalExitRootL2.sol合约的特殊storage slots中，允许L2用户访问。 （5）&（6）为完成bridging流程，用户必须调用L2 PolygonZkEVMBridge.sol合约的claimAsset/claimMessage函数，并提供之前已添加到EMT的节点的Merkle inclusion proof。L2 PolygonZkEVMBridge.sol合约 将从 L2 PolygonZkEVMGlobalExitRootL2.sol合约中获取GEMT root，并验证该inclusion proof的有效性。若该inclusion proof有效，则取决于所bridge的资产类型，L2 PolygonZkEVMBridge.sol合约将完成相应的bridging流程。若该inclusion proof无效，则该交易将被revert。 3.1.2 由L2->L1的bridge操作 由L2->L1的bridge操作的基本流程为： （1）用户调用L2 PolygonZkEVMBridge.sol合约的bridgeAsset/bridgeMessage函数。取决于具体的asset类型，该函数的内部实现将有所不同。若该bridge请求有效，则合约将根据该请求的属性来填充新的L2 EMT leaf，将该叶子节点填充到EMT树中，并计算新的L2 EMT root。 （2）在bridgeAsset/bridgeMessage同一L2交易中，L2 PolygonZkEVMBridge.sol合约会调用L2 PolygonZkEVMGlobalExitRootL2.sol合约来更新新的L2 EMT root ，并计算新的GEMT root 。 （3）L2 Aggregator将为 包含了 该L2 bridge交易在内 的sequence of batches的execution生成相应的计算完整性Zero-Knowledge proof。通过该execution，可从L2 State中获得新的L2 EMT. （4）L2 Aggregator会将新的L2 EMT以及相应的ZKP证明提交到 L1 PolygonZkEVM.sol合约。 （5）L1 PolygonZkEVM.sol合约会验证该ZKP证明的有效性，若有效，L1 PolygonZkEVM.sol合约 会调用 L1 PolygonZkEVMGlobalExitRoot.sol来更新新的L2 EMT root，并计算新的GEMT root。 （6）&（7）为完成bridging流程，用户必须调用L1 PolygonZkEVMBridge.sol合约的claimAsset/claimMessage函数，并提供之前已添加到EMT的节点的Merkle inclusion proof。L1 PolygonZkEVMBridge.sol合约 将从 L1 PolygonZkEVMGlobalExitRootL2.sol合约中获取GEMT root，并验证该inclusion proof的有效性。若该inclusion proof有效，则取决于所bridge的资产类型，L1 PolygonZkEVMBridge.sol合约将完成相应的bridging流程。若该inclusion proof无效，则该交易将被revert。 3.2 L1/L2 PolygonZkEVMBridge.sol合约 PolygonZkEVMBridge.sol合约为特定网络用户的bridging interface，因此在每个网络都有一个PolygonZkEVMBridge.sol合约。 PolygonZkEVMBridge.sol合约具有： 所需的storage slots，来维护每个网络的EMT 所需的函数供用户交互 PolygonZkEVMBridge.sol合约目前有2种bridge函数： 1）bridgeAsset函数 2）bridgeMessage函数 默认L2网络的账号是没有ether来支付交易手续费的，当claiming源自L1的Asset或Message时，调用L2 bridge合约claiming函数的 L2 claiming交易可以不支付gas费，由polygon zkEVM协议来资助。 PolygonZkEVMBridge.sol合约目前有2种claiming函数 1）claimAsset函数 2）claimMessage函数 3.2.1 bridgeAsset函数 bridgeAsset函数用于向另一网络转移资产： function bridgeAsset( uint32 destinationNetwork, address destinationAddress, uint256 amount, address token, bool forceUpdateGlobalExitRoot, bytes calldata permitData ) bridgeAsset函数参数有： token：为原始网络的ERC20 token地址，若为“0x0000…0000”，则意味着用户想要转移ether。 destinationNetwork：为目标网络的网络ID，必须不同于 调用本函数的所属网络ID，否则交易将被rever。 destinationAddress：目标网络接收所bridge token的收款方地址。 amount：bridge的token数量。 permitData：为具有EIP-2612 Permit扩展的ERC-20 token的 已签名permit data，用于改变某账号的ERC-20 allowance，并允许bridge合约将所bridged token转移给自身。 所bridge的资产类型有3种，对应的bridgeAsset有3条可能的执行流： （1）所bridged asset为ether。 对应bridgeAsset函数的token参数为“0x0000…0000”。 交易的msg.value必须匹配amount参数。 为完成bridge操作，相应的ether数量将lock在bridge合约中。注意L1和L2的ether将以相同的方式来处理，按1：1兑换。且ether为L1和L2网络的原生token，用于支付gas费。 由于ehter源自L1，相应leaf中的originNetwork参数将设置为L1网络ID。 （2）所bridged asset为源自另一网络ERC20 token的representative ERC-20 token。 Representative ERC-20 tokens由PolygonZkEVMBridge.sol管理——负责mint和burn。 bridge合约中有名为wrappedTokenToTokenInfo的map，为记录部署在本网络的representative ERC-20 token contracts list。 对于所部署的每个representative token contract，wrappedTokenToTokenInfo map会以该representative token合约地址为key，相应的value为TokenInformation结构体： // Wrapped Token information struct struct TokenInformation { uint32 originNetwork; address originTokenAddress; } 对应bridgeAsset函数的token参数为wrappedTokenToTokenInfo map中的某key，则意味着所bridged token为源自另一网络ERC-20 token的representative ERC-20 token。 为完成bridge操作，对应amount参数相应数量的token将被burn，bridge合约无需用户许可，有权burn相应的token。leaf中的originAddress和originNetwork参数将从wrappedTokenToTokenInfo map相应value中获取。 （3）所bridged asset为源自本网络的ERC-20 token。 为完成bridge操作，对应amount参数相应数量的token将lock在bridge合约中。 为让bridge合约能将相应数量的token转移给自身，bridge合约必须具有不少于用户所bridge token数量的allowance。 若该token合约支持EIP-2612 permit扩展，相应的allowance可在同一笔交易中实现——对应有signed permitData参数。 leaf中的originAddress和originNetwork参数分别为当前网络ID和ERC-20 token合约地址。 leaf中的meatadataHash参数计算方式为metadataHash = keccak256( abi.encode( IERC20MetadataUpgradeable(token).name(), IERC20MetadataUpgradeable(token).symbol(), IERC20MetadataUpgradeable(token).decimals() ) ) 对应在bridgeAsset函数中的metadata具体表示为： // Encode metadata metadata = abi.encode( _safeName(token), _safeSymbol(token), _safeDecimals(token) ); 最后的仔细步骤则是相同的，与资产类型无关。剩余的leaf参数有： leafType参数：设置为0，表示资产。 destinationNetwork和destinationAddress参数：根据调用bridgeAsset函数的相应参数设置。 bridgeAsset流程中： 会释放包含了new leaf所有信息的BridgeEvent事件 该new leaf将添加到EMT中 为更新该new EMT root，会调用GEMT合约。 3.2.2 bridgeMessage函数 bridgeMessage函数用于向另一网络转移消息： function bridgeMessage( uint32 destinationNetwork, address destinationAddress, bool forceUpdateGlobalExitRoot, bytes calldata metadata ) bridgeMessage函数的参数有： destinationNetwork：为目标网络的网络ID，必须不同于调用本函数所在的网络ID，否则交易将被revert。 destinationAddress：为目标网络接收bridged message的接收地址。 forceUpdateGlobalExitRoot：标记是否更新新的global exit root。 metadata：为Message payload。 bridgeMessage函数将： 直接释放BridgeEvent事件 向EMT中添加new leaf 与bridgeAsset函数类似，调用GEMT合约更新new EMT root bridgeMessage函数与bridgeAsset函数的主要差异在于： 所创建的leaf中的leafType参数为1 leaf中的orginAddress和metadataHash参数分别为msg.sender值和message payload的哈希值。 用户在bridge message的同时，也可bridge ether。具体bridged ether的数量添加在bridgeMeesage函数调用交易的msg.value中，在目标网络上接收消息的同时，可destinationAddress.call{value: amount}获得相应的ether。 3.2.3 claimAsset函数 claimAsset函数用于claim源自另一网络bridge来的资产： function claimAsset( bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProof, uint32 index, bytes32 mainnetExitRoot, bytes32 rollupExitRoot, uint32 originNetwork, address originTokenAddress, uint32 destinationNetwork, address destinationAddress, uint256 amount, bytes calldata metadata ) claimAsset函数参数有： smtProof：为Merkle proof，即为验证该leaf所需的sibling nodes array。 index：为leaf index。 mainnetExitRoot：为包含该leaf的L1 EMT root。 rollupExitRoot：为包含该leaf的L2 EMT root。 originNetwork：为所bridge资产所属的原始网络ID。 originTokenAddress：为原始网络的ERC-20 token地址，若为0x0000…0000，则表示claIm的为ether资产。 destinationNetwork：为目标网络ID，即为调用claimAsset函数所属的网络ID。 destinationAddress：为接收bridged token的收款方地址。 amount：所claim的token数量。 metadata： 若claim的token为ether 或 为调用claimAsset函数所属网络的ERC-20 token，则metatdata值为0（metadata参数设置为0x）。 若bridgeAsset时，bridge的为orginNetwork的ERC-20 token时，提供metadata，供目标网络上claimAsset时，wrap相应的representative token。 // 以上数据参数，通过bridge service服务api提供 // Encode metadata，bridgeAsset本网络ERC-20 token metadata = abi.encode( _safeName(token), _safeSymbol(token), _safeDecimals(token) ); // claimAsset时，基于metadata构建相应的wrap token // Get ERC20 metadata ( string memory name, string memory symbol, uint8 decimals ) = abi.decode(metadata, (string, string, uint8)); // Create a new wrapped erc20 using create2 TokenWrapped newWrappedToken = (new TokenWrapped){ salt: tokenInfoHash }(name, symbol, decimals); claimAsset函数会根据用户提供的参数来验证相应leaf的有效性。 为避免重放攻击，需确保指定leaf仅能成功验证一次。PolygonZkEVMBridge.sol合约具有claimedBitMap map来存储每个已成功验证leaf index的nullifier bit，具体如图5所示： 为优化storage slots usage，claimedBitMap map中的每个条目会hold 256 nullifier bits for 256 already verified leaves。 认定 某指定leaf的merkle proof是有效的，需满足以下条件： claimedBitMap map中该leaf index的nullifier bit必须未设置。 该leaf中的destinationNetwork参数必须 与 调用claimAsset函数所属网络ID 一致。 对mainnetExitRoot参数和rollupExitRoot参数哈希获得的GEMT root结果，必须已存在于PolygonZkEVMGlobalExitRoot.sol 合约中。 该merkle proof必须有效，即可生成期待的GEMT root。 若该leaf验证成功，与该leaf index相应的claimedBitMap map中的bit将被nullified，后续的流程如图6所示： 与bridgeAsset一致，claimAsset根据资产类型不同，有3种可能的执行流程： （1）所claimed asset为ether： originTokenAddress参数为“0x0000…0000” amount参数对应数量的ether将发送到destinationAddress参数对应的地址账号中。 由于ether无法minted on demand，L2 PolygonZkEVMBridge.sol 合约中具有preminted 100000000000 ether（1000亿个ether）作为ether bridging liquidity。由于假设所有的L2 ether都源自L1，因此，L1 PolygonZkEVMBridge.sol 合约中无需预置ether balance，L2中的每个ether wei都有a backing ether wei blocked in L1 contract。注意，L2 PolygonZkEVMBridge.sol 合约中 preminted liquidity并不会ether有任何通胀影响。【不过目前genesis中，L2 PolygonZkEVMBridge.sol 合约中preminted ether数为20亿个。】 （2）所claimed asset为源自本网络的ERC-20 token originNetwork参数对应 调用claimAsset函数所属网络ID。 意味着所claimed asset源自本网络，且之前已在PolygonZkEVMBridge.sol合约中锁定。 amount对应发送到destinationAddress参数对应地址账号中的相应ERC-20 token数量。 （3）所claimed asset为源自另一网络ERC-20 token的representative ERC-20 token： PolygonZkEVMBridge.sol合约中有tokenInfoToWrappedToken map，该map中存储了部署在本网络的representative ERC-20 token合约地址。部署的representative ERC-20 token合约采用create2 opcode，相应的salt为tokenInfoToWrappedToken map的key值。该salt值根据originNetwork和originTokenAddress参数计算而来：// The tokens is not from this network // Create a wrapper for the token if not exist yet bytes32 tokenInfoHash = keccak256( abi.encodePacked(originNetwork, originTokenAddress) ); bridge合约会检查所claimed asset的representative ERC-20 token合约是否已存在tokenInfoToWrappedToken map中： 若存在，则说明已部署该representative ERC-20 token合约，可mint对应amount参数数量的token到destinationAddress参数对应的账号中。 若不存在，则需使用create2 opcode以及之前计算的salt 来部署新的representative ERC-20 token合约。使用create2 opcode和指定的salt，可确定性的绑定 representative token的合约地址 与 origin network的origin token合约地址。部署成功后，可mint对应amount参数数量的token到destinationAddress参数对应的账号中，同时： 会释放NewWrappedToken事件。 会在tokenInfoToWrappedToken map和wrappedTokenToTokenInfo map中添加新representative ERC-20 token合约的新条目。 最终，无论是claim的是哪种资产，都会释放ClaimEvent事件。 3.2.4 claimMessage函数 claimMessage函数用于claim源自其它网络的message： function claimMessage( bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProof, uint32 index, bytes32 mainnetExitRoot, bytes32 rollupExitRoot, uint32 originNetwork, address originAddress, uint32 destinationNetwork, address destinationAddress, uint256 amount, bytes calldata metadata ) 与claimAsset函数类似，claimMessage会验证用户给定的leaf，由于二者的leaf格式是一样的，因此这2个函数的参数也是一样的。 与claimAsset函数一样，若leaf验证通过，通过设置相应leaf index对应在claimdBitMap map中的bit，来将相应leaf index nullified。 然后哦，底层会调用destinationAddress参数： // Execute message // Transfer ether /* solhint-disable avoid-low-level-calls */ (bool success, ) = destinationAddress.call{value: amount}( abi.encodeCall( IBridgeMessageReceiver.onMessageReceived, (originAddress, originNetwork, metadata) ) ); 可看出，为调用onMessageReceived函数设置了originAddress, originNetwork, metadata参数，且若message中包含了ether，相应的call value设置为amount参数。其中metadata参数为message payload。 注意，messaging service可用于将ether转移给Externally owned accounts（EOAs），但是，EOAs无法解析消息，因此message payload对其不可用。 最终，若message发送成功，则会释放ClaimEvent事件。 3.3 L2 PolygonZkEVMGlobalExitRoot.sol合约 PolygonZkEVMGlobalExitRoot.sol合约为L1合约，可计算和存储每个new GEMT root。为确保所添加的每个leaf在未来都可被验证，需要存储每个GEMT root。名为globalExitRootMap的map用来存储所有已计算的GEMT roots。 L1 PolygonZkEVMBridge.sol合约在验证leaf时，会从globalExitRootMap map中获取GEMT roots。 L1 PolygonZkEVMGlobalExitRoot.sol合约中updateExitRoot函数用于更新EMT roots并计算新的GEMT root，updateExitRoot函数会 由L1 PolygonZkEVM.sol合约 或 由L1 PolygonZkEVMBridge.sol合约 调用： 若由L1 PolygonZkEVM.sol合约调用updateExitRoot函数，则将更新L2 EMT root。 若由L1 PolygonZkEVMBridge.sol合约调用updateExitRoot函数，则将更新L1 EMT root。 function updateExitRoot(bytes32 newRoot) external 每个L2 state transition均由L1 PolygonZkEVM.sol合约固化（通过验证Aggregator提交的ZKP证明），通过调用L1 PolygonZkEVMGlobalExitRoot.sol合约的updateExitRoot函数，将更新new L2 EMT root。 当有new leaf添加到L1 PolygonZkEVMBridge.sol合约的L1 EMT中时，通过调用L1 PolygonZkEVMGlobalExitRoot.sol合约的updateExitRoot函数，将更新new L1 EMT root。 最终，都会释放UpdateGlobalExitRoot事件。 3.4 L2 PolygonZkEVMGlobalExitRootL2.sol合约 L2 PolygonZkEVMGlobalExitRootL2.sol 合约 为部署在L2上的“特殊”合约。 【v1.1版本的bridge文档与当前的实现有出入】 Aggregator zkEVM node软件在执行完transactions batches时，可直接访问L2 PolygonZkEVMGlobalExitRootL2.sol 合约的lastRollupExitRoot storage slots。lastRollupExitRoot storage slots中存储的为L2 EMT root。 随后，该L2 EMT root会和L2 State transition proof一起，提交到L1 PolygonZkEVM.sol合约中。若该proof验证通过，则会更新L1 PolygonZkEVMGlobalExitRoot.sol合约中的new L2 EMT root，并计算new GEMT，从而使得L1 PolygonZkEVMBridge.sol合约可获得有效的GEMT roots来验证包含在aggregated batches中的user claim transactions。 附录A Gas efficient append only sparse Merkle tree sparse Merkle tree基础只是可参看： Sparse Merkle Tree sparse Merkle tree为具有intractable size的Merkle tree，可以以有效的方式处理。假设它几乎是空的，事实上，最初它是空的。当它中的所有叶子都具有相同的零（空）值时，它被认为是空的，由于这种假设，可以通过log ⁡ 2 ( n ) \log_2(n)log 2 ​ (n)次哈希运算来计算root，其中n nn是树上的叶子数量。请注意，而对于non-sparse tree，需要2 n − 1 2n−12n−1次哈希运算才能计算root。在计算空树根的过程中，在每个level中，所有节点都将取相同的值，因此不需要计算每个level中的所有子树。当树的特定level的节点为空时，该节点的值都被命名为Zero Hash，并将表示具有x xx个零叶子的subtree。 以3层（8个叶子）的empty sparse Merkle tree为例，相应的Zero H按时list为： 参考资料 [1] polygon zkEVM Technical Document Bridge v.1.1 原文链接：https://blog.csdn.net/mutourend/article/details/129986151
Awesome zkEVM zkEVM is a Fully EVM equivalent and zk friendly virtual machine. Contents Awesome zkEVM Contents Article - Video Rollup zkEVM zk-hardware Zero-Knowledge-Proofs zk-SNARK Plonk Halo2 Resources - libraries, tools Early Rollup Scroll and Appliedzkp(PSE) Polygon Hermez Polygon Zero zkSync StarkWare Zero-Knowledge-Proofs Halo2 zkp Acceleration Different Solutions Native zkEVM Compiler-Based zkEVM Transpiler-Based zkEVM Other Contributing Article - Video Rollup An Incomplete Guide to Rollups - vitalik, Introducing Rollup. A rollup-centric ethereum roadmap - vitalik, rollup-centric ethereum roadmap. How Zk-Rollups Work - How barry whiteHat's rollup works. zkEVM zkEVM - Scroll zkEVM. zkEVM Architecture - Scroll & EF zkEVM Architecture. ZKVerse: Deep Dive Into Polygon Hermez 2.0 - Deep into zkEVM of polygon hermez. zkEVM circuit arithmetization - zkEVM circuits and EVM circuit, yezhang. The different types of ZK-EVMs zk-hardware Hardware Acceleration for Zero Knowledge Proofs - Hardware for ZKPs. Zero-Knowledge-Proofs zk-SNARK Introduction to zk-SNARKs with Examples - Get started to learn zk-SNARK by example. Why and How zk-SNARK Works - In-depth SNARK mechanism and understanding. Plonk Understanding PLONK ZK Study Club - Plonk with Zac Williamson Multiset checks in PLONK and Plookup Halo2 Halo and more: exploring incremental verification and SNARKs without pairings Resources - libraries, tools Early Rollup idn3 rollup - iden3 rollup implement. barryWhiteHat rollup - First rollup implemented by barryWhiteHat. Scroll and Appliedzkp(PSE) zkEVM-spec zkEVM-circuit zkEVM-document Scroll's architecture Polygon Hermez Polygon Hermez zkEVM Hermez document Polygon Zero Plonky2 zkSync zksync v1 - zksync v1 source code. compiler-solidity - zkSync v2 compiler. zksync v2 portal - zksync v2 faucet, Goerli network. StarkWare Awesome-starknet - A curated list of starkNet resources. Warp:transpile solidity to cairo Zero-Knowledge-Proofs Awesome-zkps - A curated list of zkps resources. Halo2 Halo2 repo Halo2 document Halo2 for PSE and Scroll zkp Acceleration supranational supranational - sppark Different Solutions Native zkEVM PSE (formerly appliedzkp) Scroll Polygon Hermez Polygon Zero Compiler-Based zkEVM zkSync v2 Transpiler-Based zkEVM StarkNet Miden Other Consensys zkEVM Consensys zkEVM 2.0 Contributing Contributions are very welcome! Please have a look at contributing.md for guidelines. 英文原文：https://github.com/LuozhuZhang/awesome-zkevm
1. 引言 源代码：https://github.com/0xPolygonHermez/zkevm-node （Go语言） Polygon zkEVM节点提供的主要服务模块有： 1）JSON-RPC服务 2）Sequencer服务 3）Aggregator服务 4）Synchronizer服务 5）Broadcast服务 2. JSON-RPC服务 以太坊JSON-RPC接口 与 Polygon zkEVM中的JSON-RPC接口 对比情况为： 序号 RPC接口名 以太坊 Hermez 2.0 备注 1 GetBlockByHash √ √ 2 GetBlockByNumber √ √ 3 GetBlockTransactionCountByHash √ √ 4 GetBlockTransactionCountByNumber √ √ 5 getUncleCountByBlockHash √ √ 6 getUncleCountByBlockNumber √ √ 7 ChainId √ √ 8 Syncing √ √ 9 Coinbase √ X 10 Accounts √ X 11 BlockNumber √ √ 12 Call √ √ 13 EstimateGas √ √ 14 CreateAccessList √ X EIP-2930：新交易类型，需以更贵的方式访问清单（地址或storage keys）外的内容。 15 GasPrice √ √ 16 MaxPriorityFeePerGas √ X 17 FeeHistory √ X 18 NewFilter √ √ 19 NewBlockFilter √ √ 20 NewPendingTransactionFilter √ √ 21 UninstallFilter √ √ 22 GetFilterChanges √ √ 23 GetFilterLogs √ √ 24 GetLogs √ √ 25 Mining √ X 26 Hashrate √ X 27 GetWork √ X 28 SubmitWork √ X 29 SubmitHashrate √ X 30 Sign √ X 31 SignTransaction √ X 32 GetBalance √ √ 33 GetStorageAt √ √ 34 GetTransactionCount √ √ 35 GetCode √ √ 36 GetProof √ X 37 SendTransaction √ X 38 SendRawTransaction √ √ 39 GetTransactionByHash √ √ 40 GetTransactionByBlockHashAndIndex √ √ 41 GetTransactionByBlockNumberAndIndex √ √ 42 GetTransactionReceipt √ √ 43 GetCompilers √ √ 44 GetUncleByBlockHashAndIndex √ √ 45 GetUncleByBlockNumberAndIndex √ √ 46 ProtocolVersion √ √ Hermez 2.0（zkEVM）除实现了以上与以太坊兼容的RPC接口之外，还额外实现了一些与state交互、与pool交互的接口： // jsonRPCTxPool contains the methods required to interact with the tx pool. type jsonRPCTxPool interface { AddTx(ctx context.Context, tx types.Transaction) error GetPendingTxs(ctx context.Context, isClaims bool, limit uint64) ([]pool.Transaction, error) GetGasPrice(ctx context.Context) (uint64, error) GetPendingTxHashesSince(ctx context.Context, since time.Time) ([]common.Hash, error) } // gasPriceEstimator contains the methods required to interact with gas price estimator type gasPriceEstimator interface { GetAvgGasPrice(ctx context.Context) (*big.Int, error) } // stateInterface gathers the methods required to interact with the state. type stateInterface interface { BeginStateTransaction(ctx context.Context) (pgx.Tx, error) GetLastConsolidatedL2BlockNumber(ctx context.Context, dbTx pgx.Tx) (uint64, error) GetTransactionByHash(ctx context.Context, transactionHash common.Hash, dbTx pgx.Tx) (*types.Transaction, error) GetTransactionReceipt(ctx context.Context, transactionHash common.Hash, dbTx pgx.Tx) (*types.Receipt, error) GetLastL2BlockNumber(ctx context.Context, dbTx pgx.Tx) (uint64, error) GetLastL2Block(ctx context.Context, dbTx pgx.Tx) (*types.Block, error) GetLastL2BlockHeader(ctx context.Context, dbTx pgx.Tx) (*types.Header, error) EstimateGas(transaction *types.Transaction, senderAddress common.Address) (uint64, error) GetBalance(ctx context.Context, address common.Address, blockNumber uint64, dbTx pgx.Tx) (*big.Int, error) GetL2BlockByHash(ctx context.Context, hash common.Hash, dbTx pgx.Tx) (*types.Block, error) GetL2BlockByNumber(ctx context.Context, blockNumber uint64, dbTx pgx.Tx) (*types.Block, error) GetCode(ctx context.Context, address common.Address, blockNumber uint64, dbTx pgx.Tx) ([]byte, error) GetStorageAt(ctx context.Context, address common.Address, position *big.Int, blockNumber uint64, dbTx pgx.Tx) (*big.Int, error) GetSyncingInfo(ctx context.Context, dbTx pgx.Tx) (state.SyncingInfo, error) GetTransactionByL2BlockHashAndIndex(ctx context.Context, blockHash common.Hash, index uint64, dbTx pgx.Tx) (*types.Transaction, error) GetTransactionByL2BlockNumberAndIndex(ctx context.Context, blockNumber uint64, index uint64, dbTx pgx.Tx) (*types.Transaction, error) GetNonce(ctx context.Context, address common.Address, blockNumber uint64, dbTx pgx.Tx) (uint64, error) GetL2BlockHeaderByNumber(ctx context.Context, blockNumber uint64, dbTx pgx.Tx) (*types.Header, error) GetL2BlockTransactionCountByHash(ctx context.Context, hash common.Hash, dbTx pgx.Tx) (uint64, error) GetL2BlockTransactionCountByNumber(ctx context.Context, blockNumber uint64, dbTx pgx.Tx) (uint64, error) GetLogs(ctx context.Context, fromBlock uint64, toBlock uint64, addresses []common.Address, topics [][]common.Hash, blockHash *common.Hash, since *time.Time, dbTx pgx.Tx) ([]*types.Log, error) GetL2BlockHashesSince(ctx context.Context, since time.Time, dbTx pgx.Tx) ([]common.Hash, error) DebugTransaction(ctx context.Context, transactionHash common.Hash, tracer string) (*runtime.ExecutionResult, error) ProcessUnsignedTransaction(ctx context.Context, tx *types.Transaction, senderAddress common.Address, blockNumber uint64, dbTx pgx.Tx) *runtime.ExecutionResult IsL2BlockConsolidated(ctx context.Context, blockNumber int, dbTx pgx.Tx) (bool, error) IsL2BlockVirtualized(ctx context.Context, blockNumber int, dbTx pgx.Tx) (bool, error) } type storageInterface interface { NewLogFilter(filter LogFilter) (uint64, error) NewBlockFilter() (uint64, error) NewPendingTransactionFilter() (uint64, error) GetFilter(filterID uint64) (*Filter, error) UpdateFilterLastPoll(filterID uint64) error UninstallFilter(filterID uint64) (bool, error) } 3. Sequencer服务 当前代码库中，暂未实现permissionless sequencer功能，ProofOfEfficiency.sol合约中也暂未实现registerSequencer等接口。 参考资料 [1] Ethereum JSON-RPC Specification [2] PoE [3] zkProver debugging [4] Hermez 1.5 - Merkle Tree spec [5] PoE - 1.5 附录：Polygon Hermez 2.0 zkEVM系列博客 ZK-Rollups工作原理 Polygon zkEVM——Hermez 2.0简介 Polygon zkEVM网络节点 Polygon zkEVM 基本概念 Polygon zkEVM Prover Polygon zkEVM工具——PIL和CIRCOM 转载：https://blog.csdn.net/mutourend/article/details/126409344
1. 引言 前序博客有：ZK-Rollups工作原理 近期，Polygon团队开源了其Hermez 2.0 zkEVM代码，公开测试网即将上线：https://github.com/0xpolygonhermez（Go/C++等等） 上图源自 Jordi 2022年10月分享Technical Details of the Opcode Compatible zkEVM by Jordi Baylina | Devcon Bogotá。 使用ZK proofs对以太坊扩容的基本方法是构建ZK rollup，这是一种Layer 2协议，它“rollup”了大量交易，并使用单个ZK validity proof 向以太坊主网证明了所有交易。 采用ZK rollup实现以太坊扩容的优势明显： 1）以单笔交易 代替了 很多笔交易 2）增加了交易吞吐量 3）节约了手续费 4）降低了延迟等待 Polygon Hermez 2.0 zkEVM与以太坊主网EVM兼容。可将以太坊主网的智能合约部署在Polygon zkEVM中，可共用以太坊现有的开发工具和套件。 2. 何为Hermez 2.0？ Hermez 1.0为以太坊第一个去中心化ZK Rollup，于2021年3月上线，可实现2000tps，足以满足ETH及ERC-20 token转账需求。 Hermez 2.0定位为zero-knowledge Ethereum Virtual Machine（zkEVM）—— 可以透明方式运行以太坊交易 并 为智能合约附加zero-knowledge-proof validations。为此，需要重建 透明部署现有以太坊智能合约的所有EVM opcodes。 相比于Hermez 1.0，Hermez 2.0的最主要功能是提供智能合约支持。 Polygon Hermez 2.0总体架构为： Polygon zkEVM中包含的主要元素有： 1）共识算法 2）zkNode软件 3）zkProver 4）LX-to-LY Bridge 5）Sequencers和Aggregators（需要这2个角色 以达成网络共识） 6）Hermez 2.0网络中的活跃用户，以创建交易 根据Asif Khan 2023年2月10日twitter，上面Polygon zkEVM架构中主要有四大部分组成： 1）zkNode（Synchronizer、Sequencer&Aggregator角色，以及GRPC）：客户端可启用Sequencer角色synchronization，或者是，Aggregator角色synchronization。 1.1）Synchronizer：负责获取Sequencers所提交的所有数据（transactions） + 获取Aggregators所提交的所有数据（validity proofs）。 1.2）Sequencers：以Sequencer模式运行zkEVM节点，可为Trusted或Permissionless模式。 1.2.1）Sequencers会：接收L2交易+L2用户支付的交易手续费 -》 对L2交易进行排序 -》生成batches -》 以sequences的形式将batches提交到Consensus Contract的storage slots。 1.2.2）Sequencers的经济模型为：向L1提交有效交易，以获利。 transaction pool中会采用排序算法，选择利润最丰厚的交易进行处理。 向L1提交batches时，需以MATIC支付手续费。 1.3）Aggregators：以Aggregator模式运行zkEVM节点。 1.3.1）Aggregators会：从L1中获取Sequencer提交的L2 batches -》将这些L2 batches发送给zkProver -》生成ZK proof以证明该batch的完整性。 Aggregator会使用特殊的链下EVM解析器来生成ZK proofs。 1.3.2）Aggregators的经济模型为： Sequencers提交L2 batch所支付的MATIC手续费，将给为该batch提供ZK proof的Aggregator。 随着需求的增加，所需的MATIC手续费将增加，从而激励Aggregators生成可验证的validity proof Aggregators需平衡2方面的成本：向L1提交validity proof交易成本；运行aggregator节点和zkProver服务器成本 2）Consensus Contract（PolygonZkEVM.sol）：会将Sequencers提交的L2 batches存储在PolygonZkEVM.sol合约内，创建a repo of sequences。支持Aggregator公开验证L2 State root的变化。 在向Consensus Contract commit新L2 State root之前，需对validity proof进行验证。verified proof可作为一组batches引起变更到特定L2 State 的不可否认的证据。 3）zkProver：Polygon zkProver是真正的游戏改变者，是区别于其它rollups的关键所在，由Polygon Zero、Polygon Miden、Polygon Hermez共同加持。原因在于： 工业级别的生成证明时长最低之一 采用zk virtual machine来仿真EVM Polygon zkEVM中交易的验证和证明均由zkProver处理。zkProver会以多项式和汇编语言形式执行复杂的数学计算，所生成的proof最终在合约内验证。 zkProver有独立的线程，其高性能涉及的因素很多，后续将更新。 4）LX-to-LY Bridge：为智能合约，使得用户可在LX与LY层之间进行资产转移。如zkEVM中的（L1-L2）为去中心化bridge，用于安全的存入和取出资产。LX-to-LY Bridge由2个合约组成： Bridge L1 Contract：部署在以太坊筑网上，用于管理rollups之间的资产转移。 Bridge L2 Contract：部署在某特定的rollup上，负责主网与该rollup之间的资产转移 3. Hermez 2.0 共识算法——PoE Hermez设计为去中心化的，不同于Hermez 1.0中的Proof-of-Donation（PoD）共识算法，Hermez 2.0将采用Proof of Efficiency（PoE）共识算法。 在zk-rollups中实现去中心化共识是充满挑战的，因为在Layer2中PoS共识存在leader选举的一些问题（如无法硬分叉、少量%质押可能引起的攻击——如恶意一直产空块等）。 Hermez 2.0中的节点需要高效生成zk validity proofs（对于prover来说是计算密集型的），使得整个L2层正常工作，将产batch（L2区块）的权利给随机的validator是无法满足该要求的。 Proof of Donation/Proof of Burn（PoD/PoB）是一种去中心化竞价模式，以获得特定时间段的产块权利。此时，需要设置经济激励以使validators是very efficient的 以 具备竞争力，这是一个很大的改进。 但是Proof of Donation/Proof of Burn（PoD/PoB）共识主要存在以下问题： 1）对于某个特定时间，网络由单一actor控制，其可能作弊，即使有办法来减轻影响，也无法保证L2 service层不受一丝影响，特别是在bootstrapping阶段。 2）对于coordinators/validators来说，竞价协议是昂贵和辅助的，也是当前最有效的激励方式。由于拍卖需要提前一段时间进行竞标，因此很难对它们进行自动化，而且预测的复杂性很高。 3）选出”the best“ operator使得”赢家通吃“的问题 。使得稍差性能的运营商无法参与竞争，结果就是，控制网络的运营商变得非常集中，从而产生抗审计限制。 L2 zk-rollup共识算法应满足如下要求： 1）无权限访问生产L2批次。 2）效率是网络性能的关键。 3）避免任何一方的控制。 4）防止恶意攻击。 5）总的验证工作量与网络中的价值成正比。 Proof-of-Efficiency（PoE）共识算法分2步实现，可由不同的参与者完成： 1）第一步的参与者称为Sequencer。sequencer可能是a wallet app、an exchange、或者a game等等。sequencer之间可相互协作，也可不协作。负责将L2的交易打包为batches并添加到L1的PoE智能合约中。 2）第二步的参与者称为Aggregator。Aggregator之间是竞争的。负责检查transaction batches的有效性，并提供validity proofs。 为此，PoE智能合约有2个基本的接口： 1）sendBatch：用于接收Sequencer的batches 2）validateBatch：用于接收Aggregator的validity proof，进行validate batches PoE智能合约对Sequencer的要求为 1）任何运行zkNode（为运行Hermez 2.0 node所必须的软件），均可成为Sequencer。 2）每个Sequencer都必须以$Matic token来支付fee，以此来获得创建和提交batches的权利。 3）提交了有效batches（由有效交易组成）的Sequencer，受交易请求者（网络用户）支付的手续费激励。 4）Sequencer收集L2交易，将其预处理为a new L2 batch，然后将该batch作为a valid L1交易提交到PoE智能合约。 PoE智能合约对Aggregator的要求为： 1）Aggregator负责为由Sequencer已提交的L2交易生成validity proof。 2）Aggregator除了运行Hermez 2.0 zkNode软件之外，还需要专业的硬件来创建zero-knowledge validity proof。在此称为zkProver。 3）第一个为某个batch或某些batches提交validity proof的Aggregator，可赚取Sequencer为其batch支付的Matic费用。 4）Aggregator仅需表明其验证交易的意图，并按照其自身的策略来生成validity proof参与竞争。 3.1 PoE共识中的Sequencer Sequencer负责收集用户的L2交易。 Sequencer在网络中选择并预处理a new L2 batch，然后将所选中的所有L2交易 为data 发送一笔L1 TX。 任何人都可成为Sequencer，这是无需许可的角色，包含了a gateway to the network。 有趣的点在于： 对于zk-rollup模式，这些提议的batches会记录在L1交易内； 对于Validium模式，这些提议的batches会记录在另一data availability network。 Sequencer提议batches的动机在于： 其pool内交易的经济值 或 满足用户的要求（Sequencer可要求改变手续费） 为向L1网络提议新batch，Sequencer需要发起一笔包含了所有batch transactions data的L1交易并支付相应的gas费，同时，协议中定义了 其必须存入以$MATIC token支付的额外费用。这样，可激励Sequencer提议有效的batches以及有效的交易。 根据网络负载情况，batch fee将是可变的，可根据protocol合约中的某个参数来计算。 L1交易内的batches，是以CALLDATA形式存在的，将用作L2网络的data availability，且任何新的、无许可的节点都可同步和重构该状态。 一旦L1交易被mined，其中定义的L2 TXs的data availability将按指定顺序执行，从而构建了确定性的new state，网络节点可将其计算为a virtual future state。当该new state 的 valid proof（ZKP）生成并在L1 mined时，该new state即settled了。这就是共识协议的第二步。 3.2 PoE共识中的Aggregator zk-rollups的主要优势在于，validity proof所提供的交易的快速finality。而PoE中的第二步用于强化这些proof的有效性。 与Sequencer类似，Aggregator也是PoE中任何人都可参与、无需许可的参与方。 第一个为L2 new state 创建validity proof的aggregator即获得了创建validity proof的权利，以及部分交易手续费。 具体为： Sequencer提议的batches按其在L1中的顺序排序，且在L1中包含了相应的transaction data。 PoE智能合约验证第一个validity proof通过后，即接受并更新到a new valid state，该new valid state中可包含一个或多个proposed batches。 aggregator需要自己指定触发proof生成的标准，并基于自己的策略参与竞争。 如，若对于包含了少量TXs的batches，某些aggregator会认为不值得为其生成proof，直到有更多的价值后，才会为N NN个proposed batches生成proof证明相应的状态改变。而另外一些aggregator可能采用不同的策略。 对于提交晚了的proof，智能合约会执行Revert操作。所以，如果不是第一个提交validity proof的aggregator，其损失的生成proof的开销，但是大部分gas费都可收回。 当然，当且仅当aggregator正确处理了proposed batches时（按顺序处理batch内的所有交易），该validity proof才有效。该机制类似于Polygon Hermez v1.0中的 ”Force tx“，有助于避免审查。 这种机制有助于避免L2被单一方控制，以及可避免许多潜在攻击，尽管任何Sequencer都可提议a batch，但是也要承担相应的费用。 而Aggregator也可 以非许可的方式参与进来，因为总会有某个经济价值点，使得有人有兴趣来生成并提交validity proof。 Polygon Hermez网络会启动一个Boot Aggregator backing up，使得在bootstrapping阶段，可 以特定频率来生成new validity proof。 3.3 PoE的经济模型 手续费的分发规则为： 1）L2 TXs的手续费将由创建并提交validity proof的aggregator处理并分发。 2）所有TX手续费将发送到提交每个batch的相应Sequencer。 3）创建a batch的deposited fee 将发送给 将该batch包含在validity proof中的Aggregator。 即： Sequencer赚L2的交易手续费，但是只有相应的validity proof提交后，Sequencer才能获得其所提交的batch内的L2交易手续费； Sequencer向L1提交batch时，除L1交易手续费之外，还需额外支付一个以MATIC token支付的费用，这笔MATIC token费用将由 提交了该batch对应的validity proof的Aggregator获得。即Aggregator赚取Sequencer支付的batch MATIC手续费。 4. zkNode软件 网络中需发布一个客户端，以实现同步并覆盖参与者角色——如Sequencer或Aggregator。zkNode就是相应的客户端，为运行Hermez 2.0 node所必须的软件。 Hermez 2.0参与者可自行决定其参与方式： 1）仅作为一个简单的节点，以获悉网络状态。 2）以Sequencer或Aggregator角色参与batch production。 Aggregator除运行zkNode之外，还需要使用zkEVM的核心部分来进行验证。zkEVM的该核心部分称为zkProver（又名Prover）。 Hermez 2.0 zkNode模块化架构流程图为： 上图中的： 1）Synchronizer： 除sequencing和validating进程之外，zkNode中还支持对batches和validity proofs的同步，仅在batches和validity proofs提交到L1之后才做相应的同步。该子模块称为Synchronizer。 Synchronizer负责监听以太坊链上事件，包括new batches，以保持state的完全同步。从events中读取的信息必须存储在database中。Synchronizer还必须处理可能的reorgs——通过检查last ethBlockNum 和 last ethBlockHash 是否已同步。 2）RPC（remote procedure calls）接口： 为与以太坊兼容的JSON RPC接口。支持将zkEVM与现有工具集成，如Metamask、Etherscan、Infura。RPC向Pool添加交易，通过只读方法与State交互。 3）State子模块： 实现了Merkle Tree并连接到DB后台。会： 在block level检查integrity（即，关于gas、block size等相关信息）， 检查某些交易相关信息（如签名、足够的balance等） 将smart contract（SC）代码存储到Merkle tree中，并使用EVM来处理交易。 5. zkProver Hermez 2.0采用了最先进的零知识技术。它将使用一个称为zkProver的零知识验证程序，该程序旨在在任何服务器上运行，并且正在设计为与大多数消费类硬件兼容。 每个Aggregator将使用zkProver来验证batches并提供validity proofs。 Hermez 2.0 zkProver的总体架构为： zkProver包含了： 1）Main State Machine Executor。 2）一系列secondary State Machines，每个secondary State Machine有其自己的executor。 3）STARK-proof builder 4）SNARK-proof builder 简而言之，zkEVM以多项式形式表示状态变化。因此，每个proposed batches必须满足的约束实际上是多项式约束或多项式恒等式。也就是说，所有有效batches必须满足某些多项式约束。 zkProver的简化数据流为： 5.1 zkProver的Main State Machine Executor Main Executor负责处理zkEVM的执行，会： 使用一种Polygon团队特意开发的新的zero-knowledge Assembly language（zkASM）来解析EVM Bytecodes。 会设置多项式约束，要求每个有效的transaction batch都必须满足相应的多项式约束条件。 使用另一种Polygon团队开发的Polynomial Identity Language（PIL）来对多项式约束进行编码。 5.2 zkProver的一系列secondary State Machines zkEVM中证明交易正确性的每一步计算都是一个state machine。 zkProver是整个项目中最复杂的部分，包含了多个state machines： 一些执行bitwise function的state machines（如XOR/Padding等等） 执行哈希运算的的state machines（如Keccak、Poseidon等等） 执行验签的state machines（如ECDSA等等） 因此，二级状态机的集合是指zkProver中所有状态机的集合。它本身不是一个子组件，而是单个二级状态机的各种执行器的集合。具体的二级状态机有: Binary SM Memory SM Storage SM Poseidon SM Keccak SM Arithmetic SM 根据每个SM负责的具体操作，一些SM同时使用zkASM和PIL，而另一些SM仅使用一种。 Hermez 2.0 state machines之间的依赖关系为： 5.3 zkProver的STARK-proof builder STARK，为Scalable Transparent ARgument of Knowledge的简称，是一种无需可信设置的proof system。 STARK-proof builder负责生成zero-knowledge STARK-proofs，以证明所有的多项式约束条件都满足。 State machines负责生成多项式约束。 zk-STARKs用于证明batches满足这些多项式约束条件。特别地，zkProver使用Fast Reed-Solomon Interactive Oracle Proofs of Proximity（RS-IOPP），俗称为FRI，来对zk-STARK证明加速。 5.4 zkProver的SNARK-proof builder SNARK，为Succinct Non-interactive ARgument of Knowledge的简称，是一种proof system。 由于STARK-proof的size要大于SNARK-proof的size，Hermez 2.0 zkProver使用SNARK-proof来证明STARK-proof的正确性。结果是，将SNARK-proof做为validity proof，使得可更便宜地在L1上验证该validity proof。 目标是生成CIRCOM circuit，将CIRCOM circuit用于生成SNARK proof 或 用于验证SNARK proof。 最终是采用PLONK SNARK proof还是GROUTH16 SNARK proof还暂未确定。 6. LX-to-LY Bridge 典型的Bridge智能合约由2个智能合约组成，一个智能合约部署在链A，另一个部署在链B。 Hermez 2.0的L2-to-L1 Bridge智能合约也是由2个合约组成： Bridge L1 Contract：部署在以太坊主网，用于管理rollups之间的资产转移。 Bridge L2 Contract：部署在某个特定的rollup，负责主网与该rollup之间的资产转移。 除了部署位置不同外，这两个合约实际上是相同的。 通常Bridge智能合约是L2-to-L1 Bridge，但是Hermez 2.0 Bridge更灵活且可互操作。可作为任意2个L2链（L2_A和L2_B）之间的bridge，或者任意L2（如L2_X）与L1（如以太坊）之间的bridge。从而支持资产在多个rollups之间转移，从而将其命名为"LX-to-LY Bridge"。 6.1 Bridge L1 Contract Bridge L1 Contract负责2个操作： 1）bridge：将资产由一个rollup转移到另一个rollup。 2）claim：当合约从任意rollup claim时，使用claim操作。 为满足以上2种操作，Bridge L1 Contract需要有2棵Merkle tree： 1）globalExitTree：包括了所有rollups的exit trees的所有信息。 2）mainnet exit tree：包含了用户与主网交互的交易信息。 名为global exit root manager L1的合约负责管理跨越多个网络的exit roots。 exit tree结构为： 6.2 Bridge L2 Contract Bridge L2 Contract部署在L2 with Ether on it。将Ether设置在genesis上是为了实现native Ether的mint或burn。 Bridge L2 Contract还需要包含在globalExitTree Merkle Tree内的所有rollups的exits trees的所有信息。此时，一个名为global exit root manager L2的合约负责管理跨越多个网络的exit roots。 注意，当某batch在L1 PoE智能合约内验证通过后，global exit root manager L1内相应的rollup exit root将更新。 Bridge L2 Contract负责: 处理rollup端的bridge和claim操作 与globalExitTree和rollup exit tree交互以更新exit roots 7. Hermez 2.0设计思想 以上为达成Hermez 2.0设计思想的工程化实现。 Hermez 2.0的设计思想为： 1）Permissionless-ness：任何人只要运行Hermez 2.0软件即可参与到网络中。共识算法支持每个人选择成为Sequencer或Aggregator。 2）Decentralized（Data availability）：data availability对于实现去中心化来说是最至关重要的，使得每个用户都有足够的数据，以重构出某rollup的所有状态。如上所述，Polygon团队仍在探索最优的data availability配置。目标是保证没有censorship，也没有任何一方可控制网络。 3）Security：作为L2解决方案，Hermez 2.0的大部分安全性继承自以太坊。 4）Efficient and with verifiable block data：智能合约将保证任何执行状态更改的人必须：首先，正确地做；其次，创建证明状态更改有效性的proof；第三，在链上对validity proof进行验证。 8. 效率及总体策略 效率是网络性能的关键。Hermez 2.0采用了多个实现策略来保证效率： 1）第一个策略为：部署PoE，以激励效率最高的aggregator参与到生成proof的进程中。 2）第二个策略为：将所有计算链下执行，仅在链上保持必要的data和zk-proofs。 在Hermez 2.0系统的特定组件内还实现了多种其它策略，如： 1）Bridge smart contract内实现了：以UTXO模式来settle accounts，仅使用the Exit Tree Roots。 2）在zkProver中使用专门的密码学原语 来加速计算 并 使proof size最小化： 2.1）运行一种特殊的zero-knowledge Assembly language（zkASM）来解析字节码； 2.2）使用零知识证明工具如zk-STARKs——证明速度快但proof size巨大，因此，相比于将庞大的zk-STARK proof作为validity proof，替换为，用a zk-SNARK来证明zk-STARK proof的正确性，将zk-SNARK proof作为state changes的validity proof，可将验证validity proof的gas开销由500万降低为35万。 参考资料 [1] 2022年5月博客 Polygon ZK: Deep Dive Into Polygon Hermez 2.0 [2] 2022年7月博客 The Future is Now for Ethereum Scaling: Introducing Polygon zkEVM 转载自：https://blog.csdn.net/mutourend/article/details/126034757
使用Hardhat在zkEVM Testnet上部署一个Onchain的SVG动画NFT 什么是zkEVM 简而言之，Polygon zkEVM是一个二级扩容解决方案，旨在实现EVM-equivalence（等效），并允许你使用现有的Solidity代码更便宜、更快、更安全地部署合约到它。 想进一步深入了解它吗？请看我的另一篇文章：如何将ERC20代币合约部署到zkEVM Testnet。 准备网络与Token 建议使用Polygon zkEVM Bridge 站点，将网络添加到我们的钱包，并将 Goerli Token 桥接到 zkEVM Testnet： 环境要求 在我们开始之前，请确保你的电脑上已经安装了以下依赖：NVM或Node v18.15.0，并且已经配置了钱包，并且goerli 测试网代币被桥接到了zkEVM Testnet。如果你想了解如何做到这一点，请查看我的另一篇文章如何将ERC20合约部署到zkEVM Testnet。 用Hardhat部署合约 让我们建立一个ERC721 NFT，并通过Hardhat将其部署到zkEVM。 为了确保我们从新开始，我们将利用Hardhat模板生成一个基本的TypeScript项目。 # Create our project folder mkdir zkevm-erc721-hardhat; cd zkevm-erc721-hardhat; npx hardhat; # Expected Output: # Ok to proceed? (y) y # 888 888 888 888 888 # 888 888 888 888 888 # 888 888 888 888 888 # 8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888 # 888 888 "88b 888P" d88" 888 888 "88b "88b 888 # 888 888 .d888888 888 888 888 888 888 .d888888 888 # 888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. # 888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 # # Welcome to Hardhat v2.13.0 # # ? What do you want to do? … # Create a JavaScript project # ❯ Create a TypeScript project # Create an empty hardhat.config.js # Quit # # ✔ What do you want to do? · Create a TypeScript project # ✔ Hardhat project root: · /path/to/zkevm-erc721-hardhat # ✔ Do you want to add a .gitignore? (Y/n) · y # ✔ Do you want to install this sample project's dependencies with npm (hardhat @nomicfoundation/hardhat-toolbox)? (Y/n) · y 接下来，让我们安装依赖项: npme install; npm install @openzeppelin/contracts dotenv; 创建我们的NFT 我们这里的NFT，将通过引入一个动画的SVG来使它突出一点。 首先删除默认生成的模板化合约，并在其目录上创建一个新的合约。 rm contracts/Lock.sol; touch contracts/zkEVMNFT.sol; 在该文件中，复制并粘贴以下NFT solidity代码。 文件： ./contracts/zkEVMNFT.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; // Imports // ======================================================== import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts/utils/Base64.sol"; // Contract // ======================================================== contract ZkEVMNFT is ERC721 { // Extending functionality using Strings for uint256; /** * Main constructor */ constructor() ERC721("zkEVMNFT", "zkNFT") {} /** * Main minting function */ function safeMint(address to, uint256 tokenId) public { _safeMint(to, tokenId); } // The following functions are overrides required by Solidity. /** * @dev See {ERC721} */ function _burn(uint256 tokenId) internal override(ERC721) { super._burn(tokenId); } /** * Public function or burning */ function burn (uint256 tokenId) public { _burn(tokenId); } /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view override(ERC721) returns (string memory) { // Validation require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); // SVG Image bytes memory imageSVG = abi.encodePacked( " ", " ", "<g clip-path=\"url(#clip0_108_2)\">", "<rect id=\"background\" width=\"256\" height=\"256\" fill=\"#ff0000\"/>", "<rect x=\"28\" y=\"28\" width=\"200\" height=\"200\" fill=\"white\"/>", "</g>", "<defs>", "<clipPath id=\"clip0_108_2\">", "<rect width=\"256\" height=\"256\" fill=\"white\"/>", "</clipPath>", "</defs>", "<text xmlns=\"http://www.w3.org/2000/svg\" id=\"text\" x=\"128\" y=\"150\" fill=\"black\" style=\"width: 256px; display: block; text-align: center;\" text-anchor=\"middle\">", tokenId.toString(), "</text>", " " ); // JSON bytes memory dataURI = abi.encodePacked( "{", "\"name\": \"NUMSVG #", tokenId.toString(), "\",", "\"image\": \"data:image/svg+xml;base64,", Base64.encode(bytes(imageSVG)), "\"", "}" ); // Returned JSON return string( abi.encodePacked( "data:application/json;base64,", Base64.encode(dataURI) ) ); } } 配置 Hardhat 接下来我们将配置Hardhat配置文件，使环境变量能够被加载，配置对网络的支持，并调整优化。 文件： ./hardhat.config.ts // Imports // ======================================================== import { HardhatUserConfig } from "hardhat/config"; import "@nomicfoundation/hardhat-toolbox"; import dotenv from "dotenv"; // Config // ======================================================== dotenv.config(); const config: HardhatUserConfig = { solidity: { version: "0.8.18", settings: { optimizer: { enabled: true, runs: 200, } } }, networks: { mumbai: { url: `${process.env.RPC_MUMBAI_URL || ''}`, accounts: process.env.WALLET_PRIVATE_KEY ? [`0x${process.env.WALLET_PRIVATE_KEY}`] : [], }, zkevmTestnet: { url: `${process.env.RPC_ZKEVM_URL || ''}`, accounts: process.env.WALLET_PRIVATE_KEY ? [`0x${process.env.WALLET_PRIVATE_KEY}`] : [], } }, }; // Exports // ======================================================== export default config; 为Hardhat添加环境变量 一旦我们完成了Hardhat的配置，我们将创建一个环境变量文件来存储我们的关键值。 touch .env; 注意：记得保护你的私钥不被人窥视。 文件： ./env RPC_MUMBAI_URL=https://rpc.ankr.com/polygon_mumbai RPC_ZKEVM_URL=https://rpc.public.zkevm-test.net WALLET_PRIVATE_KEY=<YOUR-WALLET-PRIVATE-KEY> 最后，让我们修改我们的部署脚本以指向正确的合约。 文件： ./scripts/deploy.ts // Imports // ======================================================== import { ethers } from "hardhat"; // Main Deployment Script // ======================================================== async function main() { // Make sure in the contract factory that it mateches the contract name in the solidity file // Ex: contract zkEVMNFT const zkERC721Contract = await ethers.getContractFactory("zkEVMNFT"); const contract = await zkERC721Contract.deploy(); await contract.deployed(); console.log(`zkEVMNFT deployed to ${contract.address}`); }; // Init // ======================================================== // We recommend this pattern to be able to use async/await everywhere // and properly handle errors. main().catch((error) => { console.error(error); process.exitCode = 1; }); 部署出NFT合约 一旦一切就绪，我们现在可以将我们的NFT合约部署到zkEVM Testnet # FROM: ./zkevm-erc721-hardhat npx hardhat run scripts/deploy.ts --network zkevmTestnet; # Expected Output: # zkEVMNFT deployed to 0x7F77cF06C84A7bae6D710eBfc78a48214E4937a7 如果我们在资源管理器上进入合约，我们可以看到以下结果： https://explorer.public.zkevm-test.net/address/0x7F77cF06C84A7bae6D710eBfc78a48214E4937a7 在zkEVM区块浏览器上验证我们的NFT合约 我们可以直接通过Hardhat与合约交互，进行铸币和读取，但我们将使用区块链浏览器来连接和铸币一个新的NFT。 在我们进入验证过程之前，我们将通过标准输入JSON进行验证，我们需要一个文件来上传。 如果我们看一下我们的build-info文件夹，我们可以找到一个JSON文件，我们只需要得到input部分。复制整个部分并创建一个名为verify.json的新文件，并将JSON数据粘贴在那里。 记住，你需要事先编译合约，用npx hardhat compile生成这些文件。 文件： ./artifacts/build-info/your-build-file.json 创建 JSON 文件 我们新的标准输入JSON文件为verify.json 接下来在区块资源管理器上访问该合约，并开始验证过程 zkEVM Testnet 区块浏览器验证和发布合约 zkEVM Testnet Block Explorer 通过标准输入 JSON 验证 zkEVM Testnet Block Explorer验证Conifgure verify.json 一旦验证通过，我们就可以在zkEVM Testnet block explorer上看到我们合约的完整代码。 https://explorer.public.zkevm-test.net/address/0x7F77cF06C84A7bae6D710eBfc78a48214E4937a7/contracts#address-tabs 在 zkEVM 测试完上铸造 NFT 现在我们的合约已经被验证了，我们可以通过区块浏览器和我们的钱包与它交互。 为了本教程的目的，我们将铸造一个NFT tokenID为4，以纪念距离zkEVM主网测试版发布的天数--2023年3月27日 https://explorer.public.zkevm-test.net/address/0x7F77cF06C84A7bae6D710eBfc78a48214E4937a7/write-contract#address-tabs 在zkEVM Testnet上铸造NFT 确认在zkEVM Testnet上成功铸造了一个NFT 查看我们的zkEVM NFT 我们在zkEVM Testnet上铸造了NFT，现在我们希望能够直观地看到这个链上SVG NFT zkEVM Testnet区块浏览器读取NFT 复制tokenURI查询的数据部分，在Chrome浏览器中，将其粘贴到地址栏。 zkEVM Testnet NFT tokenURI JSON数据 从JSON中复制image值，在Chrome中，将其粘贴在地址栏中。 zkEVM Testnet NFT SVG 我们成功地部署了合约，验证了合约，并且我们能够看到铸造的NFT。 完整的zkEVM NFT代码库 点击这里查看本教程的完整代码库. 下一步是什么？ 请关注更多关于zkEVM的教程。 如果你想更深入地了解zkEVM，请查看Polygon University。 转载：https://learnblockchain.cn/article/5579
如何使用模块化可能是突破不可能三角的一个途径。 介绍 大约十年前，世界见证了移动智能手机的增长。当时，一些大公司认为他们可以通过引入模块化架构来彻底改变智能手机。2013 年，谷歌发布了 Project Ara，这是一款采用模块化设计的新型智能手机。与今天由密封的铝和玻璃片制成的“单体”手机不同，Ara 允许用户以多种方式定制他们的手机，使所有基本部件都可以模块化。你不会被迫频繁升级到新手机。相反，你可以根据自己的喜好简单地为旧手机添加最好的新部件。可悲的是，模块化并没有在手机生态系统中取得成功，这个概念仍然是一段几乎被遗忘的技术历史。 模块化智能手机可以作为模块化区块链的警示故事，因为尽管当前工具存在合理的缺点，但新的和令人兴奋的解决方案的炒作并不能保证长期的胜利。然而，就区块链而言，用户对可扩展性的需求已经在推动开发人员构建和采用模块化架构。这种需求使得模块化区块链架构不太可能与模块化智能手机那样，遵循相同的命运。 但究竟什么是模块化区块链架构？我们如何确保这些解决方案不会成为另一个 Project Ara？这篇文章希望能回答所有这些问题。 单体和模块化 在进一步深入研究之前，让我们梳理一下单体架构和模块化架构之间的区别。介绍这个概念的最简单方法是举一个类似而熟悉的概念的例子。正如手机有一些核心部件，比如摄像头、电池和触摸屏，区块链也有核心部件。 iPhone 是“单体”手机的一个很好的例子。它配备了使用手机所需的所有部件，并没有提供太多定制选项。当然，你可能无法对内部进行太多定制，但它既流畅又快速。但是，有时你可能希望进一步自定义你的手机。假设随着岁月的流逝，新手机配备了更好的相机。过时手机的其余部分可能工作正常，但使用现有的相机，你无法获得新手机那样的体验。 使用模块化架构，你不必购买全新的手机。相反，你可以像乐高积木一样更换相机，然后换上更好的相机。 Google 的 Project Ara 是模块化手机的一个例子。这款手机由积木制成，可以根据你的选择换入和换出零件。只要有兼容的部件，Ara 都会支持。 与智能手机一样，区块链由多个基本组件组成；这些组件如下所示： 共识 区块链的共识层通过计算机网络就链的状态达成共识来提供排序和最终性。 执行 该层通过运行指定的代码来处理交易。它也是用户通常与区块链交互的地方，例如通过签署交易、部署智能合约和转移资产。 结算 结算层用作验证在第 2 层执行的活动的平台，例如Rollup以及争议解决。最重要的是，它是记录实际区块链最终状态的地方。 数据可用性 验证状态转换是否有效所需的数据应发布并存储在该层上。在发生攻击或操作失误，区块生产者未能提供交易数据的情况下，这应该是容易检索和验证的。 简而言之，单体式区块链在单个软件中自行执行所有这些任务，而模块化区块链将它们分成多个软件。现在，你可能想知道一次处理所有这些任务的区块链有什么缺点？ 这又回到了古老的问题，即可扩展性不可能三角困境。 可扩展性不可能三角困境表示区块链只能具有以下三个特征中的两个：去中心化、安全性和可扩展性。 现有的（新的）单体区块链倾向于针对三角的安全和可扩展进行优化。比特币和以太坊更加强调去中心化和尽可能安全。但都是有代价的。去中心化区块链通常没有高带宽来执行交易。以太坊每秒最多处理 20 笔交易，而比特币的处理能力甚至更低。如果我们想在全球范围内使用这些协议，每秒 20 个交易是远远不够的。一些单体链至少在理论上，可以使我们更接近全球规模，因为它们的 TPS 和总吞吐量是足够的。然而，他们往往缺乏去中心化，而这是区块链技术的核心原则。 模块化架构的目的是将区块链的一些工作外包出去，在保持去中心化的同时做出更多性能更高的链。让我们讨论一下以太坊，并讨论它有望如何利用模块化。 以以太坊为中心的生态系统 大多数第 1 层区块链 现在的以太坊是一个单体的区块链。今天大多数其他第 1 层区块链也将被归类为单体区块链。就像 iPhone 的例子一样，单体区块链的某些功能有时会开始落后于较新的替代方案，导致在对比最新和最具创新性的第 1 层时流失开发人员和消费者。为了解决以太坊目前在吞吐量方面的瓶颈，开发人员正在构建Rollup执行层以增加交易带宽。 模块化执行层 示例：Optimism, Arbitrum, Fuel, Scroll, ZkSync Rollups 作为执行层是当今以太坊上使用最广泛的扩容方法。Rollups 是独立的区块链，具有更高的交易执行性能，其最终结果在以太坊上结算，以有效继承其（更好的）安全性和去中心化。 在高层次上，Rollup只是一个区块链，它将其区块的结果发布到另一个区块链。然而，这只是Rollup的一个组成部分，因为你还需要欺诈和有效性证明以及一种无许可插入交易的方法。Rollups 通过在两个智能合约之间同步数据来实现这一点，一个部署在第 1 层，一个部署在第 2 层。这种设计使其成为 rollup 而不是侧链。这些关键组件对于 rollup 的安全是必不可少的，因为没有这些组件，rollup 可以被停止或审查 。 目前，大多数 rollup 都提供 EVM 兼容性以帮助以太坊开发人员轻松迁移，但在计算效率和开发易用性方面，执行层可能有更好的替代方案。用户甚至可能想要更多 EVM 等效链上不存在的功能，例如帐户抽象。鉴于开发人员的偏好广泛，这种趋势很可能会持续下去，我们将看到更多新颖的解决方案进入市场，例如 SolanaVM 和 MoveVM 执行层。Fuel 是一个不兼容 EVM 的执行层的例子，它的唯一重点是执行在其他Rollup上不可能执行的计算。Fuel 也是第一个“模块化执行层”，正如我们将看到的那样，它允许它成为一个主权Rollup、一个结算链，甚至是一个单体区块链。Rollup 只是执行层，而 Fuel 可以是更多。 Fuel 可以用普通 Rollups 做不到的方式进行模块化。因此得名“模块化执行层”。我们将很快深入探讨 Celestia 架构的机制。（图源：Fuel） Fuel 显示执行层可以具有创造性，并且优先考虑计算速度而不是 EVM 支持。虽然许多熟悉模块化架构的人都知道 Fuel，但另一个强大的竞争者却鲜为人知。最有趣的即将到来的模块化执行层之一是Kindelia。除了是最快的计算层之一，Kindelia 还拥有一个利用其虚拟机的独特证明系统。Kindelia 的 HVM 提供了一种近乎即时的证明检查器，内置于他们名为 Kind 的智能合约语言中。Kind是必不可少的，因为智能合约可以在它们的代码中证明它们的代码是安全的，不会被利用并且操作正确。这种类型的设计可以解决智能合约编码不当的问题，并使我们免受当今困扰智能合约的漏洞之苦。这只是Kindelia比其他执行层提供价值的一种方式。 但在执行层方面的扩展只是难题的一个部分。开发人员寻求进一步模块化单体区块链，以挤出每一点可能的性能。这就引除了如何模块化数据可用性层。 模块化数据可用性层 示例：Metis、ZkPorter、Anytrust Validium 是一种Rollup，其数据在链下而不是存储在链上。 但为什么我们要将数据转移到链下呢？这是因为我们正在尝试优化数据可用性。Rollup系统的效率在很大程度上取决于其数据可用性层的能力。当底层无法处理Rollup的交易排序器生成的数据量时，就会导致处理交易的瓶颈。因此，rollup 无法处理额外的交易，导致 gas 费用增加和/或执行时间变慢。换句话说，rollup 的数据可用性层的性能是决定其依靠的单体区块链交易处理能力和相关费用的关键因素。 validiums 的缺点是它们是链下的，引入了更多的信任假设。我们想要一个链上解决方案来提高以太坊的数据可用性层。答案是 Danksharding。 Danksharding 与以太坊的整合将其转变为一个用于结算和数据可访问性的精简平台。 Danksharding 的创新之处在于它能够将这些概念合并成一个有凝聚力的单元。Rollup证明和数据在同一块内进行验证，从而创建一个无缝且高效的系统。但是，作为其正常操作的一部分，Rollup需要为其压缩数据提供大量存储空间。Danksharding 为这一需求提供了解决方案，提供了跨多个Rollup实现数百万 TPS 的潜力。Danksharding 是一种将网络活动分成片以增加data blob 空间的技术。data blob 是以太坊中一种更高效和标准化的数据格式，可以承载大量数据，并被 rollups 使用以降低 gas 费用。Danksharding 利用“数据可用性抽样”使节点能够通过仅检查一小部分来验证大量数据，为未来铺平道路，使更便宜和更快的第二层网络能够蓬勃发展，同时实现以太坊的直接交易。 Danksharding 很棒，因为它将继承以太坊本身的所有安全性和去中心化。然而，这有一个缺点。由于以太坊的发展速度相对缓慢，我们可能需要数年时间才能将 Danksharding 正确实现到以太坊中。EIP-4844 计划引入 Proto-Danksharding，这是实现 Danksharding 的第一步。EIP-4844 通过引入容纳数据块的新交易来增强以太坊。这种用于Rollup数据的专门存储为更具成本效益的收费市场铺平了道路。 如果你想要一个快速的数据可用性层但又不想坐等 Danksharding 发布怎么办？Celestia 正是提供了这一功能的协议。转变以以太坊为中心的模块化视角，我们值得深入研究 Celestia，看看模块化区块链还可以如何解释。 以 Celestia 为中心的生态系统 Celestium 是一种独特的解决方案，它结合了 Celestia 的数据可用性和以太坊的结算及共识。Danksharding仍然是最安全的方法，因为它融入了以太坊、去中心化和稳健性。然而，一些Rollup 宁愿现在就寻求可扩展性，而不是等待 Danksharding 被实施到以太坊中。 对于等不及 Danksharding 的项目，一种可能的选择是利用链下数据可用性解决方案，例如 Validiums，它利用“数据可用性委员会”（DAC）来证明数据可用。然而，这种方法不那么去中心化或安全，因为它依赖于多重签名，并且无法验证 DAC 当前是否诚实或他们过去是否诚实。 Celestium 提供了比 DAC 更安全的替代方案。在 Celestium 中，数据可用的证明由整个 Celestia 验证者集的质押支持，这意味着如果 ⅔ 的验证者提供了不正确的信息，他们可能会被削减并可能损失一大笔钱。与不存在惩罚的 DAC 不同，Celestium提供了一个严厉和即时的反应。 此外，用户可以通过在区块上运行数据可用性采样并检查量子引力桥（Quantum Gravity Bridge）来验证 Celestia 的诚实性，量子引力桥是从 Celestia 到以太坊的无信任单向消息传递桥。桥通常是任何解决方案中最脆弱的部分，因此必须建立冗余。 Celestium 与 Danksharding 一起利用数据可用性采样 (DAS) 来验证所有数据的非恶意性质。DAS 允许节点通过下载随机片段并在任何部分丢失时发出警报来确保块的可用性。该警报系统只是采用欺诈证明（例如 Celestia）的 DAS 机制的一个方面。在像 Danksharding 这样的有效性证明 DAS 机制的情况下，不需要警报系统，因为有效性证明保证了纠删码和承诺的正确性。这些机制降低了隐藏区块数据的风险，并确保大量节点随机检查区块。 一个节点随机抽取一个区块来检查它的可用性。（来源：Vitalik Buterin） 数据抽样是使Celestia和Danksharding如此安全的原因。至少用户知道，如果发生损坏，他们可以很快发现。相比之下，在DAC这个黑匣子里，可能会有一年的“腐坏”，而没有人会意识到这一点。 示例：Fuel 与以太坊上的传统Rollup相比，主权Rollup的功能不同。与标准Rollup不同，主权Rollup不依赖第 1 层上的一组智能合约来验证区块并将其附加到规范链。相反，区块作为原始数据直接发布到链上，rollup 上的节点负责验证本地分叉选择规则以确定正确的链。这将结算责任从第 1 层转移到Rollup。与传统的 rollup 不同，在主权 rollup 和 Celestia 之间没有建立信任最小化的桥梁。这可以被视为一个负面因素，因为你希望桥接尽可能信任最小化，但这确实为主权Rollup提供了通过分叉独立升级路径的优势。并且比非主权Rollup提供的升级更加安全。从技术上讲，我们不应该将其视为Rollup，因为Rollup通常意味着具有统一的结算和数据可用性层。因此，主权Rollup也简称为主权区块链。 为了让开发人员更容易在 Celestia 上创建主权Rollup，Celestia 创建了 Rollmint，取代 Tendermint 作为共识机制。这使得 rollups 能够直接向Celestia发布区块 ，而不是通过 Tendermint 程序。通过这种设计，链背后的社区拥有完全的主权，不受其他状态机的权威约束。这使其他与以太坊上智能合约或Rollup背后的社区不同，后者受以太坊社区社会共识的约束。 结算链示例：Fuel、Cevmos、dYmension 结算Rollup 是指创建独立和模块化结算组件的。目前，rollups 使用以太坊主链进行结算，但除此之外还有其他解决方案。以太坊链与非Rollup应用（智能合约交易）共享网络，导致扩容受限和缺乏特定优化。 一个理想的Rollup结算层将只允许Rollup智能合约和Rollup之间的简单转账，同时禁止非Rollup应用或使其交易高昂。 Celestia 的设计为开发人员提供了一个标准的全局状态共识层，以构建作为单一信任最小化执行层Rollup。它还可以在同一全局状态共识层上的Rollup之间实现信任最小化的桥接，这是当前架构中没有的新概念。开发人员是否会采用这种新的跨Rollup范式还有待观察。 结算链的示例包括 Cevmos、Fuel 和 dYmension，其中 Polygon 通过建立其对模块化架构的解释与 Celestia 竞争。在Polygon的模块化设计中，Polygon Avail作为数据可用性和共识的模块化组件，Polygon区块链作为结算层。 单体链的案例 与较新的模块化解决方案相比，许多介绍模块化区块链的文章通常将单体第 1 层区块链称为过时的“恐龙”技术。目前，很难完全支持这钟说法，因为这些扩容解决方案的一个主要问题是它们给整个系统添加了更多的信任假设。虽然我们已经讨论了大多数 DAC 和 validium 是如何不安全的，但这甚至可以扩展到执行层（即Rollup）。 当今使用最广泛的一些Rollup仍然没有真正去中心化，即使它们获得了数十亿美元。在撰写本文时，Optimism 仍然没有功能性欺诈证明，而 Arbitrum 可以从单个多重签名中改变。这两种协议都在努力解决这些问题，作为其开发计划的一部分，但重要的是要记住，去中心化并不是因为协议使用特定架构就一定能实现。此外，所有模块化组件之间的桥梁，主要是主权Rollup，可能面临与跨链桥梁相同的不安全性。最后，一个主要问题是在模块化堆栈的基础之上进行开发会增加复杂性；对于某些开发人员来说，这可能是一个挑战。最终，我们希望 rollup 能解决这些问题并实现充分的去中心化。然而，与此同时，单体的第 1 层也可能变得同样去中心化。 我们之前的报告讨论了一些单体式第 1 层如何使用DAG 架构在内部进行扩展。这只是一个例子，表明单体区块链正在尝试在不依赖链下组件的情况下进行创新，并且正在进行无数其他优化以最大限度地提高性能。我们不能简单地诋毁旨在解决可扩展性三角难题的新区块链设计的想法。 结论 就像以前有模块化手机一样，现在也有模块化区块链。然而，看到基于 Danksharding 的以Rollup为中心的未来的潜力表明，模块化区块链架构不太可能遭受与模块化手机相同的命运。Kindelia 和 Fuel 等执行层尤其会看到用户增长，因为它们对速度和新功能的关注将使构建在它们之上的应用程序真正具有创新性。 不幸的是，其中许多模块化设计仍未经过测试，一些模块化区块链设计可能永远不会得到广泛采用。随着 Celestia 和 Danksharding 被广泛采用，Validiums 可能会被完全淘汰。Celestia 的 主权 rollups 可能面临一些与现有第 1 层相同的桥接问题，由于安全和复杂性问题而阻碍采用。 一个去中心化、模块化的区块链未来仍然是一个漫长的过程。在此期间，单体区块链将继续发挥作用并不断创新。当我们最终达到模块化区块链被广泛采用的未来时，单体区块链的格局可能也会完全不同。尽管如此，我们需要扩容解决方案来为现有区块链提供流动性和用户服务，从长远来看，模块化区块链架构可能是实现这一目标的最佳方式。 转载自：https://learnblockchain.cn/article/5413
技术背景 很多时候，我们需要把一份收入（金额）拆分到不同的地址，而又不想让付款人（通常是用户）承担拆分收入部分的 GAS费用， 0xSplits 的架构及解决思路值得我们参考。 本文先介绍了0xSplits 是什么，0xSplits 采用的架构是怎么样的，同时介绍了几种其他的拆分方式可能带来的问题，另外还有一些0xSplits在 Gas 优化上的做法。 0xSplits是什么 0xSplits 是链上收入拆分的协议， 它是一个智能合约，允许你将任何付款拆分到许多收款人，例如，它可以用于NFT版税。EIP-2918允许你只指定一个地址作为收款人，但如果你把这个地址设置为0xSplits地址，你就可以有很多个收款人。 0xSplits由一个主合约组成，可以创建许多子克隆合约（SplitWallet）。 你通过调用主合约创建一个新的拆分（split） 它创建一个负责接收ETH的子合约 在需要接收ETH的地方使用子合约的地址 译者注：克隆合约是使用同一份代码，创建（复制）多个出合约， 每个合约都有自己的状态。 这里是子克隆合约的代码，你可以看到，它非常简单。它可以接收ETH，并且有一个函数可以将ETH转移到主合约， 0xSplits也支持ERC-20，但省略了这部分代码 但收到的ETH如何拆分给接收者？这就是主合约的任务（为了便于阅读，代码做了修改）： 所以，资金是静止不动的，直到有人明确地调用 distributeETH。但为什么有人会浪费Gas来调用这个函数呢？答案是distributorFee（分配费用）。这是在创建拆分时设置的，谁调用distributionETH就归谁。 这种激励性的代码调用是必要的，因为EVM没有周期任务（cron ）或回调来定期分配资金，一切都需要由个人或链外机器人发起，distributorFee有点类似于CryptoKitties合约中的autoBirthFee。 为什么0xSplits会选择这种架构？还有，为什么不在收到资金的时候立即分发？这是我们这篇文章的主要想解决的问题。 0xSplits 架构 你可以在Etherscan找到0xSplits的代码 克隆SplitWallet合约接收付款并持有资金，直到它们被调用，将资金转移到主合约。 分销商（distributor）被激励在主合约中调用distributeEth，这将把资金从克隆合约转移到主合约中，同时也将支付/分成。 但是在这一点上，资金仍然存放在主合约中（合约有一个从收款人地址到余额的映射）。为了实际接收资金，用户必须调用主合约中的withdraw函数。 此架构的可能替代方案 现有的设定是相当复杂的：克隆合约接收付款，分销商（distributor）将这些资金从克隆合约转移到主合约，并将资金分给接收者。但是收款人仍然需要调用withdraw来接收实际拆分到的资金。所以，端到端的用户流程并不是完全自动化的（另外，有些钱会被分销商收取）。 为什么0xSplits需要如此复杂的设置？让我们来看看其他的方法，以及它们为什么不可行。 若在收到资金时进行拆分？ 为什么不在收到资金的同一函数中进行拆分呢？也就是说，不在合约中保存资金， 而是立即分配，这里有2个原因： 付款人需要提供额外的Gas 它破坏了互操作性。付款人不能再直接将ETH转账（transfer）到克隆合约中。而是要求付款人将不得不调用合约上的一个特定函数（来进行拆分），这会破坏互操作性。你可能会说只要重写receive函数，但receive函数经常受2300个Gas限制，这意味着你不能在其函数中进行大量的转账。 若使用共享合约而不是克隆合约呢？ 为什么没有一个共享的合约，为所有可能的拆分接收所有的付款？这将摆脱克隆（创建新合约），但它并不奏效，因为当共享合约收到付款时，它不知道付款是要进行怎样的拆分。 将distributeEth和withdraw合二为一呢？ 为什么不把整个过程端到端自动化，让分销商在一个函数中执行这2个操作？主要是因为安全问题。强烈建议使用pull over push - 即让用户手动拉取他们的自己资金，而不是把资金自动推送他们。 为什么 "推（push）"被认为是一种安全风险？阅读下面的链接以获得完整的答案，但简而言之，这是因为不是所有的接收者都能保证正确处理ETH的接收。一些恶意的行为者可能会部署一个智能合约，在 receive 功能中进行回退(revert)。因此，如果至少有一个接收者回退(revert)，整个操作就会回退(revert)。 参考阅读文章：Pull over Push 将转移以太坊的相关风险转移到用户身上。 如果你想出另一种替代架构，请告诉我（在评论中这里）。 我认为0xSplits为他们所针对的使用场景选择了正确的架构--这就是成为DeFi生态系统中的一个基本构件。还有其他类似的支付拆分器，它们是针对不同的使用场景，它们使用不同的架构。例如，disperse.app在收到资金时进行拆分，它可能是一个更好的一次性付款拆分工具。 0xSplits 代码 我对合约进行了重组，并将所有内容按功能分组，以方便阅读。 现在我们来看看现有的架构是如何在代码中实现的。 这里只有2个合约：SplitMain.sol和SplitWallet.sol（克隆）。其余的是库和接口。 你已经在前面看到了SplitWallet.sol的代码，但我把再次复制到这里只是为了参考。 这很简单。它可以接收ETH并将资金转账到主合约。你可能会问，如果没有 receive 功能，它如何接收ETH？答案是，"克隆 "库创建该合约的克隆，神奇地插入 receive 功能的汇编代码。 现在到了主合约 - SplitMain.sol, 这是所有行动发生的地方。 创建一个拆分合约 SplitMain.sol合约开头的函数，用于创建新的拆分。 validSplit只是验证一些东西，如： 百分比的总和应该是1。 收款人和百分比数组具有相同的长度。 收款人地址数组需要被排序。为什么？我们很快就会知道。 如果controller是零地址，这意味着拆分没有所有者，它变得不可改变。Clones库将创建一个SplitWallet合约的克隆，并保存在构造函数中。 clone和cloneDeterministic（在上面的createSplit函数中）的区别是，确定性的部署到一个预先定义的地址（由传入的splitHash决定）。不可变（Immutable）的拆分合约使用确定性的克隆，以避免有人创建完全相同的拆分合约时时发生碰撞。 拆分（split）是由这些数据结构表示的： 使用哈希值的Gas优化 请注意，上面只保存了拆分的哈希值，而没有保存地址、收款人和distributorFee。为什么我们需要哈希值？ 哈希值是用来将所有关于拆分的信息（收款人、百分比、distributorFee）合成一个字符串。 通过只存储哈希值而不是函数的所有参数，我们节省了大量的存储空间，从而节省了Gas。 但是，我们如何查找在哈希过程中丢失的信息，如recipients？我们要求这些信息被传递到需要它们的函数中。在合约中，我们只需再次对传入的参数进行散列，并与存储的散列进行比较。如果它们匹配，传入的参数就是正确的。 分销商被激励在链外记录recipients, percentages等，并将这些信息传递给所有需要它们的函数。分销商费用则是支付他们的服务的费用。 另外，我们现在明白为什么收款人地址数组需要在createSplit函数中进行排序。因为否则，哈希值将无法重现。 更新拆分的内容 用哈希值更新拆分也变得非常有效。只要更新哈希值即可。 (onlySplitController确保msg.sender == split.controller) 所有权的转移 如果一个拆分是可变的，你可以转移其所有权。 这是有两步过程： 为什么是两步程序？为了防止意外转移到一个错误的地址。两步程序使你的合约更安全一些（代价是略微多一点Gas）。 资金如何拆分分配 资金是如何分配的？让我们来看看： 我们首先通过哈希算法验证传入的args，并与存储的哈希算法进行比较。然后我们将资金转移到主合约，为分销商预留奖励，最后分配资金。 这个函数为了可读性做了大量的修改。请阅读原始的源代码以了解实际的实现。 终端用户提取资金 主合约的最后一个功能是收款人提取资金的能力。这是一个非常简单的功能。 由于ethBalances中的资金来于所有的克隆拆分合约。但是withdraw需要手动调用，机器人/分销商没有受到激励进行这个动作。 同样有趣的是，有人可以代表你调用withdraw（为你的Gas付钱）。 其他的想法 0xSplits 实际上允许你有嵌套的拆分--指定一个拆分为另一个拆分的收款人。 0xSplits 也适用于 ERC-20 资金的拆分。我只是省略了代码以方便阅读。 不小心发送到主合约的资金是无法恢复的，因为没有办法提取多余的ETH。 OpenZeppelin也有一个支付拆分器，但我还没有研究过它是如何实现的，还有disperse.app，对于一次性的拆分来说可能更好。 用比特币而不是用以太坊来拆分支付要容易得多。由于UTXO架构的存在，比特币几乎拥有这种开箱即用的功能。(见 Solidity Fridays) 就这样吧!让我知道你对0xSplits的看法，在评论区。 转载：https://learnblockchain.cn/article/5560
本文我们来尝试进行 RareSkills Gas 优化的一个挑战，完成这个挑战可以学习到更多 Gas 技巧。 以下是 Distribute 合约，合约的作用是向 4 个贡献者平均分配 1 ETH， 代码如下 // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.15; contract Distribute { address[4] public contributors; uint256 public createTime; constructor(address[4] memory _contributors) payable { contributors = _contributors; createTime = block.timestamp; } function distribute() external { require( block.timestamp > createTime + 1 weeks, "cannot distribute yet" ); uint256 amount = address(this).balance / 4; payable(contributors[0]).transfer(amount); payable(contributors[1]).transfer(amount); payable(contributors[2]).transfer(amount); payable(contributors[3]).transfer(amount); } } 合约代码：https://github.com/RareSkills/gas-puzzles/blob/main/contracts/Distribute.sol 这个合约默认情况下，所需 GAS 是 71953， 需要优化到 57044， 以便通过测试用例，测试用例代码在这里。 这是默认运行的截图： 在原始合约上运行测试结果 挑战的要求是仅修改合约Solidity代码来使 distribute 下降到57044， 因此不可以修改优化器或solidity版本、不可以修改测试文件，也不使用huff/yul/bytecode 层面的优化，也不变更函数属性（不能使函数为 "payable"）。 在进一步阅读之前，请确保你自己先尝试解决这个难题，它真的很有趣，而且能让你学到东西。 让我们来优化吧! 让我们先做一些明显的事情。 变更变量为不可变（immutable）变量，而不是存储变量（节省 10710Gas--因为不可变变量是作为常量嵌入合约字节码的，而不是从存储中读取）。 用 send 取代 transfer（节省108 Gas--因为send没有检查转账是否成功）。 用endTime代替currentTime (节省了69个Gas - 在构造函数中进行时间计算) 以下是修改后的合约： 这样我们就可以让Gas 节约到61066，已经比原来的好了10887，但仍比目标值高出 4千， 需继续努力。 那么，还有什么诀窍呢？ 这个挑战有一个特别的技巧，应该教给你。而这是一种通过SELFDESTRUCT向地址发送ETH的老方法： 通过Selfdestruct 解谜, 仅仅这一招就为你节省了4066*的Gas，并且达到了目标! 但如果继续深入呢？ 每一个理智的优化总是有其疯狂的邪恶兄弟（作弊）......让我们看看我们能把这个推到什么程度! 让合约自毁（selfdestruct）感觉像是在作弊 -- 为什么你会有一个这样的合约，在第一次调用分发后就不存在了呢？不过测试通过，所以我想这是允许的......？ 让我们看看还有什么（真正的作弊）能让测试通过，但又能优化更多Gas。 让contributors成为常数，而不是不可变（immutable）（节省24个Gas - 因为Hardhat地址总是相同的，所以可以这样做，对吧？ 使金额恒定为0.25ETH，而不是从余额中计算（节省了106个Gas -- 因为在测试中金额总是相同的，所以为什么不这样做呢？） 使用Assembly来做call，而不是通常的solidityaddress.send（节省9 0 Gas）。 到目前为止使用的 Gas 是 56780 还可以更进一步吗？ 把地址作为字节32的数字直接放在调用中，可以为我们多节省9个Gas! 在调用中的手动用零填充地址， 可以节约到 56771 Gas 我们继续走下去如何？ 既然我们已经针对测试上做优化，那我们就进一步 为什么我们不直接返回，如果测试是 "Gas目标"？ 知道Hardhat总是在相同的区块链中运行测试，我们可以把这个代码放在 distribute()函数的开头： if (block.number == 5) return; 这样在测试中，测量 gas 时，直接返回，而不会消耗很多Gas，但仍将在 "逻辑检查" 测试中完成了的功能检查:) 不明白的同学可以回顾这里的测试用例代码，测试用例会先检查 Gas 是否达标再检查是否"逻辑"满足。 这给了我们一个惊人的21207Gas! 令人难以置信吧，不过你明白这里发生了什么...... 但，这就是作弊! 是的，但谨记，这样的作弊经常在链上发生，MEV，漏洞，代码即法律，以及所有其他的东西。 还记得最近的Gas挑战比赛吗，最佳优化者获得了NFT？在那里，没有人在解决原来的问题--一切都在 "欺骗 "智能合约，以接受所需的值，并通过所需的测试，以最低的字节码和最低的Gas--你就能得到NFT。 这也给我们了一些启示，更好的测试和更好的条件可以规避这些作弊，同时让程序更安全。 对于开发我们更应该注重有效的程序，而不仅仅是巧妙地入侵系统。 转载自：https://learnblockchain.cn/article/5515