您正在查看: Surou 发布的文章

Polygon zkEVM架构组成-思路梳理

Polygon zkEVM架构组成-思路梳理

  • zkNode

    • Synchronizer
      • 获取Sequencers提交的transactions
      • 获取Aggregators提交的validity proofs
      • 监听以太坊链上事件,包括new batches{events中读取的信息->存储database}
      • 处理可能的reorgs {检查last ethBlockNum 和 last ethBlockHash 是否已同步}
    • State子模块
      • 实现了Merkle Tree并连接到DB后台
        • 在block level检查integrity(即,关于gas、block size等相关信息)
        • 检查某些交易相关信息(如签名、足够的balance等)
        • 将smart contract(SC)代码存储到Merkle tree中,并使用EVM来处理交易
    • Sequencer
      • 支持模式:Trusted/Permissionless
      • 处理流程:接收L2交易+L2用户支付的交易手续费 -> 对L2交易进行排序 ->生成batches -> send tx{sequences形式}(为Aggregator验证支付费用) -> L1合约 {PolygonZkEVM.sol} -> 存储storage slots
      • 经济模型:向L1提交有效交易,以获利
        • 根据利润对pool中交易排序
        • 向L1提交batches,支付L1系统代币
    • Aggregator
      • 处理流程:L1合约 {PolygonZkEVM.sol} -> 获取Sequencer提交的L2 batches -> zkProver{特殊链下EVM解析器} -> 生成ZK proof{证明该batch的完整性}-> send tx -> L1合约 {PolygonZkEVM.sol} -> 更新 L2 State root
      • 经济模型:Sequencers提交L1 batch时支付的手续费
      • 开支
        • 向L1提交validity proof交易成本
        • 运行aggregator和zkProver服务器成本
  • Consensus Contract(PolygonZkEVM.sol)

    • 存储Sequencers提交的L2 batches
    • 对Aggregator新提交的L2 State root进行validity proof验证
  • zkProver

    • 本质:zk virtual machine来仿真EVM
    • 功能:为Aggregator使用,来验证batches并提供validity proofs
    • 生成proof:以多项式和汇编语言形式
    • 功能包含如下
      • Main State Machine Executor:处理zkEVM的执行
        • 使用zkASM解析EVM Bytecodes
        • 为每个transaction batch设置多项式约束
        • 对多项式约束使用PIL进行编码
      • secondary State Machines : zkEVM中证明交易正确性的每一步计算
        • zkProver是整个项目中最复杂的部分,包含了多个state machines:
          • 一些执行bitwise function的state machines(如XOR/Padding等等)
          • 执行哈希运算的的state machines(如Keccak、Poseidon等等)
          • 执行验签的state machines(如ECDSA等等)
        • 二级状态机有:
          • Binary SM
          • Memory SM
          • Storage SM
          • Poseidon SM
          • Keccak SM
          • Arithmetic SM
      • STARK-proof builder
        • State machines负责生成多项式约束,zk-STARKs用于证明batches满足这些多项式约束条件
        • zkProver使用FRI对zk-STARK证明加速
      • SNARK-proof builder
        • size: STARK-proof > SNARK-proof
          • SNARK-proof来证明STARK-proof的正确性
          • SNARK-proof做为validity proof
          • 更便宜地在L1上验证该validity proof
  • L1-to-L2 Bridge

    • L1 Contract:部署在以太坊,管理rollups之间的资产转移

      负责2个操作

      • bridge:将资产由一个rollup转移到另一个rollup

      • claim:当合约从任意rollup claim时,使用claim操作

      需要有2棵Merkle tree

      • globalExitTree:包括了所有rollups的exit trees的所有信息

      • mainnet exit tree:包含了用户与主网交互的交易信息

      global exit root manager L1的合约负责管理跨越多个网络的exit roots

    • L2 Contract:部署在某特定的rollup上,负责主网与该rollup之间的资产转移

      • 处理rollup端的bridge和claim操作
      • 与globalExitTree和rollup exit tree交互以更新exit roots

共识算法PoE

Hermez 1.0 PoD共识缺点

  • 特定时间,网络由单一actor控制,可能作弊
  • 竞价协议,预测复杂性高
  • 参与竞争运营门槛高,导致运营集中,抗审查

Proof-of-Efficiency(PoE)实现

  • Sequencer:负责将L2的交易打包为batches并添加到L1的PoE智能合约
  • Aggregator:之间竞争,负责检查transaction batches的有效性,并提供validity proofs

PoE智能合约基本接口

  • sendBatch:用于接收Sequencer的batches
  • validateBatch:用于接收Aggregator的validity proof,进行validate batches

batch fee

