Commit 416289e8 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into jg/backoff_when_dial_rpc_proposer_batcher

parents 24126a4a 1e46da0f
package cmd
import (
"fmt"
"os"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/urfave/cli/v2"
)
var (
WitnessInputFlag = &cli.PathFlag{
Name: "input",
Usage: "path of input JSON state.",
TakesFile: true,
Required: true,
}
WitnessOutputFlag = &cli.PathFlag{
Name: "output",
Usage: "path to write binary witness.",
TakesFile: true,
}
)
func Witness(ctx *cli.Context) error {
input := ctx.Path(WitnessInputFlag.Name)
output := ctx.Path(WitnessOutputFlag.Name)
state, err := loadJSON[mipsevm.State](input)
if err != nil {
return fmt.Errorf("invalid input state (%v): %w", input, err)
}
witness := state.EncodeWitness()
h := crypto.Keccak256Hash(witness)
if output != "" {
if err := os.WriteFile(output, witness, 0755); err != nil {
return fmt.Errorf("writing output to %v: %w", output, err)
}
}
fmt.Println(h.Hex())
return nil
}
var WitnessCommand = &cli.Command{
Name: "witness",
Usage: "Convert a Cannon JSON state into a binary witness",
Description: "Convert a Cannon JSON state into a binary witness. The hash of the witness is written to stdout",
Action: Witness,
Flags: []cli.Flag{
WitnessInputFlag,
WitnessOutputFlag,
},
}
...@@ -20,6 +20,7 @@ func main() { ...@@ -20,6 +20,7 @@ func main() {
app.Description = "MIPS Fault Proof tool" app.Description = "MIPS Fault Proof tool"
app.Commands = []*cli.Command{ app.Commands = []*cli.Command{
cmd.LoadELFCommand, cmd.LoadELFCommand,
cmd.WitnessCommand,
cmd.RunCommand, cmd.RunCommand,
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
......
This diff is collapsed.
This diff is collapsed.
...@@ -423,6 +423,19 @@ func checkL2ERC721Bridge(addr common.Address, client *ethclient.Client) error { ...@@ -423,6 +423,19 @@ func checkL2ERC721Bridge(addr common.Address, client *ethclient.Client) error {
if otherBridge == (common.Address{}) { if otherBridge == (common.Address{}) {
return errors.New("L2ERC721Bridge.OTHERBRIDGE is zero address") return errors.New("L2ERC721Bridge.OTHERBRIDGE is zero address")
} }
initialized, err := getInitialized("L2ERC721Bridge", addr, client)
if err != nil {
return err
}
log.Info("L2ERC721Bridge", "_initialized", initialized)
initializing, err := getInitializing("L2ERC721Bridge", addr, client)
if err != nil {
return err
}
log.Info("L2ERC721Bridge", "_initializing", initializing)
version, err := contract.Version(&bind.CallOpts{}) version, err := contract.Version(&bind.CallOpts{})
if err != nil { if err != nil {
return err return err
...@@ -614,6 +627,12 @@ func checkL2StandardBridge(addr common.Address, client *ethclient.Client) error ...@@ -614,6 +627,12 @@ func checkL2StandardBridge(addr common.Address, client *ethclient.Client) error
} }
log.Info("L2StandardBridge", "_initialized", initialized) log.Info("L2StandardBridge", "_initialized", initialized)
initializing, err := getInitializing("L2StandardBridge", addr, client)
if err != nil {
return err
}
log.Info("L2StandardBridge", "_initializing", initializing)
log.Info("L2StandardBridge version", "version", version) log.Info("L2StandardBridge version", "version", version)
return nil return nil
} }
...@@ -727,6 +746,12 @@ func checkL2CrossDomainMessenger(addr common.Address, client *ethclient.Client) ...@@ -727,6 +746,12 @@ func checkL2CrossDomainMessenger(addr common.Address, client *ethclient.Client)
} }
log.Info("L2CrossDomainMessenger", "_initialized", initialized) log.Info("L2CrossDomainMessenger", "_initialized", initialized)
initializing, err := getInitializing("L2CrossDomainMessenger", addr, client)
if err != nil {
return err
}
log.Info("L2CrossDomainMessenger", "_initializing", initializing)
log.Info("L2CrossDomainMessenger version", "version", version) log.Info("L2CrossDomainMessenger version", "version", version)
return nil return nil
} }
...@@ -823,11 +848,33 @@ func getEIP1967ImplementationAddress(client *ethclient.Client, addr common.Addre ...@@ -823,11 +848,33 @@ func getEIP1967ImplementationAddress(client *ethclient.Client, addr common.Addre
// This is an incrementing number that starts at 1 and increments each time that // This is an incrementing number that starts at 1 and increments each time that
// the contract is upgraded. // the contract is upgraded.
func getInitialized(name string, addr common.Address, client *ethclient.Client) (*big.Int, error) { func getInitialized(name string, addr common.Address, client *ethclient.Client) (*big.Int, error) {
value, err := getStorageValue(name, "_initialized", addr, client)
if err != nil {
return nil, err
}
return new(big.Int).SetBytes(value), nil
}
// getInitializing will get the _initializing value in storage of a contract.
func getInitializing(name string, addr common.Address, client *ethclient.Client) (bool, error) {
value, err := getStorageValue(name, "_initializing", addr, client)
if err != nil {
return false, err
}
if len(value) != 1 {
return false, fmt.Errorf("Unexpected length for _initializing: %d", len(value))
}
return value[0] == 1, nil
}
// getStorageValue will get the value of a named storage slot in a contract. It isn't smart about
// automatically converting from a byte slice to a type, it is the caller's responsibility to do that.
func getStorageValue(name, entryName string, addr common.Address, client *ethclient.Client) ([]byte, error) {
layout, err := bindings.GetStorageLayout(name) layout, err := bindings.GetStorageLayout(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
entry, err := layout.GetStorageLayoutEntry("_initialized") entry, err := layout.GetStorageLayoutEntry(entryName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -848,6 +895,5 @@ func getInitialized(name string, addr common.Address, client *ethclient.Client) ...@@ -848,6 +895,5 @@ func getInitialized(name string, addr common.Address, client *ethclient.Client)
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 { for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
slice[i], slice[j] = slice[j], slice[i] slice[i], slice[j] = slice[j], slice[i]
} }
initialized := new(big.Int).SetBytes(slice[entry.Offset : entry.Offset+typ.NumberOfBytes]) return slice[entry.Offset : entry.Offset+typ.NumberOfBytes], nil
return initialized, nil
} }
...@@ -52,7 +52,11 @@ func TestCode(t *testing.T) { ...@@ -52,7 +52,11 @@ func TestCode(t *testing.T) {
db.SetCode(addr, code) db.SetCode(addr, code)
post := db.GetCode(addr) post := db.GetCode(addr)
require.Equal(t, post, code) if len(code) == 0 {
require.Nil(t, post)
} else {
require.Equal(t, post, code)
}
size := db.GetCodeSize(addr) size := db.GetCodeSize(addr)
require.Equal(t, size, len(code)) require.Equal(t, size, len(code))
......
...@@ -19,8 +19,23 @@ test: ...@@ -19,8 +19,23 @@ test:
lint: lint:
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell,errorlint -e "errors.As" -e "errors.Is" golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell,errorlint -e "errors.As" -e "errors.Is"
visualize:
./scripts/visualize.sh
alphabet:
./scripts/alphabet/init_game.sh
make alphabet-actors
alphabet-actors:
@./scripts/parallel.sh \
./scripts/alphabet/mallory.sh 'mallory' \
./scripts/alphabet/charlie.sh 'charlie'
.PHONY: \ .PHONY: \
clean \ clean \
op-challenger \ op-challenger \
test \ test \
lint lint \
visualize \
alphabet \
alphabet-actors
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -15,6 +16,8 @@ import ( ...@@ -15,6 +16,8 @@ import (
var ( var (
l1EthRpc = "http://example.com:8545" l1EthRpc = "http://example.com:8545"
gameAddressValue = "0xaa00000000000000000000000000000000000000" gameAddressValue = "0xaa00000000000000000000000000000000000000"
cannonNetwork = chaincfg.AvailableNetworks()[0]
otherCannonNetwork = chaincfg.AvailableNetworks()[1]
cannonBin = "./bin/cannon" cannonBin = "./bin/cannon"
cannonServer = "./bin/op-program" cannonServer = "./bin/op-program"
cannonPreState = "./pre.json" cannonPreState = "./pre.json"
...@@ -226,6 +229,67 @@ func TestCannonSnapshotFreq(t *testing.T) { ...@@ -226,6 +229,67 @@ func TestCannonSnapshotFreq(t *testing.T) {
}) })
} }
func TestRequireEitherCannonNetworkOrRollupAndGenesis(t *testing.T) {
verifyArgsInvalid(
t,
"flag cannon-network or cannon-rollup-config and cannon-l2-genesis is required",
addRequiredArgsExcept(config.TraceTypeCannon, "--cannon-network"))
verifyArgsInvalid(
t,
"flag cannon-network or cannon-rollup-config and cannon-l2-genesis is required",
addRequiredArgsExcept(config.TraceTypeCannon, "--cannon-network", "--cannon-rollup-config=rollup.json"))
verifyArgsInvalid(
t,
"flag cannon-network or cannon-rollup-config and cannon-l2-genesis is required",
addRequiredArgsExcept(config.TraceTypeCannon, "--cannon-network", "--cannon-l2-genesis=gensis.json"))
}
func TestMustNotSpecifyNetworkAndRollup(t *testing.T) {
verifyArgsInvalid(
t,
"flag cannon-network can not be used with cannon-rollup-config and cannon-l2-genesis",
addRequiredArgsExcept(config.TraceTypeCannon, "--cannon-network",
"--cannon-network", cannonNetwork, "--cannon-rollup-config=rollup.json"))
}
func TestCannonNetwork(t *testing.T) {
t.Run("NotRequiredForAlphabetTrace", func(t *testing.T) {
configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--cannon-network"))
})
t.Run("NotRequiredWhenRollupAndGenesIsSpecified", func(t *testing.T) {
configForArgs(t, addRequiredArgsExcept(config.TraceTypeCannon, "--cannon-network",
"--cannon-rollup-config=rollup.json", "--cannon-l2-genesis=genesis.json"))
})
t.Run("Valid", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgsExcept(config.TraceTypeCannon, "--cannon-network", "--cannon-network", otherCannonNetwork))
require.Equal(t, otherCannonNetwork, cfg.CannonNetwork)
})
}
func TestCannonRollupConfig(t *testing.T) {
t.Run("NotRequiredForAlphabetTrace", func(t *testing.T) {
configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--cannon-rollup-config"))
})
t.Run("Valid", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgsExcept(config.TraceTypeCannon, "--cannon-network", "--cannon-rollup-config=rollup.json", "--cannon-l2-genesis=genesis.json"))
require.Equal(t, "rollup.json", cfg.CannonRollupConfigPath)
})
}
func TestCannonL2Genesis(t *testing.T) {
t.Run("NotRequiredForAlphabetTrace", func(t *testing.T) {
configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--cannon-l2-genesis"))
})
t.Run("Valid", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgsExcept(config.TraceTypeCannon, "--cannon-network", "--cannon-rollup-config=rollup.json", "--cannon-l2-genesis=genesis.json"))
require.Equal(t, "genesis.json", cfg.CannonL2GenesisPath)
})
}
func verifyArgsInvalid(t *testing.T, messageContains string, cliArgs []string) { func verifyArgsInvalid(t *testing.T, messageContains string, cliArgs []string) {
_, _, err := runWithArgs(cliArgs) _, _, err := runWithArgs(cliArgs)
require.ErrorContains(t, err, messageContains) require.ErrorContains(t, err, messageContains)
...@@ -273,6 +337,7 @@ func requiredArgs(traceType config.TraceType) map[string]string { ...@@ -273,6 +337,7 @@ func requiredArgs(traceType config.TraceType) map[string]string {
case config.TraceTypeAlphabet: case config.TraceTypeAlphabet:
args["--alphabet"] = alphabetTrace args["--alphabet"] = alphabetTrace
case config.TraceTypeCannon: case config.TraceTypeCannon:
args["--cannon-network"] = cannonNetwork
args["--cannon-bin"] = cannonBin args["--cannon-bin"] = cannonBin
args["--cannon-server"] = cannonServer args["--cannon-server"] = cannonServer
args["--cannon-prestate"] = cannonPreState args["--cannon-prestate"] = cannonPreState
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
...@@ -18,8 +19,12 @@ var ( ...@@ -18,8 +19,12 @@ var (
ErrMissingAlphabetTrace = errors.New("missing alphabet trace") ErrMissingAlphabetTrace = errors.New("missing alphabet trace")
ErrMissingL1EthRPC = errors.New("missing l1 eth rpc url") ErrMissingL1EthRPC = errors.New("missing l1 eth rpc url")
ErrMissingGameAddress = errors.New("missing game address") ErrMissingGameAddress = errors.New("missing game address")
ErrMissingPreimageOracleAddress = errors.New("missing pre-image oracle address")
ErrMissingCannonSnapshotFreq = errors.New("missing cannon snapshot freq") ErrMissingCannonSnapshotFreq = errors.New("missing cannon snapshot freq")
ErrMissingCannonRollupConfig = errors.New("missing cannon network or rollup config path")
ErrMissingCannonL2Genesis = errors.New("missing cannon network or l2 genesis path")
ErrCannonNetworkAndRollupConfig = errors.New("only specify one of network or rollup config path")
ErrCannonNetworkAndL2Genesis = errors.New("only specify one of network or l2 genesis path")
ErrCannonNetworkUnknown = errors.New("unknown cannon network")
) )
type TraceType string type TraceType string
...@@ -53,7 +58,7 @@ func ValidTraceType(value TraceType) bool { ...@@ -53,7 +58,7 @@ func ValidTraceType(value TraceType) bool {
return false return false
} }
const DefaultCannonSnapshotFreq = uint(10_000) const DefaultCannonSnapshotFreq = uint(1_000_000_000)
// Config is a well typed config that is parsed from the CLI params. // Config is a well typed config that is parsed from the CLI params.
// This also contains config options for auxiliary services. // This also contains config options for auxiliary services.
...@@ -73,6 +78,9 @@ type Config struct { ...@@ -73,6 +78,9 @@ type Config struct {
CannonBin string // Path to the cannon executable to run when generating trace data CannonBin string // Path to the cannon executable to run when generating trace data
CannonServer string // Path to the op-program executable that provides the pre-image oracle server CannonServer string // Path to the op-program executable that provides the pre-image oracle server
CannonAbsolutePreState string // File to load the absolute pre-state for Cannon traces from CannonAbsolutePreState string // File to load the absolute pre-state for Cannon traces from
CannonNetwork string
CannonRollupConfigPath string
CannonL2GenesisPath string
CannonDatadir string // Cannon Data Directory CannonDatadir string // Cannon Data Directory
CannonL2 string // L2 RPC Url CannonL2 string // L2 RPC Url
CannonSnapshotFreq uint // Frequency of snapshots to create when executing cannon (in VM instructions) CannonSnapshotFreq uint // Frequency of snapshots to create when executing cannon (in VM instructions)
...@@ -119,6 +127,24 @@ func (c Config) Check() error { ...@@ -119,6 +127,24 @@ func (c Config) Check() error {
if c.CannonServer == "" { if c.CannonServer == "" {
return ErrMissingCannonServer return ErrMissingCannonServer
} }
if c.CannonNetwork == "" {
if c.CannonRollupConfigPath == "" {
return ErrMissingCannonRollupConfig
}
if c.CannonL2GenesisPath == "" {
return ErrMissingCannonL2Genesis
}
} else {
if c.CannonRollupConfigPath != "" {
return ErrCannonNetworkAndRollupConfig
}
if c.CannonL2GenesisPath != "" {
return ErrCannonNetworkAndL2Genesis
}
if _, ok := chaincfg.NetworksByName[c.CannonNetwork]; !ok {
return fmt.Errorf("%w: %v", ErrCannonNetworkUnknown, c.CannonNetwork)
}
}
if c.CannonAbsolutePreState == "" { if c.CannonAbsolutePreState == "" {
return ErrMissingCannonAbsolutePreState return ErrMissingCannonAbsolutePreState
} }
......
...@@ -14,6 +14,7 @@ var ( ...@@ -14,6 +14,7 @@ var (
validAlphabetTrace = "abcdefgh" validAlphabetTrace = "abcdefgh"
validCannonBin = "./bin/cannon" validCannonBin = "./bin/cannon"
validCannonOpProgramBin = "./bin/op-program" validCannonOpProgramBin = "./bin/op-program"
validCannonNetwork = "mainnet"
validCannonAbsolutPreState = "pre.json" validCannonAbsolutPreState = "pre.json"
validCannonDatadir = "/tmp/cannon" validCannonDatadir = "/tmp/cannon"
validCannonL2 = "http://localhost:9545" validCannonL2 = "http://localhost:9545"
...@@ -32,6 +33,7 @@ func validConfig(traceType TraceType) Config { ...@@ -32,6 +33,7 @@ func validConfig(traceType TraceType) Config {
cfg.CannonAbsolutePreState = validCannonAbsolutPreState cfg.CannonAbsolutePreState = validCannonAbsolutPreState
cfg.CannonDatadir = validCannonDatadir cfg.CannonDatadir = validCannonDatadir
cfg.CannonL2 = validCannonL2 cfg.CannonL2 = validCannonL2
cfg.CannonNetwork = validCannonNetwork
} }
return cfg return cfg
} }
...@@ -110,3 +112,41 @@ func TestCannonSnapshotFreq(t *testing.T) { ...@@ -110,3 +112,41 @@ func TestCannonSnapshotFreq(t *testing.T) {
require.ErrorIs(t, cfg.Check(), ErrMissingCannonSnapshotFreq) require.ErrorIs(t, cfg.Check(), ErrMissingCannonSnapshotFreq)
}) })
} }
func TestCannonNetworkOrRollupConfigRequired(t *testing.T) {
cfg := validConfig(TraceTypeCannon)
cfg.CannonNetwork = ""
cfg.CannonRollupConfigPath = ""
cfg.CannonL2GenesisPath = "genesis.json"
require.ErrorIs(t, cfg.Check(), ErrMissingCannonRollupConfig)
}
func TestCannonNetworkOrL2GenesisRequired(t *testing.T) {
cfg := validConfig(TraceTypeCannon)
cfg.CannonNetwork = ""
cfg.CannonRollupConfigPath = "foo.json"
cfg.CannonL2GenesisPath = ""
require.ErrorIs(t, cfg.Check(), ErrMissingCannonL2Genesis)
}
func TestMustNotSpecifyNetworkAndRollup(t *testing.T) {
cfg := validConfig(TraceTypeCannon)
cfg.CannonNetwork = validCannonNetwork
cfg.CannonRollupConfigPath = "foo.json"
cfg.CannonL2GenesisPath = ""
require.ErrorIs(t, cfg.Check(), ErrCannonNetworkAndRollupConfig)
}
func TestMustNotSpecifyNetworkAndL2Genesis(t *testing.T) {
cfg := validConfig(TraceTypeCannon)
cfg.CannonNetwork = validCannonNetwork
cfg.CannonRollupConfigPath = ""
cfg.CannonL2GenesisPath = "foo.json"
require.ErrorIs(t, cfg.Check(), ErrCannonNetworkAndL2Genesis)
}
func TestNetworkMustBeValid(t *testing.T) {
cfg := validConfig(TraceTypeCannon)
cfg.CannonNetwork = "unknown"
require.ErrorIs(t, cfg.Check(), ErrCannonNetworkUnknown)
}
package cannon
import (
"encoding/json"
"fmt"
"os"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
)
func parseState(path string) (*mipsevm.State, error) {
file, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("cannot open state file (%v): %w", path, err)
}
defer file.Close()
var state mipsevm.State
err = json.NewDecoder(file).Decode(&state)
if err != nil {
return nil, fmt.Errorf("invalid mipsevm state (%v): %w", path, err)
}
return &state, nil
}
...@@ -4,11 +4,13 @@ import ( ...@@ -4,11 +4,13 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"math"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strconv" "strconv"
"strings"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
...@@ -17,7 +19,8 @@ import ( ...@@ -17,7 +19,8 @@ import (
const ( const (
snapsDir = "snapshots" snapsDir = "snapshots"
preimagesDir = "snapshots" preimagesDir = "preimages"
finalState = "final.json"
) )
var snapshotNameRegexp = regexp.MustCompile(`^[0-9]+\.json$`) var snapshotNameRegexp = regexp.MustCompile(`^[0-9]+\.json$`)
...@@ -32,6 +35,9 @@ type Executor struct { ...@@ -32,6 +35,9 @@ type Executor struct {
inputs localGameInputs inputs localGameInputs
cannon string cannon string
server string server string
network string
rollupConfig string
l2Genesis string
absolutePreState string absolutePreState string
dataDir string dataDir string
snapshotFreq uint snapshotFreq uint
...@@ -47,6 +53,9 @@ func NewExecutor(logger log.Logger, cfg *config.Config, inputs localGameInputs) ...@@ -47,6 +53,9 @@ func NewExecutor(logger log.Logger, cfg *config.Config, inputs localGameInputs)
inputs: inputs, inputs: inputs,
cannon: cfg.CannonBin, cannon: cfg.CannonBin,
server: cfg.CannonServer, server: cfg.CannonServer,
network: cfg.CannonNetwork,
rollupConfig: cfg.CannonRollupConfigPath,
l2Genesis: cfg.CannonL2GenesisPath,
absolutePreState: cfg.CannonAbsolutePreState, absolutePreState: cfg.CannonAbsolutePreState,
dataDir: cfg.CannonDatadir, dataDir: cfg.CannonDatadir,
snapshotFreq: cfg.CannonSnapshotFreq, snapshotFreq: cfg.CannonSnapshotFreq,
...@@ -63,18 +72,23 @@ func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) erro ...@@ -63,18 +72,23 @@ func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) erro
} }
proofDir := filepath.Join(dir, proofsDir) proofDir := filepath.Join(dir, proofsDir)
dataDir := filepath.Join(e.dataDir, preimagesDir) dataDir := filepath.Join(e.dataDir, preimagesDir)
lastGeneratedState := filepath.Join(dir, finalState)
args := []string{ args := []string{
"run", "run",
"--input", start, "--input", start,
"--output", filepath.Join(dir, "out.json"), "--output", lastGeneratedState,
"--meta", "", "--meta", "",
"--proof-at", "=" + strconv.FormatUint(i, 10), "--proof-at", "=" + strconv.FormatUint(i, 10),
"--stop-at", "=" + strconv.FormatUint(i+1, 10),
"--proof-fmt", filepath.Join(proofDir, "%d.json"), "--proof-fmt", filepath.Join(proofDir, "%d.json"),
"--snapshot-at", "%" + strconv.FormatUint(uint64(e.snapshotFreq), 10), "--snapshot-at", "%" + strconv.FormatUint(uint64(e.snapshotFreq), 10),
"--snapshot-fmt", filepath.Join(snapshotDir, "%d.json"), "--snapshot-fmt", filepath.Join(snapshotDir, "%d.json"),
}
if i < math.MaxUint64 {
args = append(args, "--stop-at", "="+strconv.FormatUint(i+1, 10))
}
args = append(args,
"--", "--",
e.server, e.server, "--server",
"--l1", e.l1, "--l1", e.l1,
"--l2", e.l2, "--l2", e.l2,
"--datadir", dataDir, "--datadir", dataDir,
...@@ -83,6 +97,15 @@ func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) erro ...@@ -83,6 +97,15 @@ func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) erro
"--l2.outputroot", e.inputs.l2OutputRoot.Hex(), "--l2.outputroot", e.inputs.l2OutputRoot.Hex(),
"--l2.claim", e.inputs.l2Claim.Hex(), "--l2.claim", e.inputs.l2Claim.Hex(),
"--l2.blocknumber", e.inputs.l2BlockNumber.Text(10), "--l2.blocknumber", e.inputs.l2BlockNumber.Text(10),
)
if e.network != "" {
args = append(args, "--network", e.network)
}
if e.rollupConfig != "" {
args = append(args, "--rollup.config", e.rollupConfig)
}
if e.l2Genesis != "" {
args = append(args, "--l2.genesis", e.l2Genesis)
} }
if err := os.MkdirAll(snapshotDir, 0755); err != nil { if err := os.MkdirAll(snapshotDir, 0755); err != nil {
...@@ -94,7 +117,7 @@ func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) erro ...@@ -94,7 +117,7 @@ func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) erro
if err := os.MkdirAll(proofDir, 0755); err != nil { if err := os.MkdirAll(proofDir, 0755); err != nil {
return fmt.Errorf("could not create proofs directory %v: %w", proofDir, err) return fmt.Errorf("could not create proofs directory %v: %w", proofDir, err)
} }
e.logger.Info("Generating trace", "proof", i, "cmd", e.cannon, "args", args) e.logger.Info("Generating trace", "proof", i, "cmd", e.cannon, "args", strings.Join(args, ", "))
return e.cmdExecutor(ctx, e.logger.New("proof", i), e.cannon, args...) return e.cmdExecutor(ctx, e.logger.New("proof", i), e.cannon, args...)
} }
...@@ -102,7 +125,8 @@ func runCmd(ctx context.Context, l log.Logger, binary string, args ...string) er ...@@ -102,7 +125,8 @@ func runCmd(ctx context.Context, l log.Logger, binary string, args ...string) er
cmd := exec.CommandContext(ctx, binary, args...) cmd := exec.CommandContext(ctx, binary, args...)
stdOut := oplog.NewWriter(l, log.LvlInfo) stdOut := oplog.NewWriter(l, log.LvlInfo)
defer stdOut.Close() defer stdOut.Close()
stdErr := oplog.NewWriter(l, log.LvlError) // Keep stdErr at info level because cannon uses stderr for progress messages
stdErr := oplog.NewWriter(l, log.LvlInfo)
defer stdErr.Close() defer stdErr.Close()
cmd.Stdout = stdOut cmd.Stdout = stdOut
cmd.Stderr = stdErr cmd.Stderr = stdErr
...@@ -123,17 +147,17 @@ func findStartingSnapshot(logger log.Logger, snapDir string, absolutePreState st ...@@ -123,17 +147,17 @@ func findStartingSnapshot(logger log.Logger, snapDir string, absolutePreState st
bestSnap := uint64(0) bestSnap := uint64(0)
for _, entry := range entries { for _, entry := range entries {
if entry.IsDir() { if entry.IsDir() {
logger.Warn("Unexpected directory in snapshots dir: %v/%v", snapDir, entry.Name()) logger.Warn("Unexpected directory in snapshots dir", "parent", snapDir, "child", entry.Name())
continue continue
} }
name := entry.Name() name := entry.Name()
if !snapshotNameRegexp.MatchString(name) { if !snapshotNameRegexp.MatchString(name) {
logger.Warn("Unexpected file in snapshots dir: %v/%v", snapDir, entry.Name()) logger.Warn("Unexpected file in snapshots dir", "parent", snapDir, "child", entry.Name())
continue continue
} }
index, err := strconv.ParseUint(name[0:len(name)-len(".json")], 10, 64) index, err := strconv.ParseUint(name[0:len(name)-len(".json")], 10, 64)
if err != nil { if err != nil {
logger.Error("Unable to parse trace index of snapshot file: %v/%v", snapDir, entry.Name()) logger.Error("Unable to parse trace index of snapshot file", "parent", snapDir, "child", entry.Name())
continue continue
} }
if index > bestSnap && index < traceIndex { if index > bestSnap && index < traceIndex {
......
...@@ -3,6 +3,7 @@ package cannon ...@@ -3,6 +3,7 @@ package cannon
import ( import (
"context" "context"
"fmt" "fmt"
"math"
"math/big" "math/big"
"os" "os"
"path/filepath" "path/filepath"
...@@ -35,49 +36,90 @@ func TestGenerateProof(t *testing.T) { ...@@ -35,49 +36,90 @@ func TestGenerateProof(t *testing.T) {
l2Claim: common.Hash{0x44}, l2Claim: common.Hash{0x44},
l2BlockNumber: big.NewInt(3333), l2BlockNumber: big.NewInt(3333),
} }
captureExec := func(t *testing.T, cfg config.Config, proofAt uint64) (string, string, map[string]string) {
executor := NewExecutor(testlog.Logger(t, log.LvlInfo), &cfg, inputs) executor := NewExecutor(testlog.Logger(t, log.LvlInfo), &cfg, inputs)
executor.selectSnapshot = func(logger log.Logger, dir string, absolutePreState string, i uint64) (string, error) { executor.selectSnapshot = func(logger log.Logger, dir string, absolutePreState string, i uint64) (string, error) {
return input, nil return input, nil
} }
var binary string var binary string
var subcommand string var subcommand string
args := make(map[string]string) args := make(map[string]string)
executor.cmdExecutor = func(ctx context.Context, l log.Logger, b string, a ...string) error { executor.cmdExecutor = func(ctx context.Context, l log.Logger, b string, a ...string) error {
binary = b binary = b
subcommand = a[0] subcommand = a[0]
for i := 1; i < len(a); i += 2 { for i := 1; i < len(a); {
args[a[i]] = a[i+1] if a[i] == "--" {
// Skip over the divider between cannon and server program
i += 1
continue
}
args[a[i]] = a[i+1]
i += 2
}
return nil
} }
return nil err := executor.GenerateProof(context.Background(), cfg.CannonDatadir, proofAt)
require.NoError(t, err)
return binary, subcommand, args
} }
err := executor.GenerateProof(context.Background(), cfg.CannonDatadir, 150_000_000)
require.NoError(t, err) t.Run("Network", func(t *testing.T) {
require.DirExists(t, filepath.Join(cfg.CannonDatadir, preimagesDir)) cfg.CannonNetwork = "mainnet"
require.DirExists(t, filepath.Join(cfg.CannonDatadir, proofsDir)) cfg.CannonRollupConfigPath = ""
require.DirExists(t, filepath.Join(cfg.CannonDatadir, snapsDir)) cfg.CannonL2GenesisPath = ""
require.Equal(t, cfg.CannonBin, binary) binary, subcommand, args := captureExec(t, cfg, 150_000_000)
require.Equal(t, "run", subcommand) require.DirExists(t, filepath.Join(cfg.CannonDatadir, preimagesDir))
require.Equal(t, input, args["--input"]) require.DirExists(t, filepath.Join(cfg.CannonDatadir, proofsDir))
require.Contains(t, args, "--meta") require.DirExists(t, filepath.Join(cfg.CannonDatadir, snapsDir))
require.Equal(t, "", args["--meta"]) require.Equal(t, cfg.CannonBin, binary)
require.Equal(t, filepath.Join(cfg.CannonDatadir, "out.json"), args["--output"]) require.Equal(t, "run", subcommand)
require.Equal(t, "=150000000", args["--proof-at"]) require.Equal(t, input, args["--input"])
require.Equal(t, "=150000001", args["--stop-at"]) require.Contains(t, args, "--meta")
require.Equal(t, "%500", args["--snapshot-at"]) require.Equal(t, "", args["--meta"])
require.Equal(t, cfg.CannonServer, args["--"]) require.Equal(t, filepath.Join(cfg.CannonDatadir, finalState), args["--output"])
require.Equal(t, cfg.L1EthRpc, args["--l1"]) require.Equal(t, "=150000000", args["--proof-at"])
require.Equal(t, cfg.CannonL2, args["--l2"]) require.Equal(t, "=150000001", args["--stop-at"])
require.Equal(t, filepath.Join(cfg.CannonDatadir, preimagesDir), args["--datadir"]) require.Equal(t, "%500", args["--snapshot-at"])
require.Equal(t, filepath.Join(cfg.CannonDatadir, proofsDir, "%d.json"), args["--proof-fmt"]) // Slight quirk of how we pair off args
require.Equal(t, filepath.Join(cfg.CannonDatadir, snapsDir, "%d.json"), args["--snapshot-fmt"]) // The server binary winds up as the key and the first arg --server as the value which has no value
// Then everything else pairs off correctly again
// Local game inputs require.Equal(t, "--server", args[cfg.CannonServer])
require.Equal(t, inputs.l1Head.Hex(), args["--l1.head"]) require.Equal(t, cfg.L1EthRpc, args["--l1"])
require.Equal(t, inputs.l2Head.Hex(), args["--l2.head"]) require.Equal(t, cfg.CannonL2, args["--l2"])
require.Equal(t, inputs.l2OutputRoot.Hex(), args["--l2.outputroot"]) require.Equal(t, filepath.Join(cfg.CannonDatadir, preimagesDir), args["--datadir"])
require.Equal(t, inputs.l2Claim.Hex(), args["--l2.claim"]) require.Equal(t, filepath.Join(cfg.CannonDatadir, proofsDir, "%d.json"), args["--proof-fmt"])
require.Equal(t, "3333", args["--l2.blocknumber"]) require.Equal(t, filepath.Join(cfg.CannonDatadir, snapsDir, "%d.json"), args["--snapshot-fmt"])
require.Equal(t, cfg.CannonNetwork, args["--network"])
require.NotContains(t, args, "--rollup.config")
require.NotContains(t, args, "--l2.genesis")
// Local game inputs
require.Equal(t, inputs.l1Head.Hex(), args["--l1.head"])
require.Equal(t, inputs.l2Head.Hex(), args["--l2.head"])
require.Equal(t, inputs.l2OutputRoot.Hex(), args["--l2.outputroot"])
require.Equal(t, inputs.l2Claim.Hex(), args["--l2.claim"])
require.Equal(t, "3333", args["--l2.blocknumber"])
})
t.Run("RollupAndGenesis", func(t *testing.T) {
cfg.CannonNetwork = ""
cfg.CannonRollupConfigPath = "rollup.json"
cfg.CannonL2GenesisPath = "genesis.json"
_, _, args := captureExec(t, cfg, 150_000_000)
require.NotContains(t, args, "--network")
require.Equal(t, cfg.CannonRollupConfigPath, args["--rollup.config"])
require.Equal(t, cfg.CannonL2GenesisPath, args["--l2.genesis"])
})
t.Run("NoStopAtWhenProofIsMaxUInt", func(t *testing.T) {
cfg.CannonNetwork = "mainnet"
cfg.CannonRollupConfigPath = "rollup.json"
cfg.CannonL2GenesisPath = "genesis.json"
_, _, args := captureExec(t, cfg, math.MaxUint64)
// stop-at would need to be one more than the proof step which would overflow back to 0
// so expect that it will be omitted. We'll ultimately want cannon to execute until the program exits.
require.NotContains(t, args, "--stop-at")
})
} }
func TestRunCmdLogsOutput(t *testing.T) { func TestRunCmdLogsOutput(t *testing.T) {
......
...@@ -15,6 +15,7 @@ import ( ...@@ -15,6 +15,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -38,9 +39,14 @@ type ProofGenerator interface { ...@@ -38,9 +39,14 @@ type ProofGenerator interface {
} }
type CannonTraceProvider struct { type CannonTraceProvider struct {
logger log.Logger
dir string dir string
prestate string prestate string
generator ProofGenerator generator ProofGenerator
// lastStep stores the last step in the actual trace if known. 0 indicates unknown.
// Cached as an optimisation to avoid repeatedly attempting to execute beyond the end of the trace.
lastStep uint64
} }
func NewTraceProvider(ctx context.Context, logger log.Logger, cfg *config.Config, l1Client bind.ContractCaller) (*CannonTraceProvider, error) { func NewTraceProvider(ctx context.Context, logger log.Logger, cfg *config.Config, l1Client bind.ContractCaller) (*CannonTraceProvider, error) {
...@@ -58,6 +64,7 @@ func NewTraceProvider(ctx context.Context, logger log.Logger, cfg *config.Config ...@@ -58,6 +64,7 @@ func NewTraceProvider(ctx context.Context, logger log.Logger, cfg *config.Config
return nil, fmt.Errorf("fetch local game inputs: %w", err) return nil, fmt.Errorf("fetch local game inputs: %w", err)
} }
return &CannonTraceProvider{ return &CannonTraceProvider{
logger: logger,
dir: cfg.CannonDatadir, dir: cfg.CannonDatadir,
prestate: cfg.CannonAbsolutePreState, prestate: cfg.CannonAbsolutePreState,
generator: NewExecutor(logger, cfg, l1Head), generator: NewExecutor(logger, cfg, l1Head),
...@@ -65,7 +72,7 @@ func NewTraceProvider(ctx context.Context, logger log.Logger, cfg *config.Config ...@@ -65,7 +72,7 @@ func NewTraceProvider(ctx context.Context, logger log.Logger, cfg *config.Config
} }
func (p *CannonTraceProvider) GetOracleData(ctx context.Context, i uint64) (*types.PreimageOracleData, error) { func (p *CannonTraceProvider) GetOracleData(ctx context.Context, i uint64) (*types.PreimageOracleData, error) {
proof, err := p.loadProof(ctx, i) proof, err := p.loadProofData(ctx, i)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -74,10 +81,14 @@ func (p *CannonTraceProvider) GetOracleData(ctx context.Context, i uint64) (*typ ...@@ -74,10 +81,14 @@ func (p *CannonTraceProvider) GetOracleData(ctx context.Context, i uint64) (*typ
} }
func (p *CannonTraceProvider) Get(ctx context.Context, i uint64) (common.Hash, error) { func (p *CannonTraceProvider) Get(ctx context.Context, i uint64) (common.Hash, error) {
proof, err := p.loadProof(ctx, i) proof, state, err := p.loadProof(ctx, i)
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
if proof == nil && state != nil {
// Use the hash from the final state
return crypto.Keccak256Hash(state.EncodeWitness()), nil
}
value := common.BytesToHash(proof.ClaimValue) value := common.BytesToHash(proof.ClaimValue)
if value == (common.Hash{}) { if value == (common.Hash{}) {
...@@ -87,7 +98,7 @@ func (p *CannonTraceProvider) Get(ctx context.Context, i uint64) (common.Hash, e ...@@ -87,7 +98,7 @@ func (p *CannonTraceProvider) Get(ctx context.Context, i uint64) (common.Hash, e
} }
func (p *CannonTraceProvider) GetPreimage(ctx context.Context, i uint64) ([]byte, []byte, error) { func (p *CannonTraceProvider) GetPreimage(ctx context.Context, i uint64) ([]byte, []byte, error) {
proof, err := p.loadProof(ctx, i) proof, err := p.loadProofData(ctx, i)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
...@@ -104,37 +115,77 @@ func (p *CannonTraceProvider) GetPreimage(ctx context.Context, i uint64) ([]byte ...@@ -104,37 +115,77 @@ func (p *CannonTraceProvider) GetPreimage(ctx context.Context, i uint64) ([]byte
func (p *CannonTraceProvider) AbsolutePreState(ctx context.Context) ([]byte, error) { func (p *CannonTraceProvider) AbsolutePreState(ctx context.Context) ([]byte, error) {
path := filepath.Join(p.dir, p.prestate) path := filepath.Join(p.dir, p.prestate)
file, err := os.Open(path) state, err := parseState(path)
if err != nil { if err != nil {
return []byte{}, fmt.Errorf("cannot open state file (%v): %w", path, err) return []byte{}, fmt.Errorf("cannot load absolute pre-state: %w", err)
} }
defer file.Close() return state.EncodeWitness(), nil
var state mipsevm.State }
err = json.NewDecoder(file).Decode(&state)
// loadProofData loads the proof data for the specified step.
// If the requested index is beyond the end of the actual trace, the proof data from the last step is returned.
// Cannon will be executed a second time if required to generate the full proof data.
func (p *CannonTraceProvider) loadProofData(ctx context.Context, i uint64) (*proofData, error) {
proof, state, err := p.loadProof(ctx, i)
if err != nil { if err != nil {
return []byte{}, fmt.Errorf("invalid mipsevm state (%v): %w", path, err) return nil, err
} else if proof == nil && state != nil {
p.logger.Info("Re-executing to generate proof for last step", "step", state.Step)
proof, _, err = p.loadProof(ctx, state.Step)
if err != nil {
return nil, err
}
if proof == nil {
return nil, fmt.Errorf("proof at step %v was not generated", i)
}
return proof, nil
} }
return state.EncodeWitness(), nil return proof, nil
} }
func (p *CannonTraceProvider) loadProof(ctx context.Context, i uint64) (*proofData, error) { // loadProof will attempt to load or generate the proof data at the specified index
// If the requested index is beyond the end of the actual trace:
// - When the actual trace length is known, the proof data from the last step is returned with nil state
// - When the actual trace length is not yet know, the state from after the last step is returned with nil proofData
// and the actual trace length is cached for future runs
func (p *CannonTraceProvider) loadProof(ctx context.Context, i uint64) (*proofData, *mipsevm.State, error) {
if p.lastStep != 0 && i > p.lastStep {
// If the requested index is after the last step in the actual trace, use the last step
i = p.lastStep
}
path := filepath.Join(p.dir, proofsDir, fmt.Sprintf("%d.json", i)) path := filepath.Join(p.dir, proofsDir, fmt.Sprintf("%d.json", i))
file, err := os.Open(path) file, err := os.Open(path)
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
if err := p.generator.GenerateProof(ctx, p.dir, i); err != nil { if err := p.generator.GenerateProof(ctx, p.dir, i); err != nil {
return nil, fmt.Errorf("generate cannon trace with proof at %v: %w", i, err) return nil, nil, fmt.Errorf("generate cannon trace with proof at %v: %w", i, err)
} }
// Try opening the file again now and it should exist. // Try opening the file again now and it should exist.
file, err = os.Open(path) file, err = os.Open(path)
if errors.Is(err, os.ErrNotExist) {
// Expected proof wasn't generated, check if we reached the end of execution
state, err := parseState(filepath.Join(p.dir, finalState))
if err != nil {
return nil, nil, fmt.Errorf("cannot read final state: %w", err)
}
if state.Exited && state.Step < i {
p.logger.Warn("Requested proof was after the program exited", "proof", i, "last", state.Step)
// The final instruction has already been applied to this state, so the last step we can execute
// is one before its Step value.
p.lastStep = state.Step - 1
return nil, state, nil
} else {
return nil, nil, fmt.Errorf("expected proof not generated but final state was not exited, requested step %v, final state at step %v", i, state.Step)
}
}
} }
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot open proof file (%v): %w", path, err) return nil, nil, fmt.Errorf("cannot open proof file (%v): %w", path, err)
} }
defer file.Close() defer file.Close()
var proof proofData var proof proofData
err = json.NewDecoder(file).Decode(&proof) err = json.NewDecoder(file).Decode(&proof)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read proof (%v): %w", path, err) return nil, nil, fmt.Errorf("failed to read proof (%v): %w", path, err)
} }
return &proof, nil return &proof, nil, nil
} }
...@@ -4,12 +4,17 @@ import ( ...@@ -4,12 +4,17 @@ import (
"context" "context"
"embed" "embed"
_ "embed" _ "embed"
"encoding/json"
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -19,29 +24,35 @@ var testData embed.FS ...@@ -19,29 +24,35 @@ var testData embed.FS
func TestGet(t *testing.T) { func TestGet(t *testing.T) {
dataDir, prestate := setupTestData(t) dataDir, prestate := setupTestData(t)
t.Run("ExistingProof", func(t *testing.T) { t.Run("ExistingProof", func(t *testing.T) {
provider, generator := setupWithTestData(dataDir, prestate) provider, generator := setupWithTestData(t, dataDir, prestate)
value, err := provider.Get(context.Background(), 0) value, err := provider.Get(context.Background(), 0)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, common.HexToHash("0x45fd9aa59768331c726e719e76aa343e73123af888804604785ae19506e65e87"), value) require.Equal(t, common.HexToHash("0x45fd9aa59768331c726e719e76aa343e73123af888804604785ae19506e65e87"), value)
require.Empty(t, generator.generated) require.Empty(t, generator.generated)
}) })
t.Run("ProofUnavailable", func(t *testing.T) { t.Run("ProofAfterEndOfTrace", func(t *testing.T) {
provider, generator := setupWithTestData(dataDir, prestate) provider, generator := setupWithTestData(t, dataDir, prestate)
_, err := provider.Get(context.Background(), 7) generator.finalState = &mipsevm.State{
require.ErrorIs(t, err, os.ErrNotExist) Memory: &mipsevm.Memory{},
require.Contains(t, generator.generated, 7, "should have tried to generate the proof") Step: 10,
Exited: true,
}
value, err := provider.Get(context.Background(), 7000)
require.NoError(t, err)
require.Contains(t, generator.generated, 7000, "should have tried to generate the proof")
require.Equal(t, crypto.Keccak256Hash(generator.finalState.EncodeWitness()), value)
}) })
t.Run("MissingPostHash", func(t *testing.T) { t.Run("MissingPostHash", func(t *testing.T) {
provider, generator := setupWithTestData(dataDir, prestate) provider, generator := setupWithTestData(t, dataDir, prestate)
_, err := provider.Get(context.Background(), 1) _, err := provider.Get(context.Background(), 1)
require.ErrorContains(t, err, "missing post hash") require.ErrorContains(t, err, "missing post hash")
require.Empty(t, generator.generated) require.Empty(t, generator.generated)
}) })
t.Run("IgnoreUnknownFields", func(t *testing.T) { t.Run("IgnoreUnknownFields", func(t *testing.T) {
provider, generator := setupWithTestData(dataDir, prestate) provider, generator := setupWithTestData(t, dataDir, prestate)
value, err := provider.Get(context.Background(), 2) value, err := provider.Get(context.Background(), 2)
require.NoError(t, err) require.NoError(t, err)
expected := common.HexToHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") expected := common.HexToHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
...@@ -53,7 +64,7 @@ func TestGet(t *testing.T) { ...@@ -53,7 +64,7 @@ func TestGet(t *testing.T) {
func TestGetOracleData(t *testing.T) { func TestGetOracleData(t *testing.T) {
dataDir, prestate := setupTestData(t) dataDir, prestate := setupTestData(t)
t.Run("ExistingProof", func(t *testing.T) { t.Run("ExistingProof", func(t *testing.T) {
provider, generator := setupWithTestData(dataDir, prestate) provider, generator := setupWithTestData(t, dataDir, prestate)
oracleData, err := provider.GetOracleData(context.Background(), 420) oracleData, err := provider.GetOracleData(context.Background(), 420)
require.NoError(t, err) require.NoError(t, err)
require.False(t, oracleData.IsLocal) require.False(t, oracleData.IsLocal)
...@@ -64,15 +75,32 @@ func TestGetOracleData(t *testing.T) { ...@@ -64,15 +75,32 @@ func TestGetOracleData(t *testing.T) {
require.Empty(t, generator.generated) require.Empty(t, generator.generated)
}) })
t.Run("ProofUnavailable", func(t *testing.T) { t.Run("ProofAfterEndOfTrace", func(t *testing.T) {
provider, generator := setupWithTestData(dataDir, prestate) provider, generator := setupWithTestData(t, dataDir, prestate)
_, err := provider.GetOracleData(context.Background(), 7) generator.finalState = &mipsevm.State{
require.ErrorIs(t, err, os.ErrNotExist) Memory: &mipsevm.Memory{},
require.Contains(t, generator.generated, 7, "should have tried to generate the proof") Step: 10,
Exited: true,
}
generator.proof = &proofData{
ClaimValue: common.Hash{0xaa}.Bytes(),
StateData: []byte{0xbb},
ProofData: []byte{0xcc},
OracleKey: common.Hash{0xdd}.Bytes(),
OracleValue: []byte{0xdd},
OracleOffset: 10,
}
oracleData, err := provider.GetOracleData(context.Background(), 7000)
require.NoError(t, err)
require.Contains(t, generator.generated, 7000, "should have tried to generate the proof")
require.Contains(t, generator.generated, 9, "should have regenerated proof from last step")
require.False(t, oracleData.IsLocal)
require.EqualValues(t, generator.proof.OracleKey, oracleData.OracleKey)
require.EqualValues(t, generator.proof.OracleValue, oracleData.OracleData)
}) })
t.Run("IgnoreUnknownFields", func(t *testing.T) { t.Run("IgnoreUnknownFields", func(t *testing.T) {
provider, generator := setupWithTestData(dataDir, prestate) provider, generator := setupWithTestData(t, dataDir, prestate)
oracleData, err := provider.GetOracleData(context.Background(), 421) oracleData, err := provider.GetOracleData(context.Background(), 421)
require.NoError(t, err) require.NoError(t, err)
require.False(t, oracleData.IsLocal) require.False(t, oracleData.IsLocal)
...@@ -87,7 +115,7 @@ func TestGetOracleData(t *testing.T) { ...@@ -87,7 +115,7 @@ func TestGetOracleData(t *testing.T) {
func TestGetPreimage(t *testing.T) { func TestGetPreimage(t *testing.T) {
dataDir, prestate := setupTestData(t) dataDir, prestate := setupTestData(t)
t.Run("ExistingProof", func(t *testing.T) { t.Run("ExistingProof", func(t *testing.T) {
provider, generator := setupWithTestData(dataDir, prestate) provider, generator := setupWithTestData(t, dataDir, prestate)
value, proof, err := provider.GetPreimage(context.Background(), 0) value, proof, err := provider.GetPreimage(context.Background(), 0)
require.NoError(t, err) require.NoError(t, err)
expected := common.Hex2Bytes("b8f068de604c85ea0e2acd437cdb47add074a2d70b81d018390c504b71fe26f400000000000000000000000000000000000000000000000000000000000000000000000000") expected := common.Hex2Bytes("b8f068de604c85ea0e2acd437cdb47add074a2d70b81d018390c504b71fe26f400000000000000000000000000000000000000000000000000000000000000000000000000")
...@@ -97,22 +125,38 @@ func TestGetPreimage(t *testing.T) { ...@@ -97,22 +125,38 @@ func TestGetPreimage(t *testing.T) {
require.Empty(t, generator.generated) require.Empty(t, generator.generated)
}) })
t.Run("ProofUnavailable", func(t *testing.T) { t.Run("ProofAfterEndOfTrace", func(t *testing.T) {
provider, generator := setupWithTestData(dataDir, prestate) provider, generator := setupWithTestData(t, dataDir, prestate)
_, _, err := provider.GetPreimage(context.Background(), 7) generator.finalState = &mipsevm.State{
require.ErrorIs(t, err, os.ErrNotExist) Memory: &mipsevm.Memory{},
require.Contains(t, generator.generated, 7, "should have tried to generate the proof") Step: 10,
Exited: true,
}
generator.proof = &proofData{
ClaimValue: common.Hash{0xaa}.Bytes(),
StateData: []byte{0xbb},
ProofData: []byte{0xcc},
OracleKey: common.Hash{0xdd}.Bytes(),
OracleValue: []byte{0xdd},
OracleOffset: 10,
}
preimage, proof, err := provider.GetPreimage(context.Background(), 7000)
require.NoError(t, err)
require.Contains(t, generator.generated, 7000, "should have tried to generate the proof")
require.Contains(t, generator.generated, 9, "should have regenerated proof from last step")
require.EqualValues(t, generator.proof.StateData, preimage)
require.EqualValues(t, generator.proof.ProofData, proof)
}) })
t.Run("MissingStateData", func(t *testing.T) { t.Run("MissingStateData", func(t *testing.T) {
provider, generator := setupWithTestData(dataDir, prestate) provider, generator := setupWithTestData(t, dataDir, prestate)
_, _, err := provider.GetPreimage(context.Background(), 1) _, _, err := provider.GetPreimage(context.Background(), 1)
require.ErrorContains(t, err, "missing state data") require.ErrorContains(t, err, "missing state data")
require.Empty(t, generator.generated) require.Empty(t, generator.generated)
}) })
t.Run("IgnoreUnknownFields", func(t *testing.T) { t.Run("IgnoreUnknownFields", func(t *testing.T) {
provider, generator := setupWithTestData(dataDir, prestate) provider, generator := setupWithTestData(t, dataDir, prestate)
value, proof, err := provider.GetPreimage(context.Background(), 2) value, proof, err := provider.GetPreimage(context.Background(), 2)
require.NoError(t, err) require.NoError(t, err)
expected := common.Hex2Bytes("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc") expected := common.Hex2Bytes("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc")
...@@ -130,21 +174,21 @@ func TestAbsolutePreState(t *testing.T) { ...@@ -130,21 +174,21 @@ func TestAbsolutePreState(t *testing.T) {
prestate := "state.json" prestate := "state.json"
t.Run("StateUnavailable", func(t *testing.T) { t.Run("StateUnavailable", func(t *testing.T) {
provider, _ := setupWithTestData("/dir/does/not/exist", prestate) provider, _ := setupWithTestData(t, "/dir/does/not/exist", prestate)
_, err := provider.AbsolutePreState(context.Background()) _, err := provider.AbsolutePreState(context.Background())
require.ErrorIs(t, err, os.ErrNotExist) require.ErrorIs(t, err, os.ErrNotExist)
}) })
t.Run("InvalidStateFile", func(t *testing.T) { t.Run("InvalidStateFile", func(t *testing.T) {
setupPreState(t, dataDir, "invalid.json") setupPreState(t, dataDir, "invalid.json")
provider, _ := setupWithTestData(dataDir, prestate) provider, _ := setupWithTestData(t, dataDir, prestate)
_, err := provider.AbsolutePreState(context.Background()) _, err := provider.AbsolutePreState(context.Background())
require.ErrorContains(t, err, "invalid mipsevm state") require.ErrorContains(t, err, "invalid mipsevm state")
}) })
t.Run("ExpectedAbsolutePreState", func(t *testing.T) { t.Run("ExpectedAbsolutePreState", func(t *testing.T) {
setupPreState(t, dataDir, "state.json") setupPreState(t, dataDir, "state.json")
provider, _ := setupWithTestData(dataDir, prestate) provider, _ := setupWithTestData(t, dataDir, prestate)
preState, err := provider.AbsolutePreState(context.Background()) preState, err := provider.AbsolutePreState(context.Background())
require.NoError(t, err) require.NoError(t, err)
state := mipsevm.State{ state := mipsevm.State{
...@@ -190,9 +234,10 @@ func setupTestData(t *testing.T) (string, string) { ...@@ -190,9 +234,10 @@ func setupTestData(t *testing.T) (string, string) {
return dataDir, "state.json" return dataDir, "state.json"
} }
func setupWithTestData(dataDir string, prestate string) (*CannonTraceProvider, *stubGenerator) { func setupWithTestData(t *testing.T, dataDir string, prestate string) (*CannonTraceProvider, *stubGenerator) {
generator := &stubGenerator{} generator := &stubGenerator{}
return &CannonTraceProvider{ return &CannonTraceProvider{
logger: testlog.Logger(t, log.LvlInfo),
dir: dataDir, dir: dataDir,
generator: generator, generator: generator,
prestate: prestate, prestate: prestate,
...@@ -200,10 +245,28 @@ func setupWithTestData(dataDir string, prestate string) (*CannonTraceProvider, * ...@@ -200,10 +245,28 @@ func setupWithTestData(dataDir string, prestate string) (*CannonTraceProvider, *
} }
type stubGenerator struct { type stubGenerator struct {
generated []int // Using int makes assertions easier generated []int // Using int makes assertions easier
finalState *mipsevm.State
proof *proofData
} }
func (e *stubGenerator) GenerateProof(ctx context.Context, dir string, i uint64) error { func (e *stubGenerator) GenerateProof(ctx context.Context, dir string, i uint64) error {
e.generated = append(e.generated, int(i)) e.generated = append(e.generated, int(i))
if e.finalState != nil && e.finalState.Step <= i {
// Requesting a trace index past the end of the trace
data, err := json.Marshal(e.finalState)
if err != nil {
return err
}
return os.WriteFile(filepath.Join(dir, finalState), data, 0644)
}
if e.proof != nil {
proofFile := filepath.Join(dir, proofsDir, fmt.Sprintf("%d.json", i))
data, err := json.Marshal(e.proof)
if err != nil {
return err
}
return os.WriteFile(proofFile, data, 0644)
}
return nil return nil
} }
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
openum "github.com/ethereum-optimism/optimism/op-service/enum" openum "github.com/ethereum-optimism/optimism/op-service/enum"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
...@@ -58,6 +59,21 @@ var ( ...@@ -58,6 +59,21 @@ var (
Usage: "Correct Alphabet Trace (alphabet trace type only)", Usage: "Correct Alphabet Trace (alphabet trace type only)",
EnvVars: prefixEnvVars("ALPHABET"), EnvVars: prefixEnvVars("ALPHABET"),
} }
CannonNetworkFlag = &cli.StringFlag{
Name: "cannon-network",
Usage: fmt.Sprintf("Predefined network selection. Available networks: %s (cannon trace type only)", strings.Join(chaincfg.AvailableNetworks(), ", ")),
EnvVars: prefixEnvVars("CANNON_NETWORK"),
}
CannonRollupConfigFlag = &cli.StringFlag{
Name: "cannon-rollup-config",
Usage: "Rollup chain parameters (cannon trace type only)",
EnvVars: prefixEnvVars("CANNON_ROLLUP_CONFIG"),
}
CannonL2GenesisFlag = &cli.StringFlag{
Name: "cannon-l2-genesis",
Usage: "Path to the op-geth genesis file (cannon trace type only)",
EnvVars: prefixEnvVars("CANNON_L2_GENESIS"),
}
CannonBinFlag = &cli.StringFlag{ CannonBinFlag = &cli.StringFlag{
Name: "cannon-bin", Name: "cannon-bin",
Usage: "Path to cannon executable to use when generating trace data (cannon trace type only)", Usage: "Path to cannon executable to use when generating trace data (cannon trace type only)",
...@@ -103,6 +119,9 @@ var requiredFlags = []cli.Flag{ ...@@ -103,6 +119,9 @@ var requiredFlags = []cli.Flag{
// optionalFlags is a list of unchecked cli flags // optionalFlags is a list of unchecked cli flags
var optionalFlags = []cli.Flag{ var optionalFlags = []cli.Flag{
AlphabetFlag, AlphabetFlag,
CannonNetworkFlag,
CannonRollupConfigFlag,
CannonL2GenesisFlag,
CannonBinFlag, CannonBinFlag,
CannonServerFlag, CannonServerFlag,
CannonPreStateFlag, CannonPreStateFlag,
...@@ -130,6 +149,14 @@ func CheckRequired(ctx *cli.Context) error { ...@@ -130,6 +149,14 @@ func CheckRequired(ctx *cli.Context) error {
gameType := config.TraceType(strings.ToLower(ctx.String(TraceTypeFlag.Name))) gameType := config.TraceType(strings.ToLower(ctx.String(TraceTypeFlag.Name)))
switch gameType { switch gameType {
case config.TraceTypeCannon: case config.TraceTypeCannon:
if !ctx.IsSet(CannonNetworkFlag.Name) && !(ctx.IsSet(CannonRollupConfigFlag.Name) && ctx.IsSet(CannonL2GenesisFlag.Name)) {
return fmt.Errorf("flag %v or %v and %v is required",
CannonNetworkFlag.Name, CannonRollupConfigFlag.Name, CannonL2GenesisFlag.Name)
}
if ctx.IsSet(CannonNetworkFlag.Name) && (ctx.IsSet(CannonRollupConfigFlag.Name) || ctx.IsSet(CannonL2GenesisFlag.Name)) {
return fmt.Errorf("flag %v can not be used with %v and %v",
CannonNetworkFlag.Name, CannonRollupConfigFlag.Name, CannonL2GenesisFlag.Name)
}
if !ctx.IsSet(CannonBinFlag.Name) { if !ctx.IsSet(CannonBinFlag.Name) {
return fmt.Errorf("flag %s is required", CannonBinFlag.Name) return fmt.Errorf("flag %s is required", CannonBinFlag.Name)
} }
...@@ -175,6 +202,9 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) { ...@@ -175,6 +202,9 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) {
TraceType: traceTypeFlag, TraceType: traceTypeFlag,
GameAddress: dgfAddress, GameAddress: dgfAddress,
AlphabetTrace: ctx.String(AlphabetFlag.Name), AlphabetTrace: ctx.String(AlphabetFlag.Name),
CannonNetwork: ctx.String(CannonNetworkFlag.Name),
CannonRollupConfigPath: ctx.String(CannonRollupConfigFlag.Name),
CannonL2GenesisPath: ctx.String(CannonL2GenesisFlag.Name),
CannonBin: ctx.String(CannonBinFlag.Name), CannonBin: ctx.String(CannonBinFlag.Name),
CannonServer: ctx.String(CannonServerFlag.Name), CannonServer: ctx.String(CannonServerFlag.Name),
CannonAbsolutePreState: ctx.String(CannonPreStateFlag.Name), CannonAbsolutePreState: ctx.String(CannonPreStateFlag.Name),
......
#!/bin/bash
set -euo pipefail
DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
cd "$DIR"
make
cd ..
make devnet-clean
make devnet-up-deploy
DEVNET_SPONSOR="ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
DISPUTE_GAME_PROXY="0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e"
CHARLIE_ADDRESS="0xF45B7537828CB2fffBC69996B054c2Aaf36DC778"
CHARLIE_KEY="74feb147d72bfae943e6b4e483410933d9e447d5dc47d52432dcc2c1454dabb7"
MALLORY_ADDRESS="0x4641c704a6c743f73ee1f36C7568Fbf4b80681e4"
MALLORY_KEY="28d7045146193f5f4eeb151c4843544b1b0d30a7ac1680c845a416fac65a7715"
echo "----------------------------------------------------------------"
echo " - Fetching balance of the sponsor"
echo " - Balance: $(cast balance 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266)"
echo "----------------------------------------------------------------"
echo "Funding Charlie"
cast send $CHARLIE_ADDRESS --value 5ether --private-key $DEVNET_SPONSOR
echo "Funding Mallory"
cast send $MALLORY_ADDRESS --value 5ether --private-key $DEVNET_SPONSOR
# Fault game type = 0
GAME_TYPE=0
# Root claim commits to the entire trace.
# Alphabet game claim construction: keccak256(abi.encode(trace_index, trace[trace_index]))
ROOT_CLAIM=$(cast keccak $(cast abi-encode "f(uint256,uint256)" 15 122))
# Extra data is a dynamic `bytes` type that contains the L2 Block Number of the output proposal that the root claim disagrees with
# Doesn't matter right now since we're not deleting outputs, so just set it to 1
EXTRA_DATA=$(cast to-bytes32 1)
echo "Initializing the game"
cast call --private-key $MALLORY_KEY $DISPUTE_GAME_PROXY "create(uint8,bytes32,bytes)" $GAME_TYPE $ROOT_CLAIM $EXTRA_DATA
cast send --private-key $MALLORY_KEY $DISPUTE_GAME_PROXY "create(uint8,bytes32,bytes)" $GAME_TYPE $ROOT_CLAIM $EXTRA_DATA
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd) SOURCE_DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
cd "$DIR" CHALLENGER_DIR=$(echo ${SOURCE_DIR%/*/*})
DISPUTE_GAME_PROXY="0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" # Check that the fault game address file exists
FAULT_GAME_ADDR_FILE="$CHALLENGER_DIR/.fault-game-address"
if [[ ! -f "$FAULT_GAME_ADDR_FILE" ]]; then
echo "Game not initialized, exiting..."
exit 1
fi
CHARLIE_ADDRESS="0xF45B7537828CB2fffBC69996B054c2Aaf36DC778" # Charlie's Address: 0xF45B7537828CB2fffBC69996B054c2Aaf36DC778
CHARLIE_KEY="74feb147d72bfae943e6b4e483410933d9e447d5dc47d52432dcc2c1454dabb7" CHARLIE_KEY="74feb147d72bfae943e6b4e483410933d9e447d5dc47d52432dcc2c1454dabb7"
MALLORY_ADDRESS="0x4641c704a6c743f73ee1f36C7568Fbf4b80681e4" FAULT_GAME_ADDRESS=$(cat $FAULT_GAME_ADDR_FILE)
MALLORY_KEY="28d7045146193f5f4eeb151c4843544b1b0d30a7ac1680c845a416fac65a7715" echo "Fault dispute game address: $FAULT_GAME_ADDRESS"
FAULT_GAME_ADDRESS="0x8daf17a20c9dba35f005b6324f493785d239719d" $CHALLENGER_DIR/bin/op-challenger \
PREIMAGE_ORACLE_ADDRESS="0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"
./bin/op-challenger \
--l1-eth-rpc http://localhost:8545 \ --l1-eth-rpc http://localhost:8545 \
--trace-type="alphabet" \ --trace-type="alphabet" \
--alphabet "abcdefgh" \ --alphabet "abcdefgh" \
--game-address $FAULT_GAME_ADDRESS \ --game-address $FAULT_GAME_ADDRESS \
--preimage-oracle-address $PREIMAGE_ORACLE_ADDRESS \
--private-key $CHARLIE_KEY \ --private-key $CHARLIE_KEY \
--num-confirmations 1 \ --num-confirmations 1 \
--game-depth 4 \ --game-depth 4 \
......
#!/bin/bash
set -euo pipefail
SOURCE_DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
CHALLENGER_DIR=$(echo ${SOURCE_DIR%/*/*})
MONOREPO_DIR=$(echo ${SOURCE_DIR%/*/*/*})
cd $CHALLENGER_DIR
make
cd $MONOREPO_DIR
make devnet-clean
make cannon-prestate
make devnet-up
DEVNET_SPONSOR="ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
DISPUTE_GAME_PROXY=$(jq -r .DisputeGameFactoryProxy $MONOREPO_DIR/.devnet/addresses.json)
echo "----------------------------------------------------------------"
echo " Dispute Game Factory at $DISPUTE_GAME_PROXY"
echo "----------------------------------------------------------------"
L2_OUTPUT_ORACLE_PROXY=$(jq -r .L2OutputOracleProxy $MONOREPO_DIR/.devnet/addresses.json)
echo "----------------------------------------------------------------"
echo " L2 Output Oracle Proxy at $L2_OUTPUT_ORACLE_PROXY"
echo "----------------------------------------------------------------"
BLOCK_ORACLE_PROXY=$(jq -r .BlockOracle $MONOREPO_DIR/.devnet/addresses.json)
echo "----------------------------------------------------------------"
echo " Block Oracle Proxy at $BLOCK_ORACLE_PROXY"
echo "----------------------------------------------------------------"
CHARLIE_ADDRESS="0xF45B7537828CB2fffBC69996B054c2Aaf36DC778"
CHARLIE_KEY="74feb147d72bfae943e6b4e483410933d9e447d5dc47d52432dcc2c1454dabb7"
MALLORY_ADDRESS="0x4641c704a6c743f73ee1f36C7568Fbf4b80681e4"
MALLORY_KEY="28d7045146193f5f4eeb151c4843544b1b0d30a7ac1680c845a416fac65a7715"
echo "----------------------------------------------------------------"
echo " - Fetching balance of the sponsor"
echo " - Balance: $(cast balance 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266)"
echo "----------------------------------------------------------------"
echo "Funding Charlie"
cast send $CHARLIE_ADDRESS --value 5ether --private-key $DEVNET_SPONSOR
echo "Funding Mallory"
cast send $MALLORY_ADDRESS --value 5ether --private-key $DEVNET_SPONSOR
# Loop and wait until there are at least 2 outputs in the l2 output oracle
echo "Waiting until 2 output proposals are in the l2 output oracle..."
echo "NOTE: This may show errors if no output proposals are in the oracle yet."
while [[ $(cast call $L2_OUTPUT_ORACLE_PROXY "latestOutputIndex()" | cast to-dec) -lt 2 ]]
do
echo "[BLOCK: $(cast block-number)] Waiting for output proposals..."
sleep 2
done
# Fetch the latest block number
L2_BLOCK_NUMBER=$(cast call $L2_OUTPUT_ORACLE_PROXY "latestBlockNumber()")
echo "Using the latest L2OO block number: $L2_BLOCK_NUMBER"
# We will use the l2 block number of 1 for the dispute game.
# We need to check that the block oracle contains the corresponding l1 block number.
echo "Checkpointing the block oracle..."
L1_CHECKPOINT=$(cast send --private-key $DEVNET_SPONSOR $BLOCK_ORACLE_PROXY "checkpoint()" --json | jq -r .blockNumber | cast to-dec)
((L1_CHECKPOINT=L1_CHECKPOINT-1))
echo "L1 Checkpoint: $L1_CHECKPOINT"
INDEX=$(cast call $L2_OUTPUT_ORACLE_PROXY "getL2OutputIndexAfter(uint256)" $L2_BLOCK_NUMBER | cast to-dec)
((PRIOR_INDEX=INDEX-1))
echo "Getting the l2 output at index $PRIOR_INDEX"
cast call $L2_OUTPUT_ORACLE_PROXY "getL2Output(uint256)" $PRIOR_INDEX
echo "Getting the l2 output at index $INDEX"
cast call $L2_OUTPUT_ORACLE_PROXY "getL2Output(uint256)" $INDEX
# (Alphabet) Fault game type = 0
GAME_TYPE=0
# Root claim commits to the entire trace.
# Alphabet game claim construction: keccak256(abi.encode(trace_index, trace[trace_index]))
ROOT_CLAIM=$(cast keccak $(cast abi-encode "f(uint256,uint256)" 15 122))
# Fault dispute game extra data is calculated as follows.
# abi.encode(uint256(l2_block_number), uint256(l1 checkpoint))
EXTRA_DATA=$(cast abi-encode "f(uint256,uint256)" $L2_BLOCK_NUMBER $L1_CHECKPOINT)
echo "Initializing the game"
FAULT_GAME_ADDRESS=$(cast call --private-key $MALLORY_KEY $DISPUTE_GAME_PROXY "create(uint8,bytes32,bytes)" $GAME_TYPE $ROOT_CLAIM $EXTRA_DATA)
echo "Creating game at address $FAULT_GAME_ADDRESS"
cast send --private-key $MALLORY_KEY $DISPUTE_GAME_PROXY "create(uint8,bytes32,bytes)" $GAME_TYPE $ROOT_CLAIM $EXTRA_DATA
FORMATTED_ADDRESS=$(cast parse-bytes32-address $FAULT_GAME_ADDRESS)
echo "Formatted Address: $FORMATTED_ADDRESS"
echo $FORMATTED_ADDRESS > $CHALLENGER_DIR/.fault-game-address
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd) SOURCE_DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
cd "$DIR" CHALLENGER_DIR=$(echo ${SOURCE_DIR%/*/*})
DISPUTE_GAME_PROXY="0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" # Check that the fault game address file exists
FAULT_GAME_ADDR_FILE="$CHALLENGER_DIR/.fault-game-address"
if [[ ! -f "$FAULT_GAME_ADDR_FILE" ]]; then
echo "Game not initialized, exiting..."
exit 1
fi
CHARLIE_ADDRESS="0xF45B7537828CB2fffBC69996B054c2Aaf36DC778" # Mallory's Address: 0x4641c704a6c743f73ee1f36C7568Fbf4b80681e4
CHARLIE_KEY="74feb147d72bfae943e6b4e483410933d9e447d5dc47d52432dcc2c1454dabb7"
MALLORY_ADDRESS="0x4641c704a6c743f73ee1f36C7568Fbf4b80681e4"
MALLORY_KEY="28d7045146193f5f4eeb151c4843544b1b0d30a7ac1680c845a416fac65a7715" MALLORY_KEY="28d7045146193f5f4eeb151c4843544b1b0d30a7ac1680c845a416fac65a7715"
FAULT_GAME_ADDRESS="0x8daf17a20c9dba35f005b6324f493785d239719d" FAULT_GAME_ADDRESS=$(cat $FAULT_GAME_ADDR_FILE)
echo "Fault dispute game address: $FAULT_GAME_ADDRESS"
PREIMAGE_ORACLE_ADDRESS="0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"
./bin/op-challenger \ $CHALLENGER_DIR/bin/op-challenger \
--l1-eth-rpc http://localhost:8545 \ --l1-eth-rpc http://localhost:8545 \
--trace-type="alphabet" \ --trace-type="alphabet" \
--alphabet "abcdexyz" \ --alphabet "abcdexyz" \
--game-address $FAULT_GAME_ADDRESS \ --game-address $FAULT_GAME_ADDRESS \
--preimage-oracle-address $PREIMAGE_ORACLE_ADDRESS \
--private-key $MALLORY_KEY \ --private-key $MALLORY_KEY \
--num-confirmations 1 \ --num-confirmations 1 \
--game-depth 4 \ --game-depth 4 \
......
#!/bin/bash
# set -x
trap killgroup SIGINT
killgroup(){
echo killing...
kill 0
}
$1 | sed "s/^/[$2] /" &
$3 | sed "s/^/[$4] /" &
wait
#!/bin/bash
set -euo pipefail
if [ $# -eq 0 ]
then
echo "Missing Fault Dispute Game address argument"
fi
echo ""
echo "Visualize the fault dispute game at https://dispute.clab.by/game?addr=$1"
echo ""
DISPUTE_GAME_PROXY=$(jq .DisputeGameFactoryProxy .devnet/addresses.json)
DISPUTE_GAME_PROXY=$(echo $DISPUTE_GAME_PROXY | tr -d '"')
echo "----------------------------------------------------------------"
echo " Dispute Game Factory at $DISPUTE_GAME_PROXY"
echo "----------------------------------------------------------------"
FAULT_GAME_ADDRESS=$1
DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
DIR=$(echo ${DIR%/*/*})
cd $DIR/packages/contracts-bedrock
forge script scripts/FaultDisputeGameViz.s.sol \
--sig "remote(address)" $FAULT_GAME_ADDRESS \
--fork-url http://localhost:8545
mv dispute_game.svg "$dir"
#!/bin/bash
set -euo pipefail
DISPUTE_GAME_PROXY="0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e"
FAULT_GAME_ADDRESS="0x8daf17a20c9dba35f005b6324f493785d239719d"
dir=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
cd "$dir"
cd ../packages/contracts-bedrock
forge script scripts/FaultDisputeGameViz.s.sol --sig "remote(address)" $FAULT_GAME_ADDRESS --fork-url http://localhost:8545
mv dispute_game.svg "$dir"
...@@ -2,16 +2,21 @@ package disputegame ...@@ -2,16 +2,21 @@ package disputegame
import ( import (
"context" "context"
"encoding/json"
"os"
"path/filepath"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum/go-ethereum/core"
) )
type CannonGameHelper struct { type CannonGameHelper struct {
FaultGameHelper FaultGameHelper
} }
func (g *CannonGameHelper) StartChallenger(ctx context.Context, l1Endpoint string, l2Endpoint string, name string, options ...challenger.Option) *challenger.Helper { func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Endpoint string, l2Endpoint string, name string, options ...challenger.Option) *challenger.Helper {
opts := []challenger.Option{ opts := []challenger.Option{
func(c *config.Config) { func(c *config.Config) {
c.GameAddress = g.addr c.GameAddress = g.addr
...@@ -24,6 +29,18 @@ func (g *CannonGameHelper) StartChallenger(ctx context.Context, l1Endpoint strin ...@@ -24,6 +29,18 @@ func (g *CannonGameHelper) StartChallenger(ctx context.Context, l1Endpoint strin
c.CannonServer = "../op-program/bin/op-program" c.CannonServer = "../op-program/bin/op-program"
c.CannonAbsolutePreState = "../op-program/bin/prestate.json" c.CannonAbsolutePreState = "../op-program/bin/prestate.json"
c.CannonSnapshotFreq = config.DefaultCannonSnapshotFreq c.CannonSnapshotFreq = config.DefaultCannonSnapshotFreq
genesisBytes, err := json.Marshal(l2Genesis)
g.require.NoError(err, "marshall l2 genesis config")
genesisFile := filepath.Join(c.CannonDatadir, "l2-genesis.json")
g.require.NoError(os.WriteFile(genesisFile, genesisBytes, 0644))
c.CannonL2GenesisPath = genesisFile
rollupBytes, err := json.Marshal(rollupCfg)
g.require.NoError(err, "marshall rollup config")
rollupFile := filepath.Join(c.CannonDatadir, "rollup.json")
g.require.NoError(os.WriteFile(rollupFile, rollupBytes, 0644))
c.CannonRollupConfigPath = rollupFile
}, },
} }
opts = append(opts, options...) opts = append(opts, options...)
......
...@@ -33,7 +33,7 @@ func (g *FaultGameHelper) GameDuration(ctx context.Context) time.Duration { ...@@ -33,7 +33,7 @@ func (g *FaultGameHelper) GameDuration(ctx context.Context) time.Duration {
} }
func (g *FaultGameHelper) WaitForClaimCount(ctx context.Context, count int64) { func (g *FaultGameHelper) WaitForClaimCount(ctx context.Context, count int64) {
ctx, cancel := context.WithTimeout(ctx, time.Minute) ctx, cancel := context.WithTimeout(ctx, 2*time.Minute)
defer cancel() defer cancel()
err := utils.WaitFor(ctx, time.Second, func() (bool, error) { err := utils.WaitFor(ctx, time.Second, func() (bool, error) {
actual, err := g.game.ClaimDataLen(&bind.CallOpts{Context: ctx}) actual, err := g.game.ClaimDataLen(&bind.CallOpts{Context: ctx})
......
...@@ -145,7 +145,6 @@ func TestChallengerCompleteDisputeGame(t *testing.T) { ...@@ -145,7 +145,6 @@ func TestChallengerCompleteDisputeGame(t *testing.T) {
} }
func TestCannonDisputeGame(t *testing.T) { func TestCannonDisputeGame(t *testing.T) {
t.Skip("CLI-4290: op-challenger doesn't handle trace extension correctly for cannon")
InitParallel(t) InitParallel(t)
ctx := context.Background() ctx := context.Background()
...@@ -156,7 +155,7 @@ func TestCannonDisputeGame(t *testing.T) { ...@@ -156,7 +155,7 @@ func TestCannonDisputeGame(t *testing.T) {
game := disputeGameFactory.StartCannonGame(ctx, common.Hash{0xaa}) game := disputeGameFactory.StartCannonGame(ctx, common.Hash{0xaa})
require.NotNil(t, game) require.NotNil(t, game)
game.StartChallenger(ctx, sys.NodeEndpoint("l1"), sys.NodeEndpoint("sequencer"), "Challenger", func(c *config.Config) { game.StartChallenger(ctx, sys.RollupConfig, sys.L2GenesisCfg, sys.NodeEndpoint("l1"), sys.NodeEndpoint("sequencer"), "Challenger", func(c *config.Config) {
c.AgreeWithProposedOutput = true // Agree with the proposed output, so disagree with the root claim c.AgreeWithProposedOutput = true // Agree with the proposed output, so disagree with the root claim
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Alice) c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Alice)
}) })
...@@ -173,8 +172,10 @@ func TestCannonDisputeGame(t *testing.T) { ...@@ -173,8 +172,10 @@ func TestCannonDisputeGame(t *testing.T) {
func startFaultDisputeSystem(t *testing.T) (*System, *ethclient.Client) { func startFaultDisputeSystem(t *testing.T) (*System, *ethclient.Client) {
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
delete(cfg.Nodes, "verifier") delete(cfg.Nodes, "verifier")
cfg.DeployConfig.SequencerWindowSize = 4
cfg.DeployConfig.FinalizationPeriodSeconds = 2
cfg.SupportL1TimeTravel = true cfg.SupportL1TimeTravel = true
cfg.DeployConfig.L2OutputOracleSubmissionInterval = 2 cfg.DeployConfig.L2OutputOracleSubmissionInterval = 1
cfg.NonFinalizedProposals = true // Submit output proposals asap cfg.NonFinalizedProposals = true // Submit output proposals asap
sys, err := cfg.Start() sys, err := cfg.Start()
require.NoError(t, err, "Error starting up system") require.NoError(t, err, "Error starting up system")
......
...@@ -409,7 +409,7 @@ func TestMixedWithdrawalValidity(t *testing.T) { ...@@ -409,7 +409,7 @@ func TestMixedWithdrawalValidity(t *testing.T) {
l2Verif := sys.Clients["verifier"] l2Verif := sys.Clients["verifier"]
require.NoError(t, err) require.NoError(t, err)
systemConfig, err := bindings.NewSystemConfigCaller(cfg.L1Deployments.SystemConfig, l1Client) systemConfig, err := bindings.NewSystemConfigCaller(cfg.L1Deployments.SystemConfigProxy, l1Client)
require.NoError(t, err) require.NoError(t, err)
unsafeBlockSigner, err := systemConfig.UnsafeBlockSigner(nil) unsafeBlockSigner, err := systemConfig.UnsafeBlockSigner(nil)
require.NoError(t, err) require.NoError(t, err)
......
...@@ -18,21 +18,25 @@ import ( ...@@ -18,21 +18,25 @@ import (
const agreedBlockTrailingDistance = 100 const agreedBlockTrailingDistance = 100
func main() { func main() {
if len(os.Args) != 3 { if len(os.Args) < 3 {
_, _ = fmt.Fprintln(os.Stderr, "Must specify L1 RPC URL and L2 RPC URL as arguments") _, _ = fmt.Fprintln(os.Stderr, "Must specify L1 RPC URL and L2 RPC URL as arguments")
os.Exit(2) os.Exit(2)
} }
l1RpcUrl := os.Args[1] l1RpcUrl := os.Args[1]
l2RpcUrl := os.Args[2] l2RpcUrl := os.Args[2]
l1RpcKind := "alchemy"
if len(os.Args) > 3 {
l1RpcKind = os.Args[3]
}
goerliOutputAddress := common.HexToAddress("0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0") goerliOutputAddress := common.HexToAddress("0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0")
err := Run(l1RpcUrl, l2RpcUrl, goerliOutputAddress) err := Run(l1RpcUrl, l1RpcKind, l2RpcUrl, goerliOutputAddress)
if err != nil { if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Failed: %v\n", err.Error()) _, _ = fmt.Fprintf(os.Stderr, "Failed: %v\n", err.Error())
os.Exit(1) os.Exit(1)
} }
} }
func Run(l1RpcUrl string, l2RpcUrl string, l2OracleAddr common.Address) error { func Run(l1RpcUrl string, l1RpcKind string, l2RpcUrl string, l2OracleAddr common.Address) error {
ctx := context.Background() ctx := context.Background()
l1RpcClient, err := rpc.Dial(l1RpcUrl) l1RpcClient, err := rpc.Dial(l1RpcUrl)
if err != nil { if err != nil {
...@@ -150,7 +154,7 @@ func Run(l1RpcUrl string, l2RpcUrl string, l2OracleAddr common.Address) error { ...@@ -150,7 +154,7 @@ func Run(l1RpcUrl string, l2RpcUrl string, l2OracleAddr common.Address) error {
} }
fmt.Printf("Configuration: %s\n", args) fmt.Printf("Configuration: %s\n", args)
fmt.Println("Running in online mode") fmt.Println("Running in online mode")
err = runFaultProofProgram(ctx, append(args, "--l1", l1RpcUrl, "--l2", l2RpcUrl)) err = runFaultProofProgram(ctx, append(args, "--l1", l1RpcUrl, "--l2", l2RpcUrl, "--l1.rpckind", l1RpcKind))
if err != nil { if err != nil {
return fmt.Errorf("online mode failed: %w", err) return fmt.Errorf("online mode failed: %w", err)
} }
......
...@@ -11,10 +11,11 @@ RUN apt-get update && \ ...@@ -11,10 +11,11 @@ RUN apt-get update && \
chmod +x ./rustup.sh && \ chmod +x ./rustup.sh && \
./rustup.sh -y ./rustup.sh -y
COPY ./.foundryrc ./.foundryrc COPY ./.abigenrc ./.abigenrc
# Only diff from upstream docker image is this clone instead # Only diff from upstream docker image is this clone instead
# of COPY. We select a specific commit to use. # of COPY. We select a specific commit to use.
COPY ./.foundryrc ./.foundryrc
RUN git clone https://github.com/foundry-rs/foundry.git ./foundry \ RUN git clone https://github.com/foundry-rs/foundry.git ./foundry \
&& cd foundry && git checkout $(cat ../.foundryrc) && cd foundry && git checkout $(cat ../.foundryrc)
...@@ -39,7 +40,6 @@ ENV DEBIAN_FRONTEND=noninteractive ...@@ -39,7 +40,6 @@ ENV DEBIAN_FRONTEND=noninteractive
COPY --from=foundry-build /opt/foundry/target/release/forge /usr/local/bin/forge COPY --from=foundry-build /opt/foundry/target/release/forge /usr/local/bin/forge
COPY --from=foundry-build /opt/foundry/target/release/cast /usr/local/bin/cast COPY --from=foundry-build /opt/foundry/target/release/cast /usr/local/bin/cast
COPY --from=foundry-build /opt/foundry/target/release/anvil /usr/local/bin/anvil COPY --from=foundry-build /opt/foundry/target/release/anvil /usr/local/bin/anvil
COPY --from=geth /usr/local/bin/abigen /usr/local/bin/abigen
COPY --from=echidna-test /usr/local/bin/echidna-test /usr/local/bin/echidna-test COPY --from=echidna-test /usr/local/bin/echidna-test /usr/local/bin/echidna-test
RUN apt-get update && \ RUN apt-get update && \
...@@ -56,6 +56,9 @@ RUN apt-get update && \ ...@@ -56,6 +56,9 @@ RUN apt-get update && \
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.53.3 && \ curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.53.3 && \
curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/master/install.sh | bash curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/master/install.sh | bash
# Install the specific version of abigen from .abigenrc
RUN go install github.com/ethereum/go-ethereum/cmd/abigen@$(cat .abigenrc)
# We need to isntall yarn because a bespoke dependency installed from github https://codeload.github.com/Saw-mon-and-Natalie/clones-with-immutable-arg needs it # We need to isntall yarn because a bespoke dependency installed from github https://codeload.github.com/Saw-mon-and-Natalie/clones-with-immutable-arg needs it
# it is installed from github which means it's postpack script which uses yarn is ran when unpacked into node_modules # it is installed from github which means it's postpack script which uses yarn is ran when unpacked into node_modules
RUN echo "downloading pnpm and yarn" && npm i -g pnpm && npm i -g yarn@1 && pnpm --version && yarn --version RUN echo "downloading pnpm and yarn" && npm i -g pnpm && npm i -g yarn@1 && pnpm --version && yarn --version
......
This diff is collapsed.
...@@ -89,6 +89,7 @@ ...@@ -89,6 +89,7 @@
| batcherHash | bytes32 | 103 | 0 | 32 | src/L1/SystemConfig.sol:SystemConfig | | batcherHash | bytes32 | 103 | 0 | 32 | src/L1/SystemConfig.sol:SystemConfig |
| gasLimit | uint64 | 104 | 0 | 8 | src/L1/SystemConfig.sol:SystemConfig | | gasLimit | uint64 | 104 | 0 | 8 | src/L1/SystemConfig.sol:SystemConfig |
| _resourceConfig | struct ResourceMetering.ResourceConfig | 105 | 0 | 32 | src/L1/SystemConfig.sol:SystemConfig | | _resourceConfig | struct ResourceMetering.ResourceConfig | 105 | 0 | 32 | src/L1/SystemConfig.sol:SystemConfig |
| startBlock | uint256 | 106 | 0 | 32 | src/L1/SystemConfig.sol:SystemConfig |
======================= =======================
➡ src/legacy/DeployerWhitelist.sol:DeployerWhitelist ➡ src/legacy/DeployerWhitelist.sol:DeployerWhitelist
......
...@@ -45,5 +45,6 @@ ...@@ -45,5 +45,6 @@
"l2GenesisRegolithTimeOffset": "0x0", "l2GenesisRegolithTimeOffset": "0x0",
"faultGameAbsolutePrestate": "0x41c7ae758795765c6664a5d39bf63841c71ff191e9189522bad8ebff5d4eca98", "faultGameAbsolutePrestate": "0x41c7ae758795765c6664a5d39bf63841c71ff191e9189522bad8ebff5d4eca98",
"faultGameMaxDepth": 4, "faultGameMaxDepth": 4,
"faultGameMaxDuration": 300 "faultGameMaxDuration": 300,
"systemConfigStartBlock": 0
} }
# `SystemConfig` Invariants # `SystemConfig` Invariants
## The gas limit of the `SystemConfig` contract can never be lower than the hard-coded lower bound. ## The gas limit of the `SystemConfig` contract can never be lower than the hard-coded lower bound.
**Test:** [`SystemConfig.t.sol#L44`](../test/invariants/SystemConfig.t.sol#L44) **Test:** [`SystemConfig.t.sol#L65`](../test/invariants/SystemConfig.t.sol#L65)
...@@ -44,6 +44,6 @@ ...@@ -44,6 +44,6 @@
"solhint": "^3.4.1", "solhint": "^3.4.1",
"solhint-plugin-prettier": "^0.0.5", "solhint-plugin-prettier": "^0.0.5",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^4.9.3" "typescript": "^5.1.6"
} }
} }
...@@ -385,32 +385,30 @@ contract Deploy is Deployer { ...@@ -385,32 +385,30 @@ contract Deploy is Deployer {
/// @notice Deploy the SystemConfig /// @notice Deploy the SystemConfig
function deploySystemConfig() broadcast() public returns (address) { function deploySystemConfig() broadcast() public returns (address) {
bytes32 batcherHash = bytes32(uint256(uint160(cfg.batchSenderAddress()))); SystemConfig config = new SystemConfig();
SystemConfig config = new SystemConfig({
_owner: cfg.finalSystemOwner(),
_overhead: cfg.gasPriceOracleOverhead(),
_scalar: cfg.gasPriceOracleScalar(),
_batcherHash: batcherHash,
_gasLimit: uint64(cfg.l2GenesisBlockGasLimit()),
_unsafeBlockSigner: cfg.p2pSequencerAddress(),
_config: Constants.DEFAULT_RESOURCE_CONFIG()
});
require(config.owner() == cfg.finalSystemOwner()); require(config.owner() == address(0xdEaD));
require(config.overhead() == cfg.gasPriceOracleOverhead()); require(config.overhead() == 0);
require(config.scalar() == cfg.gasPriceOracleScalar()); require(config.scalar() == 0);
require(config.unsafeBlockSigner() == cfg.p2pSequencerAddress()); require(config.unsafeBlockSigner() == address(0));
require(config.batcherHash() == batcherHash); require(config.batcherHash() == bytes32(0));
require(config.gasLimit() == 1);
ResourceMetering.ResourceConfig memory rconfig = Constants.DEFAULT_RESOURCE_CONFIG();
ResourceMetering.ResourceConfig memory resourceConfig = config.resourceConfig(); ResourceMetering.ResourceConfig memory resourceConfig = config.resourceConfig();
require(resourceConfig.maxResourceLimit == rconfig.maxResourceLimit); require(resourceConfig.maxResourceLimit == 1);
require(resourceConfig.elasticityMultiplier == rconfig.elasticityMultiplier); require(resourceConfig.elasticityMultiplier == 1);
require(resourceConfig.baseFeeMaxChangeDenominator == rconfig.baseFeeMaxChangeDenominator); require(resourceConfig.baseFeeMaxChangeDenominator == 2);
require(resourceConfig.systemTxMaxGas == rconfig.systemTxMaxGas); require(resourceConfig.systemTxMaxGas == 0);
require(resourceConfig.minimumBaseFee == rconfig.minimumBaseFee); require(resourceConfig.minimumBaseFee == 0);
require(resourceConfig.maximumBaseFee == rconfig.maximumBaseFee); require(resourceConfig.maximumBaseFee == 0);
require(config.l1ERC721Bridge() == address(0));
require(config.l1StandardBridge() == address(0));
require(config.l2OutputOracle() == address(0));
require(config.optimismPortal() == address(0));
require(config.l1CrossDomainMessenger() == address(0));
require(config.optimismMintableERC20Factory() == address(0));
require(config.startBlock() == 0);
save("SystemConfig", address(config)); save("SystemConfig", address(config));
console.log("SystemConfig deployed at %s", address(config)); console.log("SystemConfig deployed at %s", address(config));
...@@ -498,7 +496,17 @@ contract Deploy is Deployer { ...@@ -498,7 +496,17 @@ contract Deploy is Deployer {
batcherHash, batcherHash,
uint64(cfg.l2GenesisBlockGasLimit()), uint64(cfg.l2GenesisBlockGasLimit()),
cfg.p2pSequencerAddress(), cfg.p2pSequencerAddress(),
Constants.DEFAULT_RESOURCE_CONFIG() Constants.DEFAULT_RESOURCE_CONFIG(),
cfg.systemConfigStartBlock(),
cfg.batchInboxAddress(),
SystemConfig.Addresses({
l1CrossDomainMessenger: mustGetAddress("L1CrossDomainMessengerProxy"),
l1ERC721Bridge: mustGetAddress("L1ERC721BridgeProxy"),
l1StandardBridge: mustGetAddress("L1StandardBridgeProxy"),
l2OutputOracle: mustGetAddress("L2OutputOracleProxy"),
optimismPortal: mustGetAddress("OptimismPortalProxy"),
optimismMintableERC20Factory: mustGetAddress("OptimismMintableERC20FactoryProxy")
})
) )
) )
}); });
...@@ -522,6 +530,12 @@ contract Deploy is Deployer { ...@@ -522,6 +530,12 @@ contract Deploy is Deployer {
require(resourceConfig.minimumBaseFee == rconfig.minimumBaseFee); require(resourceConfig.minimumBaseFee == rconfig.minimumBaseFee);
require(resourceConfig.maximumBaseFee == rconfig.maximumBaseFee); require(resourceConfig.maximumBaseFee == rconfig.maximumBaseFee);
require(config.l1ERC721Bridge() == mustGetAddress("L1ERC721BridgeProxy"));
require(config.l1StandardBridge() == mustGetAddress("L1StandardBridgeProxy"));
require(config.l2OutputOracle() == mustGetAddress("L2OutputOracleProxy"));
require(config.optimismPortal() == mustGetAddress("OptimismPortalProxy"));
require(config.l1CrossDomainMessenger() == mustGetAddress("L1CrossDomainMessengerProxy"));
require(config.startBlock() == cfg.systemConfigStartBlock());
} }
/// @notice Initialize the L1StandardBridge /// @notice Initialize the L1StandardBridge
......
...@@ -47,6 +47,7 @@ contract DeployConfig is Script { ...@@ -47,6 +47,7 @@ contract DeployConfig is Script {
uint256 public faultGameAbsolutePrestate; uint256 public faultGameAbsolutePrestate;
uint256 public faultGameMaxDepth; uint256 public faultGameMaxDepth;
uint256 public faultGameMaxDuration; uint256 public faultGameMaxDuration;
uint256 public systemConfigStartBlock;
constructor(string memory _path) { constructor(string memory _path) {
console.log("DeployConfig: reading file %s", _path); console.log("DeployConfig: reading file %s", _path);
...@@ -87,6 +88,7 @@ contract DeployConfig is Script { ...@@ -87,6 +88,7 @@ contract DeployConfig is Script {
gasPriceOracleScalar = stdJson.readUint(_json, "$.gasPriceOracleScalar"); gasPriceOracleScalar = stdJson.readUint(_json, "$.gasPriceOracleScalar");
eip1559Denominator = stdJson.readUint(_json, "$.eip1559Denominator"); eip1559Denominator = stdJson.readUint(_json, "$.eip1559Denominator");
eip1559Elasticity = stdJson.readUint(_json, "$.eip1559Elasticity"); eip1559Elasticity = stdJson.readUint(_json, "$.eip1559Elasticity");
systemConfigStartBlock = stdJson.readUint(_json, "$.systemConfigStartBlock");
if (block.chainid == Chains.LocalDevnet || block.chainid == Chains.GethDevnet) { if (block.chainid == Chains.LocalDevnet || block.chainid == Chains.GethDevnet) {
faultGameAbsolutePrestate = stdJson.readUint(_json, "$.faultGameAbsolutePrestate"); faultGameAbsolutePrestate = stdJson.readUint(_json, "$.faultGameAbsolutePrestate");
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"src/L1/L1StandardBridge.sol": "0xa35dc0ab143043063c3bff73c8b065e401c23296a2017258ce8a87f4a4da9416", "src/L1/L1StandardBridge.sol": "0xa35dc0ab143043063c3bff73c8b065e401c23296a2017258ce8a87f4a4da9416",
"src/L1/L2OutputOracle.sol": "0x8f32ccb4c5cb63a459a0e79ee412177dc03fd279fdaaf1dac69e8c714902e857", "src/L1/L2OutputOracle.sol": "0x8f32ccb4c5cb63a459a0e79ee412177dc03fd279fdaaf1dac69e8c714902e857",
"src/L1/OptimismPortal.sol": "0xeaa47a63e8a3bcfdb7dfd3e6c8608369e34e362d9de82f3acf13cbc27c070bf7", "src/L1/OptimismPortal.sol": "0xeaa47a63e8a3bcfdb7dfd3e6c8608369e34e362d9de82f3acf13cbc27c070bf7",
"src/L1/SystemConfig.sol": "0x8e2b5103d2eb93b74af2e2f96a4505e637cdc3c44d80cf5ec2eca70060e1deff", "src/L1/SystemConfig.sol": "0x689a3339e9167e62fac105eaae0390b39fc7dfee21b19c5f78636d98ee97148e",
"src/L2/BaseFeeVault.sol": "0xa596e60762f16192cfa86459fcb9f4da9d8f756106eedac643a1ffeafbbfcc5f", "src/L2/BaseFeeVault.sol": "0xa596e60762f16192cfa86459fcb9f4da9d8f756106eedac643a1ffeafbbfcc5f",
"src/L2/GasPriceOracle.sol": "0xc735a8bf01ad8bca194345748537bfd9924909c0342bc133c4a31e2fb8cb9882", "src/L2/GasPriceOracle.sol": "0xc735a8bf01ad8bca194345748537bfd9924909c0342bc133c4a31e2fb8cb9882",
"src/L2/L1Block.sol": "0x7fbfc8b4da630386636c665570321fdec287b0867dbe0f91c2e7cd5b7697c220", "src/L2/L1Block.sol": "0x7fbfc8b4da630386636c665570321fdec287b0867dbe0f91c2e7cd5b7697c220",
......
...@@ -202,17 +202,38 @@ contract Portal_Initializer is L2OutputOracle_Initializer { ...@@ -202,17 +202,38 @@ contract Portal_Initializer is L2OutputOracle_Initializer {
function setUp() public virtual override { function setUp() public virtual override {
super.setUp(); super.setUp();
ResourceMetering.ResourceConfig memory config = Constants.DEFAULT_RESOURCE_CONFIG(); Proxy systemConfigProxy = new Proxy(multisig);
systemConfig = new SystemConfig({ SystemConfig systemConfigImpl = new SystemConfig();
_owner: address(1),
_overhead: 0, vm.prank(multisig);
_scalar: 10000, systemConfigProxy.upgradeToAndCall(
_batcherHash: bytes32(0), address(systemConfigImpl),
_gasLimit: 30_000_000, abi.encodeCall(
_unsafeBlockSigner: address(0), SystemConfig.initialize,
_config: config (
}); address(1), //_owner,
0, //_overhead,
10000, //_scalar,
bytes32(0), //_batcherHash,
30_000_000, //_gasLimit,
address(0), //_unsafeBlockSigner,
Constants.DEFAULT_RESOURCE_CONFIG(), //_config,
0, //_startBlock
address(0xff), // _batchInbox
SystemConfig.Addresses({ // _addresses
l1CrossDomainMessenger: address(0),
l1ERC721Bridge: address(0),
l1StandardBridge: address(0),
l2OutputOracle: address(oracle),
optimismPortal: address(op),
optimismMintableERC20Factory: address(0)
})
)
)
);
systemConfig = SystemConfig(address(systemConfigProxy));
opImpl = new OptimismPortal(); opImpl = new OptimismPortal();
......
...@@ -3,24 +3,45 @@ pragma solidity 0.8.15; ...@@ -3,24 +3,45 @@ pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { SystemConfig } from "../../src/L1/SystemConfig.sol"; import { SystemConfig } from "../../src/L1/SystemConfig.sol";
import { Proxy } from "../../src/universal/Proxy.sol";
import { ResourceMetering } from "../../src/L1/ResourceMetering.sol"; import { ResourceMetering } from "../../src/L1/ResourceMetering.sol";
import { Constants } from "../../src/libraries/Constants.sol"; import { Constants } from "../../src/libraries/Constants.sol";
contract SystemConfig_GasLimitLowerBound_Invariant is Test { contract SystemConfig_GasLimitLowerBound_Invariant is Test {
SystemConfig public config; SystemConfig public config;
function setUp() public { function setUp() external {
ResourceMetering.ResourceConfig memory cfg = Constants.DEFAULT_RESOURCE_CONFIG(); Proxy proxy = new Proxy(msg.sender);
SystemConfig configImpl = new SystemConfig();
config = new SystemConfig({ vm.prank(msg.sender);
_owner: address(0xbeef), proxy.upgradeToAndCall(
_overhead: 2100, address(configImpl),
_scalar: 1000000, abi.encodeCall(
_batcherHash: bytes32(hex"abcd"), configImpl.initialize,
_gasLimit: 30_000_000, (
_unsafeBlockSigner: address(1), address(0xbeef), // owner
_config: cfg 2100, // overhead
}); 1000000, // scalar
bytes32(hex"abcd"), // batcher hash
30_000_000, // gas limit
address(1), // unsafe block signer
Constants.DEFAULT_RESOURCE_CONFIG(), // resource config
0, //_startBlock
address(0), // _batchInbox
SystemConfig.Addresses({ // _addrs
l1CrossDomainMessenger: address(0),
l1ERC721Bridge: address(0),
l1StandardBridge: address(0),
l2OutputOracle: address(0),
optimismPortal: address(0),
optimismMintableERC20Factory: address(0)
})
)
)
);
config = SystemConfig(address(proxy));
// Set the target contract to the `config` // Set the target contract to the `config`
targetContract(address(config)); targetContract(address(config));
......
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
}, },
"homepage": "https://optimism.io", "homepage": "https://optimism.io",
"type": "module", "type": "module",
"main": "dist/constants.js", "main": "dist/constants.cjs",
"module": "dist/constants.mjs", "module": "dist/constants.js",
"types": "src/constants.ts", "types": "src/constants.ts",
"exports": { "exports": {
".": { ".": {
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react-hooks": "^8.0.1", "@testing-library/react-hooks": "^8.0.1",
"@types/glob": "^8.1.0", "@types/glob": "^8.1.0",
"@vitest/coverage-istanbul": "^0.33.0", "@vitest/coverage-istanbul": "^0.34.1",
"@wagmi/cli": "^1.3.0", "@wagmi/cli": "^1.3.0",
"@wagmi/core": "^1.3.8", "@wagmi/core": "^1.3.8",
"abitype": "^0.9.3", "abitype": "^0.9.3",
......
...@@ -10,8 +10,16 @@ ...@@ -10,8 +10,16 @@
}, },
"homepage": "https://optimism.io", "homepage": "https://optimism.io",
"type": "module", "type": "module",
"main": "dist/estimateFees.js", "main": "dist/estimateFees.cjs",
"module": "dist/estimateFees.mjs", "module": "dist/estimateFees.js",
"exports": {
".": {
"import": "./dist/estimateFees.js",
"require": "./dist/estimateFees.cjs",
"default": "./dist/estimateFees.js",
"types": "./src/estimateFees.ts"
}
},
"types": "src/estimateFees.ts", "types": "src/estimateFees.ts",
"files": [ "files": [
"dist/", "dist/",
...@@ -29,7 +37,7 @@ ...@@ -29,7 +37,7 @@
"@eth-optimism/contracts-ts": "workspace:^", "@eth-optimism/contracts-ts": "workspace:^",
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react-hooks": "^8.0.1", "@testing-library/react-hooks": "^8.0.1",
"@vitest/coverage-istanbul": "^0.33.0", "@vitest/coverage-istanbul": "^0.34.1",
"abitype": "^0.9.3", "abitype": "^0.9.3",
"isomorphic-fetch": "^3.0.0", "isomorphic-fetch": "^3.0.0",
"jest-dom": "link:@types/@testing-library/jest-dom", "jest-dom": "link:@types/@testing-library/jest-dom",
......
This diff is collapsed.
...@@ -48,7 +48,7 @@ to each of the different game types. For specification of dispute game types, se ...@@ -48,7 +48,7 @@ to each of the different game types. For specification of dispute game types, se
### `GameType.FAULT` ### `GameType.FAULT`
> **Warning** > **Warning**
> The `FAULT` game type is not yet implemented. In the first iteration of Optimism's decentralization effort, > The `FAULT` game type is not yet implemented. In the first iteration of Optimism's decentralization effort,
> challengers will respond to `ATTESTATION` games only. > challengers will respond to `ATTESTATION` games only.
...@@ -59,7 +59,7 @@ to each of the different game types. For specification of dispute game types, se ...@@ -59,7 +59,7 @@ to each of the different game types. For specification of dispute game types, se
**Events and Responses** **Events and Responses**
- [`L2OutputOracle.OutputProposed`](../packages/contracts-bedrock/contracts/L1/L2OutputOracle.sol#L57-70) - [`L2OutputOracle.OutputProposed`](../packages/contracts-bedrock/src/L1/L2OutputOracle.sol#L57-70)
The `L2OutputOracle` contract emits this event when a new output is proposed on the data availability The `L2OutputOracle` contract emits this event when a new output is proposed on the data availability
layer. Each time an output is proposed, the Challenger should check to see if the output is equal layer. Each time an output is proposed, the Challenger should check to see if the output is equal
the output given by the `optimism_outputAtBlock` endpoint of their `rollup-node`. the output given by the `optimism_outputAtBlock` endpoint of their `rollup-node`.
...@@ -85,7 +85,7 @@ A full diagram and lifecycle of the Challenger's role in the `ATTESTATION` game ...@@ -85,7 +85,7 @@ A full diagram and lifecycle of the Challenger's role in the `ATTESTATION` game
**TODO** **TODO**
> **Warning** > **Warning**
> The `VALIDITY` game type is not yet implemented. In the first iteration of Optimism's decentralization effort, > The `VALIDITY` game type is not yet implemented. In the first iteration of Optimism's decentralization effort,
> challengers will respond to `ATTESTATION` games only. A validity proof based dispute game is a possibility, > challengers will respond to `ATTESTATION` games only. A validity proof based dispute game is a possibility,
> but fault proof based dispute games will be the primary focus of the team in the near future. > but fault proof based dispute games will be the primary focus of the team in the near future.
......
...@@ -306,7 +306,7 @@ The contract has the following solidity interface, and can be interacted with ac ...@@ -306,7 +306,7 @@ The contract has the following solidity interface, and can be interacted with ac
A reference implementation of the L1 Attributes predeploy contract can be found in [L1Block.sol]. A reference implementation of the L1 Attributes predeploy contract can be found in [L1Block.sol].
[L1Block.sol]: ../packages/contracts-bedrock/contracts/L2/L1Block.sol [L1Block.sol]: ../packages/contracts-bedrock/src/L2/L1Block.sol
After running `pnpm build` in the `packages/contracts` directory, the bytecode to add to the genesis After running `pnpm build` in the `packages/contracts` directory, the bytecode to add to the genesis
file will be located in the `deployedBytecode` field of the build artifacts file at file will be located in the `deployedBytecode` field of the build artifacts file at
......
...@@ -70,7 +70,7 @@ or `Bedrock`. Deprecated contracts should not be used. ...@@ -70,7 +70,7 @@ or `Bedrock`. Deprecated contracts should not be used.
## LegacyMessagePasser ## LegacyMessagePasser
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/legacy/LegacyMessagePasser.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/legacy/LegacyMessagePasser.sol)
Address: `0x4200000000000000000000000000000000000000` Address: `0x4200000000000000000000000000000000000000`
...@@ -92,7 +92,7 @@ finalized. ...@@ -92,7 +92,7 @@ finalized.
## L2ToL1MessagePasser ## L2ToL1MessagePasser
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/L2/L2ToL1MessagePasser.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol)
Address: `0x4200000000000000000000000000000000000016` Address: `0x4200000000000000000000000000000000000016`
...@@ -106,7 +106,7 @@ permissionlessly removed from the L2 supply by calling the `burn()` function. ...@@ -106,7 +106,7 @@ permissionlessly removed from the L2 supply by calling the `burn()` function.
## DeployerWhitelist ## DeployerWhitelist
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/legacy/DeployerWhitelist.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/legacy/DeployerWhitelist.sol)
Address: `0x4200000000000000000000000000000000000002` Address: `0x4200000000000000000000000000000000000002`
...@@ -125,7 +125,7 @@ This contract is deprecated and its usage should be avoided. ...@@ -125,7 +125,7 @@ This contract is deprecated and its usage should be avoided.
## LegacyERC20ETH ## LegacyERC20ETH
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/legacy/LegacyERC20ETH.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/legacy/LegacyERC20ETH.sol)
Address: `0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000` Address: `0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000`
...@@ -141,7 +141,7 @@ This contract is deprecated and its usage should be avoided. ...@@ -141,7 +141,7 @@ This contract is deprecated and its usage should be avoided.
## WETH9 ## WETH9
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/vendor/WETH9.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/vendor/WETH9.sol)
Address: `0x4200000000000000000000000000000000000006` Address: `0x4200000000000000000000000000000000000006`
...@@ -151,7 +151,7 @@ deterministic address across Optimism based networks. ...@@ -151,7 +151,7 @@ deterministic address across Optimism based networks.
## L2CrossDomainMessenger ## L2CrossDomainMessenger
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/L2/L2CrossDomainMessenger.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2CrossDomainMessenger.sol)
Address: `0x4200000000000000000000000000000000000007` Address: `0x4200000000000000000000000000000000000007`
...@@ -170,7 +170,7 @@ domain through the remote domain's `relayMessage` function. ...@@ -170,7 +170,7 @@ domain through the remote domain's `relayMessage` function.
## L2StandardBridge ## L2StandardBridge
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/L2/L2StandardBridge.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2StandardBridge.sol)
Address: `0x4200000000000000000000000000000000000010` Address: `0x4200000000000000000000000000000000000010`
...@@ -197,7 +197,7 @@ withdrawn to L1. ...@@ -197,7 +197,7 @@ withdrawn to L1.
## L1BlockNumber ## L1BlockNumber
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/legacy/L1BlockNumber.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/legacy/L1BlockNumber.sol)
Address: `0x4200000000000000000000000000000000000013` Address: `0x4200000000000000000000000000000000000013`
...@@ -210,7 +210,7 @@ L1 on L2. ...@@ -210,7 +210,7 @@ L1 on L2.
## GasPriceOracle ## GasPriceOracle
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/L2/GasPriceOracle.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/GasPriceOracle.sol)
Address: `0x420000000000000000000000000000000000000F` Address: `0x420000000000000000000000000000000000000F`
...@@ -240,7 +240,7 @@ has been hardcoded to 6. ...@@ -240,7 +240,7 @@ has been hardcoded to 6.
## L1Block ## L1Block
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/L2/L1Block.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L1Block.sol)
Address: `0x4200000000000000000000000000000000000015` Address: `0x4200000000000000000000000000000000000015`
...@@ -251,7 +251,7 @@ maintaining L1 context in L2. This allows for L1 state to be accessed in L2. ...@@ -251,7 +251,7 @@ maintaining L1 context in L2. This allows for L1 state to be accessed in L2.
## ProxyAdmin ## ProxyAdmin
[ProxyAdmin](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/universal/ProxyAdmin.sol) [ProxyAdmin](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/universal/ProxyAdmin.sol)
Address: `0x4200000000000000000000000000000000000018` Address: `0x4200000000000000000000000000000000000018`
The `ProxyAdmin` is the owner of all of the proxy contracts set at the The `ProxyAdmin` is the owner of all of the proxy contracts set at the
...@@ -260,7 +260,7 @@ have the ability to upgrade any of the other predeploy contracts. ...@@ -260,7 +260,7 @@ have the ability to upgrade any of the other predeploy contracts.
## SequencerFeeVault ## SequencerFeeVault
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/L2/SequencerFeeVault.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SequencerFeeVault.sol)
Address: `0x4200000000000000000000000000000000000011` Address: `0x4200000000000000000000000000000000000011`
...@@ -273,7 +273,7 @@ upgraded by changing its proxy's implementation key. ...@@ -273,7 +273,7 @@ upgraded by changing its proxy's implementation key.
## OptimismMintableERC20Factory ## OptimismMintableERC20Factory
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/universal/OptimismMintableERC20Factory.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol)
Address: `0x4200000000000000000000000000000000000012` Address: `0x4200000000000000000000000000000000000012`
...@@ -286,7 +286,7 @@ and burn tokens, depending on if the user is depositing from L1 to L2 or withdra ...@@ -286,7 +286,7 @@ and burn tokens, depending on if the user is depositing from L1 to L2 or withdra
## OptimismMintableERC721Factory ## OptimismMintableERC721Factory
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/universal/OptimismMintableERC721Factory.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol)
Address: `0x4200000000000000000000000000000000000017` Address: `0x4200000000000000000000000000000000000017`
...@@ -295,7 +295,7 @@ depositing native L1 NFTs into. ...@@ -295,7 +295,7 @@ depositing native L1 NFTs into.
## BaseFeeVault ## BaseFeeVault
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/L2/BaseFeeVault.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/BaseFeeVault.sol)
Address: `0x4200000000000000000000000000000000000019` Address: `0x4200000000000000000000000000000000000019`
...@@ -306,7 +306,7 @@ L1. ...@@ -306,7 +306,7 @@ L1.
## L1FeeVault ## L1FeeVault
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/L2/L1FeeVault.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L1FeeVault.sol)
Address: `0x420000000000000000000000000000000000001a` Address: `0x420000000000000000000000000000000000001a`
...@@ -316,7 +316,7 @@ withdrawn to an immutable address on L1. ...@@ -316,7 +316,7 @@ withdrawn to an immutable address on L1.
## SchemaRegistry ## SchemaRegistry
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/EAS/SchemaRegistry.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/EAS/SchemaRegistry.sol)
Address: `0x4200000000000000000000000000000000000020` Address: `0x4200000000000000000000000000000000000020`
...@@ -325,7 +325,7 @@ protocol. ...@@ -325,7 +325,7 @@ protocol.
## EAS ## EAS
[Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/contracts/EAS/EAS.sol) [Implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/EAS/EAS.sol)
Address: `0x4200000000000000000000000000000000000021` Address: `0x4200000000000000000000000000000000000021`
......
...@@ -214,5 +214,5 @@ whether or not it was 'supposed' to fail, and whether or not it should be 'repla ...@@ -214,5 +214,5 @@ whether or not it was 'supposed' to fail, and whether or not it should be 'repla
minimize complexity, we have not provided any replay functionality, this may be implemented in external utility minimize complexity, we have not provided any replay functionality, this may be implemented in external utility
contracts if desired. contracts if desired.
[`WithdrawalTransaction` type]: https://github.com/ethereum-optimism/optimism/blob/6c6d142d7bb95faa11066aab5d8aed7187abfe38/packages/contracts-bedrock/contracts/libraries/Types.sol#L76-L83 [`WithdrawalTransaction` type]: https://github.com/ethereum-optimism/optimism/blob/08daf8dbd38c9ffdbd18fc9a211c227606cdb0ad/packages/contracts-bedrock/src/libraries/Types.sol#L62-L69
[`OutputRootProof` type]: https://github.com/ethereum-optimism/optimism/blob/6c6d142d7bb95faa11066aab5d8aed7187abfe38/packages/contracts-bedrock/contracts/libraries/Types.sol#L33-L38 [`OutputRootProof` type]: https://github.com/ethereum-optimism/optimism/blob/08daf8dbd38c9ffdbd18fc9a211c227606cdb0ad/packages/contracts-bedrock/src/libraries/Types.sol#L25-L30
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment