对Optimism , OP Stack的技术做了简要描述。 主要内容来自Optimism的官方文档 (2023.9)
deposit和发交易: <!--StartFragment-->
<!--EndFragment-->
withdraw: <!--StartFragment-->
<!--EndFragment-->
OP主网安全模型: 目前OP主网的安全性依赖于多签,由几个匿名人士掌握着这些钱包。 在 OP 主网中,区块每两秒生成一次,即使没交易也会生成空区块。每个L2区块都归属于epoch,epoch是指L2区块归属于的L1块,这个L1块一般是几分钟前的某个L1块。epoch的编号等于L1上对应块的号。L2块一般由epoch和它在epoch中的序号标识。 epoch的第一个块中包含了所有从L1发往L2的交易(统称deposit)。如果Sequencer忽略了某个deposit交易,这也算是作恶,会被抓出来。 OP交易处理流程: <!--StartFragment-->
<!--EndFragment-->
交易压缩: 由op-batcher完成。它有两个功能: ● 将交易压缩成批次。 ● 将这些批次发布到 L1 当通道满了,或者超时时,就会把压缩交易发布到L1。 超时时间: 以L1的区块时间来计算,5个块,5*12=60s 压缩: 有两个参数可以控制压缩: ○ L1上的目标大小。--target-l1-tx-size-bytes ○ 预期的压缩比:--approx-compr-ratio 交易发布 当通道已满时,它会作为单个事务或多个事务(取决于数据大小)发布到 L1。 已处理的 L2 事务存在以下三种状态之一: ● 不安全:交易已被处理,但尚未写入 L1。某些情况可能会导致这些事务被丢弃。 ● 安全:交易已被处理并写入 L1。此时如果L1区块重组,还是有可能会被删除。 ● 最终确定:交易被写入 L1 ,且已经足够老以完成确定。 交易处理 交易处理分为两步:
<!--EndFragment-->
L1上的处理
参考:https://medium.com/taipei-ethereum-meetup/optimistic-rollup-%E7%9A%84%E6%8C%91%E6%88%B0%E6%A9%9F%E5%88%B6-%E4%B8%80-optimism-ovm-1-0-2b6a8e9d64cd 这部分是OP的核心,即如何在L1上完成欺诈证明挑战。 Optimism出过两个版本,V1和V2,分别看下。
核心思想是:交易是在L2运行的,状态根提交到了L1上。当有人发现提交到L1的状态根不对(某笔交易的执行结果不对)时,需要在L1上发起挑战。挑战就是在L1上把这笔交易重新执行一遍,看是谁错了,如果挑战成功,就得回滚。 所以这里的重点是:从L1重新部署L2上的合约,然后执行挑战的那笔交易,比较结果。 难点是:在L1上,Context都跟L2上执行时不一样,如何在L1上重现L2的执行环境+上下文?这里上下文包括如:block.time,block.height, msg.sender等,其中最关键的是:SLOAD,也就是执行中用到的状态。这些如何处理? OP修改了编译器,把这些相关的opcode的处理做了定制化。所以同一套solidity代码,在OP上部署时,需要用OVM compiler重新编译,里面所有跟Context相关的都变了。 以SLOAD为例,它会加载storage里的状态,实际上这个状态需要提前在L1上先用一笔交易填上去,然后下次执行挑战交易的时候,SLOAD执行的时候就能读出正确数据了。注意交易里所有SLOAD要用到的值全都得加载进去。 提前加载的交易是否能加载错误数据来影响挑战结果呢?不行。因为这个是有merkle证明的,要符合merkle证明的才能被加载进去。 使用这些被OP改过的opcode,就可以在L1上完美执行来自L2的交易,完成裁决。这种方法也叫EVM in EVM。 这种方案是有很多代价的: ● 需要修改编译器,所以有的工具在OP没法直接用,得重新编写才行。geth等很多东西都得改。 ● 合约大小限制。目前合约限制24kb左右,OP的编译器会导致编译结果变大约15%,所以实际合约大小限制是20.8kb左右。对于本就受此限制影响的合约来说,更难了。 ● 为了防止SLOAD需要在L1加载的数据过多,导致挑战消耗的gas过大,L2上一个交易一个块。
这个新的OVM方案叫做Cannon,文档:https://medium.com/ethereum-optimism/cannon-cannon-cannon-introducing-cannon-4ce0d9245a03 V1的缺点是:需要修改编译器,geth等,导致很多工具都得改,对开发者不友好,维护也很困难。而且一个交易一个块。 所以对以上重新思考后,推出了OVM V2,即Cannon。Cannon的主要价值是:可以在L2上运行跟L1上完全一样的EVM。即实现了EVM等效性。 OVM的出发点还是如何进行作恶挑战。这是个交互式的挑战过程。在V1里,挑战的是一个交易,在V2里,挑战的是一个opcode。因此首先需要考虑要解决哪些问题:
<!--EndFragment-->
这些在执行时都是确定性的。因此如果我们把一笔交易拆开细化来看,每一个opcode执行完之后,都会有个确定的状态,这个状态可以被提取出来,形成merkle树,用树根就可以表达这个状态。 然后,我们通过比较挑战者和守护者的这个状态树根,用二分法来查找哪个opcode出现分歧。这应该很快,但它是交互式的,所以。。。未必很快。
这里有几个问题:
<!--EndFragment-->
所以在L2上,是: go代码执行mini geth, mini geth里运行EVM, EVM上运行MIPS的合约,合约里模拟的是mini geth, 上面运行的是EVM。 在L1上,是: EVM上运行MIPS合约,合约里模拟mini geth,上面运行EVM。 这样,在L1和L2上,就统一起来了,从以前的EVM兼容,变成了EVM等效。这样也不必再改编译器那些东西了,L1和L2上运行的是一样的合约,因为这个合约是从相当于一个mini geth模拟器里执行的。 preImage Oracle 但上面的东西只是完成了执行层面的一致性,L1上挑战时,还是得加载L2上的状态值才行。这通过preImage Oracle完成。 <!--StartFragment-->
<!--EndFragment-->
以太坊里的状态存在一棵树里,其实底层是通过一个个key-value对存储的。 例如要取node4的值,就先拿到root,然后node1,然后node4,是个递归过程。minigeth通过不停询问preImage Oracle最终获得需要的值。这个方式在链上和链下都可以做。 对链上,需要挑战者和被挑战者先把这些需要的值load到oracle里,后续在执行中就会通过oracle获取。在链下时就是通过rpc服务获取。 另外,对一些区块context的oracle可以直接在L1的块头中获得。例如可以把L2的一个epoch的块,其context都设置为一个L1上的块,这样在执行这个epoch内的所有交易时,对Context的获取都可以直接从L1块头中,而不需要用oracle专门写入了。 扩展: 既然EVM能模拟MIPS,那也可以模拟其他指令集,所以L2上就可以执行非EVM的虚拟机了。
OP Stack 是一套为 Optimism 提供支持的软件——即当前 Optimism 主网使用的软件。它最终将以 Optimism 超级链及其治理的形式出现。 随着超级链概念的出现,对于 Optimism 来说,轻松创建可在超级链生态系统中互操作的新链变得越来越重要。因此,OP Stack 主要专注于创建一个共享的、高质量的、完全开源的系统,用于创建新的 L2 区块链。通过协调共享标准 随着Optimism的发展,OP 堆栈也会发展。尽管 OP Stack 目前的核心是运行 L2 区块链的基础设施,但 OP Stack 理论上可以扩展到底层区块链之上的层,包括区块浏览器、消息传递机制、治理系统等工具。 现状 Optimism Bedrock 是 OP Stack的当前迭代。Bedrock 提供了用于启动 Optimistic Rollup 的工具。 今天的 OP Stack是为了支持Optimism Superchain 构建的,这是一个拟议的 L2 网络,共享安全性、通信层和通用开发堆栈(OP Stack本身)。OP Stack 的 Bedrock 版本可以轻松启动 L2,该 L2 启动时将与超级链兼容。OP 堆栈是一个不断发展的概念。它随着Optimism的增长而增长。 架构 <!--StartFragment-->
<!--EndFragment-->
并不是所有东西都已经是生产状态。上图中的部分内容不是最新的。例如cannon已经被部署了。 Derivation Derivation层用来对DA层的数据进行解释,将其翻译为可以直接用标准以太坊API作为执行输入的参数。Derivation层一般与DA层紧密耦合,因为它们配合在一起工作。 结算层 结算层用于在外部链(如L1 以太坊)上建立对OP链的视图(view),从而了解当前OP链的状态情况。结算层对外部链来说是只读的,可以让外部链通过这个view来做出决定,如从L2到L1的提款请求。
一个去中心化的区块链平台,由许多共享安全性和技术堆栈(OP Stack)的链组成。互操作性和标准化使工具和钱包能够同等对待各个链。 超级链是一个 L2 链网络,其中每条链称为 OP 链,它们共享安全性、通信层和开源技术堆栈。然而,与多链设计不同,这些链是标准化的,旨在用作可互换的资源。这使得开发人员能够构建以整个超级链为目标的应用程序,并抽象出应用程序运行的底层链。
<!--StartFragment-->
<!--EndFragment-->
<!--StartFragment-->
<!--EndFragment-->
以下内容,需要逐一确认是否已经ready,可能已经好了,但是文档没更新。 将BedRock桥升级成为链工厂 BedRock在L1上部署合约用来定义L2链。包括链id,key定义,gas Limit等。 (这应该是待实现的)一旦链的定义数据在链上了,我们就可以创建一个工厂合约,为每条链来部署链配置,以及其他所需的合约。用CREATE2,就可以在有了链配置后,就把链桥的地址确定下来。这可以让链继承标准安全性。 使用链工厂导出OP Chain的数据 通过L1链,可以获得所有标准OP Chain的数据。OP节点应该可以在给定单个 L1 地址和到 L1 的连接的情况下确定性地同步任何OP 链。 当OP链同步时,链状态在本地计算。这意味着确定 OP 链的状态是完全无需许可且安全的。链推导不需要证明系统,因为所有无效交易都会被节点执行的本地计算过程忽略。然而,仍然需要一个证明系统来启用超级链withdraw。 用来withdraw的无需许可的证明系统 在Bedrock中,有一个permissioned的提款交易提交者,并且用户提款需要在L1上规定时间内提交特定交易。在未来,这将被修改。特定permissioned的提款交易提交者将被消除,任何人都可以提交提款申请。 每个链可配置的Sequencer 所有链共享升级路径 为了使初始超级链对安全性和去中心化充满信心,引入去中心化的安全委员会来管理升级。安全委员会应该能够更新链证明者集,延迟启动合约升级,并按下紧急桥暂停按钮,这也会取消待处理的升级。 在紧急情况下暂停桥梁的能力意味着,在最坏的情况下,即安理会参与者的私钥泄露的必要阈值,结果将是无限期暂停提款,桥梁升级将永久取消。换句话说,L1资金将被冻结。
组成:
● op-node ● op-geth ● op-batcher ● op-proposer ● contracts-bedrock ● fault-detector ● sdk ● chain-mon 数据可用性来自两个来源:
<!--StartFragment-->
<!--EndFragment-->
如果在L2想获得最近的L1的数据(也就是下次打包用的L1的Context,即这些L2交易被“认为打入”的L1的Context),可以用getter函数访问合约L1Block。是个预置合约,0x4200000000000000000000000000000000000015 ● number: The latest L1 block number known to L2 ● timestamp: The timestamp of the latest L1 block ● basefee: The base fee of the latest L1 block ● hash: The hash of the latest L1 block ● sequenceNumber: The number of the L2 block within the epoch (the epoch changes when there is a new L1 block 地址别名 由于CREATE操作码的特性,用户可以在L1和L2上创建相同地址的合约,这可能会引入作恶行为。因此对tx.origin和msg.sender的行为,在L1和L2上有一定差异。 <!--StartFragment-->
<!--EndFragment-->
即,如果是EOA调用的话,在L2上跟L1是一样的。但是如果是L1的合约调用的话(即用L1合约调用deposite),tx.origin是L1合约地址加一个固定字符串。 msg.sender在第一层调用时,总是等于tx.origin,所以也会受上面规定的影响。一般来说,不能用tx.origin作为授权的检测。 块生产: <!--StartFragment-->
<!--EndFragment-->
其他: ● JSON RPC:OP的JSON RPC除了完全支持以太坊部分外,还引入了一些针对OP的特定接口。 ● EIP-155:在EIP-155之前的交易不支持链id,这可能引入重放攻击,所以OP默认不支持EIP155之前的交易。 ● 交易成本:OP的执行成本包括在L2上的执行成本和L1的数据成本。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!