区块链中文技术社区 - 2024年6月
type: channel
link: https://www.bcskill.com/index.php/2024/06
description: 区块链,EOS,Ethereum,IPFS,Dapp技能学习
items: OPStack在EIP-4844中的升级
items: op-proposer介绍
items: opstack是如何从Layer1中派生出来Layer2的
items: batcher工作原理
items: optimism中的libp2p应用
items: optimism中区块的传递
items: Optimism Sequencer 工作流程
items: docker compose for Optimism
items: Optimism Gas Tracker
items: Docker automation for Ethereum nodes

OPStack在EIP-4844中的升级
type: item
date: 2024-06-25T10:28:05+08:00
description: OPStack在EIP-4844中的升级 Ethereum的EIP-4844对layer2来说是一次巨大的变革，它显著降低了layer2在使用L1作为DA（数据可用性）的费用。本文不详细解析EIP-4844的具体内容，只简要介绍，作为我们了解OP-Stack更新的背景。 EIP-4844 TL;DR: EIP-4844引入了一种称为“blob”的数据格式，这种格式的数据不参与EVM执行，而是存储在共识层，每个数据块的生命周期为4096个epoch（约18天）。blob存在于l1主网上，由新的type3 transaction携带，每个区块最多能容纳6个blob，每个transaction最多可以携带6个blob。 欲了解更多详情，请参考以下文章： Ethereum Evolved: Dencun Upgrade Part 5, EIP-4844 EIP-4844, Blobs, and Blob Gas: What you need to know Proto-Danksharding OP-Stack的应用 OP-Stack在采用BLOB替换之前的CALLDATA作为rollup的数据存储方式后，费率直线下降 在OP-Stack的此次更新中，主要的业务逻辑变更涉及将原先通过calldata发送的数据转换为blob格式，并通过blob类型的交易发送到L1。此外，还涉及到从L1获取发送到rollup的数据时对blob的解析，以下是参与此次升级的主要组件： submitter —— 负责将rollup数据发送到L1的组件 fetcher —— 将L1的数据（旧rollup数据/deposit交易等）同步到L2中 blob相关定义与实现 —— 如何获取和结构blob数据等内容等 其他相关设计部分 —— 如客户端支持blob类型交易的签名、与fault proof相关的设计等 ⚠️⚠️⚠️请注意，本文中所有涉及的代码均基于最初的PR设计，可能与实际生产环境中运行的代码存在差异。 Blob相关定义与编解码实现 Pull Request(8131) blob 定义 Pull Request(8767) encoding & decoding 定义blob BlobSize = 4096 * 32 type Blob [BlobSize]byte blob encoding official specs about blob encoding 需要注意的是，此specs对应的是最新版本的代码，而下方PR截取的代码则为最初的简化版本。主要区别在于：Blob类型被分为4096个字段元素，每个字段元素的最大大小受限于特定模的大小，即math.log2(BLS_MODULUS) = 254.8570894...，这意味着每个字段元素的大小不会超过254位，即31.75字节。最初的演示代码只使用了31字节，放弃了0.75字节的空间。而在最新版本的代码中，通过四个字段元素的联合作用，充分利用了每个字段元素的0.75字节空间，从而提高了数据的使用效率。 以下为Pull Request(8767)的部分截取代码 通过4096次循环，它读取总共31*4096字节的数据，这些数据随后被加入到blob中。 func (b *Blob) FromData(data Data) error { if len(data) > MaxBlobDataSize { return fmt.Errorf("data is too large for blob. len=%v", len(data)) } b.Clear() // encode 4-byte little-endian length value into topmost 4 bytes (out of 31) of first field // element binary.LittleEndian.PutUint32(b[1:5], uint32(len(data))) // encode first 27 bytes of input data into remaining bytes of first field element offset := copy(b[5:32], data) // encode (up to) 31 bytes of remaining input data at a time into the subsequent field element for i := 1; i < 4096; i++ { offset += copy(b[i*32+1:i*32+32], data[offset:]) if offset == len(data) { break } } if of…

op-proposer介绍
type: item
date: 2024-06-25T10:27:00+08:00
description: op-proposer介绍 在这一章节中，我们将探讨到底什么是op-proposer 首先分享下来自官方specs中的资源(source) 一句话概括性的描述proposer的作用：定期的将来自layer2上的状态根（state root）发送到layer1上，以便可以无需信任地直接在layer1层面上执行一些来自layer2的交易，如提款或者message通信。 在本章节中，将会以处理来自layer2的一笔layer1的提现交易为例子，讲解op-proposer在整个流程中的作用。 提现流程 在Optimism中，提现是从L2（如 OP Mainnet, OP Goerli）到L1（如 Ethereum mainnet, Goerli）的交易，可能附带或不附带资产。可以粗略的分为四笔交易： 用户在L2提交的提现发起交易； proposer将L2中的state root 通过发送交易的方式上传到L1当中，以供接下来步骤中用户中L1中使用 用户在L1提交的提现证明交易，基于Merkle Patricia Trie，证明提现的合法性； 错误挑战期过后，用户在L1提交的提现最终交易，实际运行L1交易，认领任何附加资产等； 具体详情可以查看官方对于这部分的描述(source) 什么是proposer proposer是服务于在L1中需要用到L2部分数据时的连通器，通过proposer将这一部分L2的数据（state root）发送到L1的合约当中。L1的合约就可以通过合约调用的方式直接使用了。 注意⚠️：很多人认为proposer发送的state root后才代表这些设计的区块是finalized。这种理解是错误的。safe的区块在L1中经过两个epoch（64个区块）后即可认定为finalized。 proposer是将finalized的区块数据上传，而不是上传后才finalized。 proposer和batcher的区别 在之前我们讲解了batcher部分，batcher也是负责把L2的数据发送到L1中。你可能会有疑问，batcher不都已经把数据搬运到L1当中了，为什么还需要一个proposer再进行一次搬运呢？ 区块状态不一致 batcher发送数据时，区块的状态还是unsafe状态，不能直接使用，且无法根据batcher的交易来判断区块的状态何时变成finalized状态。 proposer发送数据时，代表了相关区块已经达到了finalized阶段，可以最大程度的去相信并使用相关数据。 传递的数据格式和大小不同 batcher是将几乎完整的交易信息，包括gasprice，data等详细信息存储在layer1当中。 proposer只是将区块的state root发送到l1当中。state root后续配合merkle-tree的设计使用 batcher传递的数据是巨量，proposer是少量的。因此batcher的数据更适合放置在calldate中，便宜，但是不能直接被合约使用。proposer的数据存储在合约的storage当中，数据量少，成本不会很高，并且可以在合约交互中使用。 在以太坊中，数据存储calldata当中和存储在合约的storage当中的区别 在以太坊中，calldata和storage的区别主要有三方面： 持久性： storage：持久存储，数据永久保存。 calldata：临时存储，函数执行完毕后数据消失。 成本： storage：较贵，需永久存储数据。 calldata：较便宜，临时存储。 可访问性： storage：多个函数或事务中可访问。 calldata：仅当前函数执行期间可访问。 代码实现 在这部分我们会从代码层来进行深度的机制和实现原理的讲解 程序起点 op-proposer/proposer/l2_output_submitter.go 通过调用Start函数来启动loop循环，在loop的循环中，主要通过函数FetchNextOutputInfo负责查看下一个区块是否该发送proposal交易，如果需要发送，则直接调用sendTransaction函数发送到L1当作，如不需要发送，则进行下一次循环。 func (l *L2OutputSubmitter) loop() { defer l.wg.Done() ctx := l.ctx ticker := time.NewTicker(l.pollInterval) defer ticker.Stop() for { select { case <-ticker.C: output, shouldPropose, err := l.FetchNextOutputInfo(ctx) if err != nil { break } if !…

opstack是如何从Layer1中派生出来Layer2的
type: item
date: 2024-06-25T10:26:54+08:00
description: opstack是如何从Layer1中派生出来Layer2的 在阅读本文章之前，我强烈建议你先阅读一下来自optimism/specs中有关派生部分的介绍(source) 如果你看完这篇文章，感到迷茫，这是正常的。但是还是请记住这份感觉，因为在看完我们这篇文章的分析之后，请你回过来头再看一遍，你就会发现这篇官方的文章真的很凝练，把所有要点和细节都精炼的阐述了一遍。 接下来让我们进入文章正题。我们都知道layer2的运行节点，是可以从DA层（layer1）中获取数据，并且构建出完整的区块数据的。今天我们就来讲解一下这个过程中是如何在codebase中实现的。 你需要有的问题 如果现在让你设计这样一套系统，你会怎么设计呢？你会有哪些问题？在这里我列出来了一些问题，带着这些问题去思考会帮助你更好的理解整篇文章 当你启动一个新节点的时候，整个系统是如何运行的？ 你需要一个个去查询所有l1的区块数据吗？如何触发查询？ 当拿到l1区块的数据后，你需要哪些数据？ 派生过程中，区块的状态是怎么变化的？如何从unsafe变成safe再变成finalized？ 官方specs中晦涩的数据结构 batch/channel/frame 这些到底是干嘛的？（可以在上一章03-how-batcher-works章节中详细理解） 什么是派生（derivation）？ 在理解derivation前，我们先来聊一聊optimism的基本rollup机制，这里我们简单以一笔l2上的transfer交易为例。 当你在optimism网络上发出一笔转账交易，这笔交易会被"转发"给sequencer节点，由sequencer进行排序，然后进行区块的封装并进行区块的广播，这里可以理解为出块。我们把这个包含你交易的区块称为区块A。这时的区块A状态为unsafe。接下来等sequencer达到一定的时间间隔了（比如4分钟），会由sequencer中的batcher的模块把这四分钟内所有收集到的交易（包括你这笔转账交易）通过一笔交易发送到l1上，并由l1产出区块X。这时的区块A状态仍然为unsafe。当任何一个节点执行derivation部分的程序后，此节点从l1中获取区块X的数据，并对本地l2的unsafe区块A进行更新。这时的区块A状态为safe。在经过l1两个epoch（64个区块）后，由l2节点将区块A标记为finalized区块。 而派生就是把角色带入到上述例子的l2节点当中，通过不断的并行执行derivation程序将获取的unsafe区块逐步变成safe区块，同时把已经是safe的区块逐步变成finalized状态的一个过程。 代码层深潜 hoho 船长，让我们深潜

batcher工作原理
type: item
date: 2024-06-25T10:26:22+08:00
description: batcher工作原理 在这一章节中，我们将探讨到底什么是batcher ⚙️ 官方specs中有batcher的介绍(source) 在进行之前，我们先提出几个问题，通过这两个问题来真正理解batcher的作用以及工作原理 batcher是什么？它为什么叫做batcher batcher在代码中到底是怎么运行的？ 前置知识 在rollup机制中，要想做到的去中心化特性，例如抗审查等。我们必须要把layer2上发生的数据（transactions）全部发送到layer1当中。这样就可以在利用layer1的安全性的同时，又可以完全从layer1中构建出来整个layer2的数据，使得layer2才真正的具有有效性。 Epochs and the Sequencing Window:Epoch可以简单理解为L1新的一个区块（N+1）生成的这段时间。epoch的编号等于L1区块N的编号，在L1区块N -> N+1 这段时间内产生的所有L2区块都属于epoch N。在上个概念中我们提到必须上传L2的数据到L1中，那么我们应该在什么范围内上传数据才是有效的呢，Sequencing Window的size给了我们答案，即区块N/epoch N的相关数据，必须在L1的第N + size之前已经上传到L1了。 Batch/Batcher Transaction: Batch可以简单理解为每一个L2区块构建所需要的交易。Batcher Transaction为多个batch组合起来经过加工后发送到L1的那笔交易 Channe: channel可以简单理解为是batch的组合，组合是为了获得更好的压缩率，从而降低数据可用性成本，以使batcher上传的成本进一步降低。 Frame: frame可以理解为，有时候为了更好的压缩率，可能会导致channel数据过大而不能直接被batcher将整个channel发送给L1，因此需要对channel进行切割，分多次进行发送。 什么是batcher 在rollup中，需要一个角色来传递L2信息到L1当中，同时每当有新的交易就马上发送是昂贵且不方便管理的。这时候我们将需要制定一种合理的批量上传策略。因此，为了解决这个问题，batcher出现了。batcher是唯一存在（sequencer当前掌管私钥），且和特定地址发送Batcher Transaction来传递L2信息的组件。 batcher通过对unsafe区块数据进行收集，来获取多个batch，在这里每个区块都对应一个batch。当收集足够的batch进行高效压缩后生成channel，并以frame的形式发送到L1来完成L2的信息上传。 代码实现 在这部分我们会从代码层来进行深度的机制和实现原理的讲解 程序起点 op-batcher/batcher/driver.go 通过调用Start函数来启动loop循环，在loop的循环中，主要处理三件事 当定时器触发时，将所有新的还未加载的L2block加载进来，然后触发publishStateToL1函数向L1进行state发布 处理receipts，记录成功或者失败状态 处理关闭请求 func (l *BatchSubmitter) Start() error { l.log.Info("Starting Batch Submitter") l.mutex.Lock() defer l.mutex.Unlock() if l.running { return errors.New("batcher is already running") } l.running = true l.shutdownCtx, l.cancelShutdownCtx = context.WithCancel(context.Background()) l.killCtx, l.cancelKillCtx = context.WithCancel(context.Background()) l.state.Clear() l.lastStoredBlock = eth.BlockID{} l.wg.Add(1) go l.loop() l.log.Info("Batch Submitter started") return nil } func (l *BatchSubmitter) loop() { defer l.wg.Done() ticker := time.NewTicker(l.PollInterval) defer ticker.Stop() receiptsCh := make(chan txmgr.TxReceipt[txData]) queue := txmgr.NewQueue[txData](l.killCtx…

optimism中的libp2p应用
type: item
date: 2024-06-25T10:25:28+08:00
description: optimism中的libp2p应用 在本节中，主要用于讲解optimism是如何使用libp2p来完成op-node中的p2p网络建立的。 p2p网络主要是用于在不同的node中传递信息，例如sequencer完成unsafe的区块构建后，通过p2p的gossiphub的pub/sub进行传播。libp2p还处理了其他，例如网络，寻址等在p2p网络中的基础件层。 了解libp2p libp2p（简称来自“库对等”或“library peer-to-peer”）是一个面向对等（P2P）网络的框架，能够帮助开发P2P应用程序。它包含了一套协议、规范和库，使网络参与者（也称为“对等体”或“peers”）之间的P2P通信变得更为简便 (source)。libp2p最初是作为IPFS（InterPlanetary File System，星际文件系统）项目的一部分，后来它演变成了一个独立的项目，成为了分布式网络的模块化网络堆栈 (source)。 libp2p是IPFS社区的一个开源项目，欢迎广泛社区的贡献，包括帮助编写规范、编码实现以及创建示例和教程 (source)。libp2p是由多个构建模块组成的，每个模块都有非常明确、有文档记录且经过测试的接口，使得它们可组合、可替换，因此可升级 (source)。libp2p的模块化特性使得开发人员可以选择并使用仅对他们的应用程序必要的组件，从而在构建P2P网络应用程序时促进了灵活性和效率。 相关资源 libp2p的官方文档 libp2p的GitHub仓库 在ProtoSchool上的libp2p简介 libp2p的模块化架构和开源特性为开发强大、可扩展和灵活的P2P应用程序提供了良好的环境，使其成为分布式网络和网络应用程序开发领域的重要参与者。 libp2p实现方式 在使用libp2p时，你会需要实现和配置一些核心组件以构建你的P2P网络。以下是libp2p在应用中的一些主要实现方面： 1. 节点创建与配置: 创建和配置libp2p节点是最基本的步骤，这包括设置节点的网络地址、身份和其他基本参数。 关键使用代码:libp2p.New() 2. 传输协议: 选择和配置你的传输协议（例如TCP、WebSockets等）以确保节点之间的通信。 关键使用代码：tcpTransport := tcp.NewTCPTransport() 3. 多路复用和流控制: 实现多路复用来允许在单一的连接上处理多个并发的数据流。 实现流量控制来管理数据的传输速率和处理速率。 关键使用代码：yamuxTransport := yamux.New() 4. 安全和加密: 配置安全传输层以确保通信的安全性和隐私。 实现加密和身份验证机制以保护数据和验证通信方。 关键使用代码：tlsTransport := tls.New() 5. 协议和消息处理: 定义和实现自定义协议来处理特定的网络操作和消息交换。 处理接收到的消息并根据需要发送响应。 关键使用代码：host.SetStreamHandler("/my-protocol/1.0.0", myProtocolHandler) 6. 发现和路由: 实现节点发现机制来找到网络中的其他节点。 实现路由逻辑以确定如何将消息路由到网络中的正确节点。 关键使用代码：dht := kaddht.NewDHT(ctx, host, datastore.NewMapDatastore()) 7. 网络行为和策略: 定义和实现网络的行为和策略，例如连接管理、错误处理和重试逻辑。 关键使用代码：connManager := connmgr.NewConnManager(lowWater, highWater, gracePeriod) 8. 状态管理和存储: 管理节点和网络的状态，包括连接状态、节点列表和数据存储。 关键使用代码：peerstore := pstoremem.NewPeerstore() 9. 测试和调试: 为你的libp2p应用编写测试以确保其正确性和可靠性。 使用调试工具和日志来诊断和解决网络问题。 关键使用代码：logging.SetLogLevel("libp2p", "DEBUG") 10. 文档和社区支持: - 查阅libp2p的文档以了解其各种组件和API。 - 与libp2p社区交流以获取支持和解决问题。 } 以上是使用libp2p时需要考虑和实现的一些主要方面。每个项目的具体实现可能会有所不同，但这些基本方面是构建和运行libp2p应用所必需的。在实现这些功能时，可以参考libp2p的官方文档和GitHub仓库中的示例代码和教程。 在OP-node中libp2p的使用 为了弄清楚op-node和libp2p的关系，我们必须弄清楚几个问题 - 为什么选择l…

optimism中区块的传递
type: item
date: 2024-06-25T10:24:00+08:00
description: optimism中区块的传递 区块的传递是整个optimism rollup系统中较为重要的概念，在这一章节，我们将从介绍optimism中多种sync方式的原理，来揭开整个系统里区块的传递过程。 区块类型 在进行进一步深入前，让我们了解一些基本的概念。 Unsafe L2 Block (不安全的 L2 区块): 这是指 L1 链上最高的 L2 区块，其 L1 起源是规范 L1 链的 可能 扩展（如 op-node 所知）。这意味着，尽管该区块链接到 L1 链，但其完整性和正确性尚未得到充分验证。 Safe L2 Block (安全的 L2 区块): 这是指 L1 链上最高的 L2 区块，其 epoch 的序列窗口在规范的 L1 链中是完整的（如 op-node 所知）。这意味着该区块的所有前提条件都已在 L1 链上得到验证，因此它被认为是安全的。 Finalized L2 Block (定稿的 L2 区块): 这是指已知完全源自定稿 L1 区块数据的 L2 区块。这意味着该区块不仅安全，而且已根据 L1 链的数据完全确认，不会再发生更改。 sync类型 op-node p2p gossip 同步： op-node 通过 p2p gossip 协议接收最新的不安全区块，由 sequencer 推送的。 op-node 基于libp2p的请求-响应的逆向区块头同步： 通过此同步方式，op-node 可以填补不安全区块的任何缺口。 执行层（EL，又名 engine sync）同步： 在 op-node 中有两个标志，允许来自 gossip 的不安全区块触发引擎中向这些区块的长范围同步。相关的标志是 --l2.engine-sync 和 --l2.skip-sync-start-check（用于处理非常旧的安全区块）。然后，如果为此设置了 EL，它可以执行任何同步，例如 snap-sync（需要 op-geth p2p 连接等，并且需要从某些节点进行同步）。 op-node RPC 同步： 这是一种基于可信 RPC 方法的同步，当 L1 出现问题时，这种同步方式相对简单。 op-node p2p gossip 同步 这种同步的场景处于：当l2的块新产生的时候，即在上一节我们讨论的sequencer模式下是如何产生新的区块的。 当产生新的区块后，sequencer通过基于libp2p的P2P网络的pub/sub（广播/订阅）模块，向’新unsafe区块‘ topic 发出广播。所有订阅了此topic的节点都会直接或间接的收到这一广播消息。详情可以查看 op-node 基于libp2p的请求-响应的逆向区块头同步 这种同步的场景处于：当节点因为特殊情况，比如宕机后重新链接，可能会产生一些没有同步上的区块（gaps） 当这种情况出现的时候，可以通过p2p网络的反向链的方式快速同步，即通过使用libp2p原生的stream流来和其他p2p节点建立链接，同时发送同步请求。详情可以查看 执行层（EL，又名 engine sync）同步 这种同步的场景处于：当有较多区块，一个大范围区块需要同步的时候，从l1慢慢派生比较慢，想要快速同步。 使用--l2.engine-sync 和 --l2.skip-sync-start-check去启动op-node，发送的payload来达到发送长范围同步请求的目的。 代码层讲解 首先我们来看一下这两个标志的定义 在 op-node/flags/flags.go 中定义并解释了这两个flag的作用 L2EngineSyncEnabled Flag (l2.engine-sync): 该标志用于启用或禁用执行引擎的 P2P 同步功能。当设置为 true 时，它允许执行引擎通过 P2P 网络与其他节点同步区块数据。它的默认值是 false，意味着在默认情况下，该 P2P 同步功能是禁用的。 SkipSyncStartCheck Flag (l2.skip-sync-start-check): 该标志用于在确定同步起始点时，跳过对不安全 L2 区块的 L1 起源一致性的合理性检查。当设置为 true 时，它会推迟 L1 起源的验证。如果你正在使用 l2.engine-sync，建议启用此标志来跳过初始的一致性检查。它的默认值是 false，意味着在默认情况下，该合理性检查是启用的。 L2EngineSyncEnabled = &cli.BoolFlag{ Name: "l2.engine-sync", Usage: "Enables or disables execution engine P2P sync", EnvVars: prefixEnvVars("L2_ENGINE_SYNC_ENABLED"), Req…

Optimism Sequencer 工作流程
type: item
date: 2024-06-25T10:21:00+08:00
description: Sequencer 工作流程 Sequencer 在 Layer 2 (L2) 解决方案中起到核心作用，主要负责交易汇总，L1 数据派生，L2 区块生成，L1 batch 数据提交，以及 L1 中 L2 state root 的提议。在本文中，我们将深入探讨 Sequencer 的工作原理和相关代码实现。在这部分我们主要讨论L2区块的产生流程 L2 区块生成 在一个更宏观的层面，sequencer在L2 区块的生成过程中实际上只是创建一个只包含 deposit 的模板块的 payload 。该 payload 随后被发送给 Execution Layer (EL)，EL 从 txpool 中提取交易，然后进行 payload 包装以生成实际的区块。 代码流程 当操作节点（opnode）启动后，Driver 会启动一个 eventloop。在这个 eventloop 中，我们定义了 sequencerCh 通道和 planSequencerAction 方法。 sequencerTimer := time.NewTimer(0) var sequencerCh <-chan time.Time planSequencerAction := func() { delay := s.sequencer.PlanNextSequencerAction() sequencerCh = sequencerTimer.C if len(sequencerCh) > 0 { // 确保通道在重置前已被清空 <-sequencerCh } sequencerTimer.Reset(delay) } 在 planSequencerAction 方法中，我们重新设置了通道信号接收计时器的时间。而 PlanNextSequencerAction 方法则用于计算 RunNextSequencerAction 的延迟时间。 延迟时间解释 在这里，“延迟时间”是一个重要的概念。它决定了执行下一个序列化动作之前应该等待的时间。通过动态计算延迟时间，我们可以更灵活地控制序列化的频率和时机，从而优化系统的性能。 Event Loop 的循环结构 在 event loop 的 for 循环中，首先进行了一系列的检查。例如，我们检查是否启用了 sequencer 和 L1 状态是否已准备好，以确定是否可以触发下一个 sequencer 操作。 for { // 主条件：检查 Sequencer 是否启用和 L1 状态是否准备好 // 在这个 if 语句中，我们检查了几个关键条件来确定是否可以进行 sequencing，包括： // - Sequencer 是否启用 // - Sequencer 是否停止 // - L1 状态是否准备好 // - Derivation pipeline 的引擎是否准备好 if s.driverConfig.SequencerEnabled && !s.driverConfig.SequencerStopped && s.l1State.L1Head() != (eth.L1BlockRef{}) && s.derivation.EngineReady() { // 检查安全滞后 // 在这段代码中，我们监视安全和不安全的 L2 头之间的滞后，以确定是否需要暂停新区块的创建。 if s.driverConfig.SequencerMaxSafeLag > 0 && s.derivation.SafeL2Head().Number+s.driverConfig.SequencerMaxSafeLag <= s.derivation.UnsafeL2Head().Number { if sequencerCh != nil { s.log.Warn( "Delay creating new block since safe lag exceeds limit", "safe_l2", s.derivation.SafeL2Head(), "unsafe_l2", s.derivation.UnsafeL2Head(), ) sequencerCh = nil } // 更新 Sequencer 操作 // 如果 sequencer 正在构建一个新的区块，并且 L1 状态已准备好，我们将更新下一个 sequencer 动作的触发器。 } else if s.sequencer.BuildingOnto().ID() != s.derivation.UnsafeL2Head().ID() { planSequencerAction() } // 默认条件：在所有其他情况下，我们将 sequencerCh 设置为 nil，这意味着没有计划任何新的…

docker compose for Optimism
type: item
date: 2024-06-25T10:16:31+08:00
description: https://github.com/CryptoManufaktur-io/optimism-docker

Optimism Gas Tracker
type: item
date: 2024-06-25T10:13:00+08:00
description: https://github.com/buildooor/optimism-gas-tracker

Docker automation for Ethereum nodes
type: item
date: 2024-06-24T15:54:33+08:00
description: https://github.com/eth-educators/eth-docker https://ethdocker.com/