根据网络负载情况,batch fee将是可变的,可根据protocol合约中的某个参数来计算

PoE的经济模型

  • Sequencer赚L2的交易手续费{相应的validity proof提交后}
  • Aggregator赚取Sequencer支付的batch MATIC手续费

Polygon zkEVM-Hermez 2.0简介

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上部署一个动态NFT


使用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(
            "<svg width=\"256\" height=\"256\" viewBox=\"0 0 256 256\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">",
            "<style xmlns=\"http://www.w3.org/2000/svg\">@keyframes rainbow-background { 0% { fill: #ff0000; } 8.333% { fill: #ff8000; } 16.667% { fill: #ffff00; } 25.000% { fill: #80ff00; } 33.333% { fill: #00ff00; } 41.667% { fill: #00ff80; } 50.000% { fill: #00ffff; } 58.333% { fill: #0080ff; } 66.667% { fill: #0000ff; } 75.000% { fill: #8000ff; } 83.333% { fill: #ff00ff; } 91.667% { fill: #ff0080; } 100.00% { fill: #ff0000; }} #background { animation: rainbow-background 5s infinite; } #text { font-family: \"Helvetica\", \"Arial\", sans-serif; font-weight: bold; font-size: 72px; }</style>",
            "<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>",
            "</svg>"
        );

        // 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

Goerli 与zkEVM Testnet跨链测试

使用Polygon zkEVM Bridge 站点,将网络添加到我们的钱包,并将 Goerli Token 桥接到 zkEVM Testnet:

Goerli Token转0.01 ETH到zkEVM Testnet

交易:0x10210572d6b4924af7ef946136295e9b209e1fa0 -> 0xf6beeebb578e214ca9e23b0e9683454ff88ed2a7
tx hash: https://goerli.etherscan.io/tx/0x07aa6363c5854180b41b8c4584314ca6d6af989d48332375f06f9caef3270dd7

执行合约

bridgeAsset(uint32 destinationNetwork,address destinationAddress,uint256 amount,address token,bool forceUpdateGlobalExitRoot,bytes permitData)

执行参数

Name Type Data
0 destinationNetwork uint32 1
1 destinationAddress address 0x10210572d6b4924Af7Ef946136295e9b209E1FA0
2 amount uint256 10000000000000000
3 token address 0x0000000000000000000000000000000000000000
4 forceUpdateGlobalExitRoot bool true
5 permitData bytes

0xF6BEEeBB578e214CA9E23B0e9683454Ff88Ed2A7为代理合约,使用EIP-1967 Transparent Proxy
逻辑合约地址:0x39e780D8800f7396e8B7530A8925B14025AedC77
关键逻辑合约代码:https://goerli.etherscan.io/address/0x39e780d8800f7396e8b7530a8925b14025aedc77#code

合约bridgeAsset接口源码

/**
     * @notice Deposit add a new leaf to the merkle tree
     * @param destinationNetwork Network destination
     * @param destinationAddress Address destination
     * @param amount Amount of tokens
     * @param token Token address, 0 address is reserved for ether
     * @param forceUpdateGlobalExitRoot Indicates if the new global exit root is updated or not
     * @param permitData Raw data of the call `permit` of the token
     */
    function bridgeAsset(
        uint32 destinationNetwork,
        address destinationAddress,
        uint256 amount,
        address token,
        bool forceUpdateGlobalExitRoot,
        bytes calldata permitData
    ) public payable virtual ifNotEmergencyState nonReentrant {
        if (
            destinationNetwork == networkID ||
            destinationNetwork >= _CURRENT_SUPPORTED_NETWORKS
        ) {
            revert DestinationNetworkInvalid();
        }

        address originTokenAddress;
        uint32 originNetwork;
        bytes memory metadata;
        uint256 leafAmount = amount;

        if (token == address(0)) {
            // Ether transfer
            if (msg.value != amount) {
                revert AmountDoesNotMatchMsgValue();
            }

            // Ether is treated as ether from mainnet
            originNetwork = _MAINNET_NETWORK_ID;
        } else {
            // Check msg.value is 0 if tokens are bridged
            if (msg.value != 0) {
                revert MsgValueNotZero();
            }

            TokenInformation memory tokenInfo = wrappedTokenToTokenInfo[token];

            if (tokenInfo.originTokenAddress != address(0)) {
                // The token is a wrapped token from another network

                // Burn tokens
                TokenWrapped(token).burn(msg.sender, amount);

                originTokenAddress = tokenInfo.originTokenAddress;
                originNetwork = tokenInfo.originNetwork;
            } else {
                // Use permit if any
                if (permitData.length != 0) {
                    _permit(token, amount, permitData);
                }

                // In order to support fee tokens check the amount received, not the transferred
                uint256 balanceBefore = IERC20Upgradeable(token).balanceOf(
                    address(this)
                );
                IERC20Upgradeable(token).safeTransferFrom(
                    msg.sender,
                    address(this),
                    amount
                );
                uint256 balanceAfter = IERC20Upgradeable(token).balanceOf(
                    address(this)
                );

                // Override leafAmount with the received amount
                leafAmount = balanceAfter - balanceBefore;

                originTokenAddress = token;
                originNetwork = networkID;

                // Encode metadata
                metadata = abi.encode(
                    _safeName(token),
                    _safeSymbol(token),
                    _safeDecimals(token)
                );
            }
        }

        emit BridgeEvent(
            _LEAF_TYPE_ASSET,
            originNetwork,
            originTokenAddress,
            destinationNetwork,
            destinationAddress,
            leafAmount,
            metadata,
            uint32(depositCount)
        );

        _deposit(
            getLeafValue(
                _LEAF_TYPE_ASSET,
                originNetwork,
                originTokenAddress,
                destinationNetwork,
                destinationAddress,
                leafAmount,
                keccak256(metadata)
            )
        );
        // Update the new root to the global exit root manager if set by the user
        if (forceUpdateGlobalExitRoot) {
            _updateGlobalExitRoot();
        }
    }

