op-program主要分为两大模块
原文链接:<https://github.com/joohhnnn/The-book-of-optimism-fault-proof-CN/blob/main/04-op-program.md>\ 作者:joohhnnn
op-program 主要分为两大模块:
原生的基于 op-node 和 op-geth 的实现模块因其复杂性及某些操作无法被 MIPS 表示,无法直接生成对应的 ELF 文件。这就需要一个能完全表达 L2 state 执行、去除复杂操作且完全适配 MIPS 的简化版 op-node 和 op-geth 实现。Client 模块是一个实现了与生产代码逻辑一致的 Golang 模块,用于生成适配 MIPS 使用的 ELF 文件。此 ELF 文件将在后续被 Cannon 解析,生成包含内存、寄存器排列的 state.json 供 Cannon 使用。
ELF(Executable and Linkable Format)文件是一种广泛使用的文件格式,主要用于 Unix 和类 Unix 操作系统(如 Linux)。这种格式支持可执行文件、可重定位文件以及共享库,并允许包含详细的元数据,如程序的内存布局、各种段(如代码段、数据段)的位置和大小,以及其他用于动态链接和程序执行的信息。
当程序被编译并打包成一个 ELF 文件时,其内存布局(如代码段和数据段的位置)及启动时需要的初始化设置被固定下来。在 Cannon 中,这些数据被转化为 state.json,作为输入供 Cannon 使用。
runDerivation() 函数实现了 op-stack 中 L2 state 变化的执行过程。
// runDerivation executes the L2 state transition, given a minimal interface to retrieve data.
func runDerivation(logger log.Logger, cfg *rollup.Config, l2Cfg *params.ChainConfig, l1Head common.Hash, l2OutputRoot common.Hash, l2Claim common.Hash, l2ClaimBlockNum uint64, l1Oracle l1.Oracle, l2Oracle l2.Oracle) error {
l1Source := l1.NewOracleL1Client(logger, l1Oracle, l1Head)
engineBackend, err := l2.NewOracleBackedL2Chain(logger, l2Oracle, l2Cfg, l2OutputRoot)
if err != nil {
return fmt.Errorf("failed to create oracle-backed L2 chain: %w", err)
}
l2Source := l2.NewOracleEngine(cfg, logger, engineBackend)
logger.Info("Starting derivation")
d := cldr.NewDriver(logger, cfg, l1Source, l2Source, l2ClaimBlockNum)
for {
if err = d.Step(context.Background()); errors.Is(err, io.EOF) {
break
} else if err != nil {
return err
}
}
return d.ValidateClaim(l2ClaimBlockNum, eth.Bytes32(l2Claim))
}
注意,此模块只负责编译生成 ELF 文件,而将 ELF 文件解析为 state.json 的逻辑是在 Cannon 中完成的。
当 Cannon 启动时,会同时启动 Host 模块(./bin/op-program
,这里指的是 host 编译后的二进制名称,不是整个 op-program 模块)。当执行 syscall 时,如果需要读取操作,会抛出一个 hint,Host 的服务捕获此 hint 并将数据加载,以供使用。
因为 Host 是随着 Cannon 的启动命令启动的,因此 Host 是 Cannon 的子模块,二者通过一对 FileChannel
对象进行通讯,例如 pClientRW, pOracleRW
,pOracleRW
通道在创建子命令时传递到子程序,并通过如 pWriter := os.NewFile(6, "pOracleWriter")
的方式获取。
func NewProcessPreimageOracle(name string, args []string) (*ProcessPreimageOracle, error) {
if name == "" {
return &ProcessPreimageOracle{}, nil
}
pClientRW, pOracleRW, err := preimage.CreateBidirectionalChannel()
if err != nil {
return nil, err
}
hClientRW, hOracleRW, err := preimage.CreateBidirectionalChannel()
if err != nil {
return nil, err
}
cmd := exec.Command(name, args...) // nosemgrep
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.ExtraFiles = []*os.File{
hOracleRW.Reader(),
hOracleRW.Writer(),
pOracleRW.Reader(),
pOracleRW.Writer(),
}
在子进程代码中使用这些描述符:
// CreatePreimageChannel returns a FileChannel for the preimage oracle in a detached context
func CreatePreimageChannel() oppio.FileChannel {
r := os.NewFile(PClientRFd, "preimage-oracle-read")
w := os.NewFile(PClientWFd, "preimage-oracle-write")
return oppio.NewReadWritePair(r, w)
}
当通讯建立后,当 Cannon 中需要读取对应 key 的 pre-image 数据后,会往 channel 中写入 key,Host 从 channel 中读取 key,并根据 key 来获取相应的数据,并将数据写入 channel,供 Cannon 使用。
核心函数为 GetPreimage() 函数,此函数收到由 Cannon 传过来的 key,并对内容进行获取。
func (p *Prefetcher) GetPreimage(ctx context.Context, key common.Hash) ([]byte, error) {
p.logger.Trace("Pre-image requested", "key", key)
pre, err := p.kvStore.Get(key)
// Use a loop to keep retrying the prefetch as long as the key is not found
// This handles the case where the prefetch downloads a preimage, but it is then deleted unexpectedly
// before we get to read it.
for errors.Is(err, kvstore.ErrNotFound) && p.lastHint != "" {
hint := p.lastHint
if err := p.prefetch(ctx, hint); err != nil {
return nil, fmt.Errorf("prefetch failed: %w", err)
}
pre, err = p.kvStore.Get(key)
if err != nil {
p.logger.Error("Fetched pre-images for last hint but did not find required key", "hint", hint, "key", key)
}
}
return pre, err
}
官方对 OP-program 的介绍 可以完美总结 Client 模块的功能:该程序通过 rollup 状态转换来验证从 L1 输入得到的 L2 输出。这种可验证的输出随后可以用来解决 L1 上的争议输出。FPP 是 op-node 和 op-geth 的结合体,因此它在单一进程中同时具备了协议的共识和执行“部分”。这意味着通常通过 HTTP 进行的 Engine API 调用改为直接对 op-geth 代码进行方法调用。FPP 设计之初就是为了能够以确定性方式运行,这样使用相同的输入数据进行的两次调用不仅会产生相同的输出,还会产生相同的程序执行迹。这使得它可以作为争议解决过程的一部分,在链上 VM 中运行。
对于 Host 模块,它是一个为 Cannon 在运行时提供 Pre-image 信息的服务。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!