Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions op-chain-ops/cmd/check-output-root/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Overview

Generates an output root for a given block using only the execution client RPC endpoint.

## Prerequisites:

1. git clone or pull the latest develop branch of the optimism repo
2. Go Installed
- You can follow the instructions in the [CONTRIBUTING.md](http://CONTRIBUTING.md) to install all software dependencies of the repo
3. RPC URL for the **L2** chain execution client you want to generate a output root for.
- **Important**: The RPC endpoint must be trusted as it provide the chain state used to compute the output root.

## Usage:

```bash
go run op-chain-ops/cmd/check-output-root/ --l2-eth-rpc $RPC_URL --block-num $BLOCK_NUM
```

Output:

```text
0xfefc68b1c0aa7f6e744a8c74084142cf3daa8692179fd5b9ff46c6eacdffe9aa
```

## Environment Variables

Alternatively, you can use environment variables to configure the script:

- `CHECK_OUTPUT_ROOT_L2_ETH_RPC`: L2 execution client RPC endpoint.
- `CHECK_OUTPUT_ROOT_BLOCK_NUM`: Block number to calculate the output root for.
91 changes: 91 additions & 0 deletions op-chain-ops/cmd/check-output-root/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package main

import (
"context"
"fmt"
"math/big"
"os"

"github.com/ethereum-optimism/optimism/op-service/ctxinterrupt"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2"

oplog "github.com/ethereum-optimism/optimism/op-service/log"
)

const (
// L2EthRpcFlagname defines the flag name for the l2 eth RPC endpoint.
L2EthRpcFlagName = "l2-eth-rpc"
// BlockNumberFlagName defines the flag name for the L2 block number.
BlockNumberFlagName = "block-num"
)

// Flags contains the list of configuration options available to the binary.
var Flags = []cli.Flag{
&cli.StringFlag{
Name: L2EthRpcFlagName,
Usage: "Required: L2 execution client RPC endpoint (e.g., http://host:port).",
Required: true,
EnvVars: []string{"CHECK_OUTPUT_ROOT_L2_ETH_RPC"},
},
&cli.Uint64Flag{
Name: BlockNumberFlagName,
Usage: "Required: L2 block number to calculate the output root for.",
EnvVars: []string{"CHECK_OUTPUT_ROOT_BLOCK_NUM"},
},
}

func main() {
oplog.SetupDefaults()

app := cli.NewApp()
app.Name = "check-output-root"
app.Usage = "Calculates a output root from an L2 EL endpoint."
// Combine specific flags with log flags
app.Flags = append(Flags, oplog.CLIFlags("CHECK_OUTPUT_ROOT")...)

app.Action = func(c *cli.Context) error {
ctx := ctxinterrupt.WithCancelOnInterrupt(c.Context)
rpcUrl := c.String(L2EthRpcFlagName)
blockNum := c.Uint64(BlockNumberFlagName)
root, err := CalculateOutputRoot(ctx, rpcUrl, blockNum)
if err != nil {
return err
}
fmt.Println(root.Hex())
return nil
}

if err := app.Run(os.Args); err != nil {
log.Crit("Application failed", "err", err)
}
}

func CalculateOutputRoot(ctx context.Context, rpcUrl string, blockNum uint64) (common.Hash, error) {
client, err := ethclient.DialContext(ctx, rpcUrl)
if err != nil {
return common.Hash{}, fmt.Errorf("failed to connect to L2 RPC endpoint: %w", err)
}
header, err := client.HeaderByNumber(ctx, new(big.Int).SetUint64(blockNum))
if err != nil {
return common.Hash{}, fmt.Errorf("failed to get L2 block header: %w", err)
}
// Isthmus assumes WithdrawalsHash is present in the header.
if header.WithdrawalsHash == nil {
return common.Hash{}, fmt.Errorf("target block %d (%s) is missing withdrawals hash, required for Isthmus output root calculation",
header.Number.Uint64(), header.Hash())
}

// Construct OutputV0 using StateRoot, WithdrawalsHash (as MessagePasserStorageRoot), and BlockHash
output := &eth.OutputV0{
StateRoot: eth.Bytes32(header.Root),
MessagePasserStorageRoot: eth.Bytes32(*header.WithdrawalsHash),
BlockHash: header.Hash(),
}

// Calculate the output root hash
return common.Hash(eth.OutputRoot(output)), nil
}