// TODO 代码分析

等待zkevm-test处理完成

zkevm-test处理完成

Bridge Details

zkevm-test 对应交易

交易 0xa49d20f2f5a26d4a8e6fe44409f862d744f5b1aa -> 0xf6beeebb578e214ca9e23b0e9683454ff88ed2a7
tx hash: https://testnet-zkevm.polygonscan.com/tx/0x830ac94e69eb9e3aacd384d4db8a46e7830b8f99b12b5c6b5176bd8326fddc4b

执行方法

claimAsset(bytes32[32] smtProof,uint32 index,bytes32 mainnetExitRoot,bytes32 rollupExitRoot,uint32 originNetwork,address originTokenAddress,uint32 destinationNetwork,address destinationAddress,uint256 amount,bytes metadata)

执行参数

# Name Type Data
0 smtProof bytes32[32] 0x0000000000000000000000000000000000000000000000000000000000000000 0x5c4dafd55279a81649518c48d62f1e9105c9e7f5a8933aa2fce126d207755c14 0x890afe4ec8996c6ddd3977287b067f0f67dd25cd11cda85b22a93deac5675cdd 0xb6b78f0847e6ecdfc18ee568a1349fae5f336bdce9716be04db241b68b8dd60b 0x18dd01806c4a8a02ac0303f33fe609a944d81197cb52e8b8e159fbf9e45f12ed 0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d 0x4ac259557eb77da7cf4309feb02d02d0884d94e56bbf71f539bb3871e5c5f0db 0xffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83 0x08f9cb503a809cc64017397ec880786e6713c3ba6a85c181d02ae7536e9b58e3 0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0 0xf9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5 0xa647f1787afe7ec81c77a49649ba32057b0550470d3c9318dda05fcb8168d914 0xe1f88e3362870dcdb9c43a352e2b031a0c71080b78cc2a5732c3a5c1c0c8e3b4 0xc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb 0x5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8becc 0xda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d2 0x645b9978ad76c57db977fe48efb0b43110e3ebe78f9badd9047aac6bcd8d66a5 0xe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a 0x5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0 0xb46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0 0xc65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2 0xf4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd9 0x5a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e377 0x4df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652 0xcdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef 0x0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618d 0xb8cd74046ff337f0a7bf2c8e03e10f642c1886798d71806ab1e888d9e5ee87d0 0x838c5655cb21c6cb83313b5a631175dff4963772cce9108188b34ac87c81c41e 0x662ee4dd2dd7b2bc707961b1e646c4047669dcb6584f0d8d770daf5d7e7deb2e 0x388ab20e2573d171a88108e79d820e98f26c0b84aa8b2f4aa4968dbb818ea322 0x93237c50ba75ee485f4c22adf2f741400bdf8d6a9cc7df7ecae576221665d735 0x8448818bb4ae4562849e949e17ac16e0be16688e156b5cf15e098c627c0056a9
1 index uint32 72030
2 mainnetExitRoot bytes32 0x8ac4bfc9b4381015f4d41ca9d127a0a4065827d9c61ae290ffbf401ae446a97a
3 rollupExitRoot bytes32 0xd226edaccb2a0c0dc760d2a670a84862a271af7916e0a289b286f61435378411
4 originNetwork uint32 0
5 originTokenAddress address 0x0000000000000000000000000000000000000000
6 destinationNetwork uint32 1

合约地址0xf6beeebb578e214ca9e23b0e9683454ff88ed2a7 为 EIP-1967 Transparent Proxy代理合约,代理的逻辑合约地址为0x39e780d8800f7396e8b7530a8925b14025aedc77
合约源码:https://testnet-zkevm.polygonscan.com/address/0x39e780d8800f7396e8b7530a8925b14025aedc77#code

/**
     * @notice Verify merkle proof and withdraw tokens/ether
     * @param smtProof Smt proof
     * @param index Index of the leaf
     * @param mainnetExitRoot Mainnet exit root
     * @param rollupExitRoot Rollup exit root
     * @param originNetwork Origin network
     * @param originTokenAddress  Origin token address, 0 address is reserved for ether
     * @param destinationNetwork Network destination
     * @param destinationAddress Address destination
     * @param amount Amount of tokens
     * @param metadata Abi encoded metadata if any, empty otherwise
     */
    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
    ) external ifNotEmergencyState {
        // Verify leaf exist and it does not have been claimed
        _verifyLeaf(
            smtProof,
            index,
            mainnetExitRoot,
            rollupExitRoot,
            originNetwork,
            originTokenAddress,
            destinationNetwork,
            destinationAddress,
            amount,
            metadata,
            _LEAF_TYPE_ASSET
        );

        // Transfer funds
        if (originTokenAddress == address(0)) {
            // Transfer ether
            /* solhint-disable avoid-low-level-calls */
            (bool success, ) = destinationAddress.call{value: amount}(
                new bytes(0)
            );
            if (!success) {
                revert EtherTransferFailed();
            }
        } else {
            // Transfer tokens
            if (originNetwork == networkID) {
                // The token is an ERC20 from this network
                IERC20Upgradeable(originTokenAddress).safeTransfer(
                    destinationAddress,
                    amount
                );
            } else {
                // The tokens is not from this network
                // Create a wrapper for the token if not exist yet
                bytes32 tokenInfoHash = keccak256(
                    abi.encodePacked(originNetwork, originTokenAddress)
                );
                address wrappedToken = tokenInfoToWrappedToken[tokenInfoHash];

                if (wrappedToken == address(0)) {
                    // 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);

                    // Mint tokens for the destination address
                    newWrappedToken.mint(destinationAddress, amount);

                    // Create mappings
                    tokenInfoToWrappedToken[tokenInfoHash] = address(
                        newWrappedToken
                    );

                    wrappedTokenToTokenInfo[
                        address(newWrappedToken)
                    ] = TokenInformation(originNetwork, originTokenAddress);

                    emit NewWrappedToken(
                        originNetwork,
                        originTokenAddress,
                        address(newWrappedToken),
                        metadata
                    );
                } else {
                    // Use the existing wrapped erc20
                    TokenWrapped(wrappedToken).mint(destinationAddress, amount);
                }
            }
        }

        emit ClaimEvent(
            index,
            originNetwork,
            originTokenAddress,
            destinationAddress,
            amount
        );
    }

// TODO 代码分析

确认余额

https://testnet-zkevm.polygonscan.com/address/0x10210572d6b4924af7ef946136295e9b209e1fa0
// Note: Our ETH balance display is temporarily unavailable. Please check back later.
testnet-zkevm.polygonscan.com 浏览器余额出问题了,直接MetaMask查看

以正确到账

//TODO testnet-zkevm反向Goerli的转账跟踪

Silkworm - C++ 以太坊执行客户端

GitHub:https://github.com/torquem-ch/silkworm

引用于EOS EVM

Silkworm 架构设计

EOS EVM 的推出时间表曾发生过一些变化,而导致这一变化的原因之一是实施 Silkworm 作为 EOS EVM 的执行客户端,这是一个主要架构增强优化。 Silkworm 是 Ethereum 节点的 C++ 实现,符合 Erigon 的规范。 它被用于支持 RPC 并提高该领域的兼容性。

它的设计目标是成为最快的以太坊客户端,同时不牺牲代码的性能或可读性。 以下是 EtherWorld 的一篇文章,其中介绍了 Silkworm 强大功能的一些要点:

  • Silkworm 更容易理解,因为其代码库是新的,并且不包含任何主要的遗留功能。
  • 它在开发者社区保持中立客观。
  • Silkworm 采用 Apache-2.0 许可协议进行许可。 此许可证是 Permissive,这意味着它的限制最少,可以在大多数项目中使用。
  • Silkworm 使用 evmone 作为其 EVM 解释器,这是已知的最快、完全兼容的 EVM 实现。
  • Silkworm 使用 MDBX,它是最快的嵌入式键值存储,具有完全 ACID 交易。

// TODO 代码分析