Commit ba939af9 authored by Adrian Sutton's avatar Adrian Sutton

Merge branch 'develop' into jg/challenger_metrics

# Conflicts:
#	op-challenger/fault/service.go
parents 532d8ce6 7b30659f
---
'@eth-optimism/common-ts': patch
---
Updated npm dependencies of common-ts
...@@ -415,6 +415,10 @@ jobs: ...@@ -415,6 +415,10 @@ jobs:
pnpm autogen:invariant-docs pnpm autogen:invariant-docs
git diff --exit-code ./invariant-docs/*.md || echo "export INVARIANT_DOCS_STATUS=1" >> "$BASH_ENV" git diff --exit-code ./invariant-docs/*.md || echo "export INVARIANT_DOCS_STATUS=1" >> "$BASH_ENV"
working_directory: packages/contracts-bedrock working_directory: packages/contracts-bedrock
- run:
name: check deploy configs || echo "export DEPLOY_CONFIGS_STATUS=1" >> "$BASH_ENV"
command: pnpm validate-deploy-configs
working_directory: packages/contracts-bedrock
- run: - run:
name: check statuses name: check statuses
command: | command: |
...@@ -430,12 +434,16 @@ jobs: ...@@ -430,12 +434,16 @@ jobs:
echo "Storage snapshot failed, see job output for details." echo "Storage snapshot failed, see job output for details."
FAILED=1 FAILED=1
fi fi
if [[ "$SEMVER_LOCK_STATUS" -ne 0 ]]; then
echo "Semver lock failed, see job output for details."
FAILED=1
fi
if [[ "$INVARIANT_DOCS_STATUS" -ne 0 ]]; then if [[ "$INVARIANT_DOCS_STATUS" -ne 0 ]]; then
echo "Invariant docs failed, see job output for details." echo "Invariant docs failed, see job output for details."
FAILED=1 FAILED=1
fi fi
if [[ "$SEMVER_LOCK_STATUS" -ne 0 ]]; then if [[ "$DEPLOY_CONFIGS_STATUS" -ne 0 ]]; then
echo "Semver lock failed, see job output for details." echo "Deploy config check failed, see job output for details."
FAILED=1 FAILED=1
fi fi
if [[ "$FAILED" -ne 0 ]]; then if [[ "$FAILED" -ne 0 ]]; then
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -13,7 +13,7 @@ const ProxyStorageLayoutJSON = "{\"storage\":null,\"types\":{}}" ...@@ -13,7 +13,7 @@ const ProxyStorageLayoutJSON = "{\"storage\":null,\"types\":{}}"
var ProxyStorageLayout = new(solc.StorageLayout) var ProxyStorageLayout = new(solc.StorageLayout)
var ProxyDeployedBin = "0x60806040526004361061005e5760003560e01c80635c60da1b116100435780635c60da1b146100be5780638f283970146100f8578063f851a440146101185761006d565b80633659cfe6146100755780634f1ef286146100955761006d565b3661006d5761006b61012d565b005b61006b61012d565b34801561008157600080fd5b5061006b6100903660046106d9565b610224565b6100a86100a33660046106f4565b610296565b6040516100b59190610777565b60405180910390f35b3480156100ca57600080fd5b506100d3610419565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100b5565b34801561010457600080fd5b5061006b6101133660046106d9565b6104b0565b34801561012457600080fd5b506100d3610517565b60006101577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b905073ffffffffffffffffffffffffffffffffffffffff8116610201576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f50726f78793a20696d706c656d656e746174696f6e206e6f7420696e6974696160448201527f6c697a656400000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b3660008037600080366000845af43d6000803e8061021e573d6000fd5b503d6000f35b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16148061027d575033155b1561028e5761028b816105a3565b50565b61028b61012d565b60606102c07fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102f7575033155b1561040a57610305846105a3565b6000808573ffffffffffffffffffffffffffffffffffffffff16858560405161032f9291906107ea565b600060405180830381855af49150503d806000811461036a576040519150601f19603f3d011682016040523d82523d6000602084013e61036f565b606091505b509150915081610401576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f50726f78793a2064656c656761746563616c6c20746f206e657720696d706c6560448201527f6d656e746174696f6e20636f6e7472616374206661696c65640000000000000060648201526084016101f8565b91506104129050565b61041261012d565b9392505050565b60006104437fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16148061047a575033155b156104a557507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b6104ad61012d565b90565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610509575033155b1561028e5761028b8161060b565b60006105417fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610578575033155b156104a557507fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc81905560405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60006106357fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61038390556040805173ffffffffffffffffffffffffffffffffffffffff8084168252851660208201529192507f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f910160405180910390a15050565b803573ffffffffffffffffffffffffffffffffffffffff811681146106d457600080fd5b919050565b6000602082840312156106eb57600080fd5b610412826106b0565b60008060006040848603121561070957600080fd5b610712846106b0565b9250602084013567ffffffffffffffff8082111561072f57600080fd5b818601915086601f83011261074357600080fd5b81358181111561075257600080fd5b87602082850101111561076457600080fd5b6020830194508093505050509250925092565b600060208083528351808285015260005b818110156107a457858101830151858201604001528201610788565b818111156107b6576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b818382376000910190815291905056fea164736f6c634300080f000a" var ProxyDeployedBin = "0x60806040526004361061005e5760003560e01c80635c60da1b116100435780635c60da1b146100be5780638f283970146100f8578063f851a440146101185761006d565b80633659cfe6146100755780634f1ef286146100955761006d565b3661006d5761006b61012d565b005b61006b61012d565b34801561008157600080fd5b5061006b6100903660046106dd565b610224565b6100a86100a33660046106f8565b610296565b6040516100b5919061077b565b60405180910390f35b3480156100ca57600080fd5b506100d3610419565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100b5565b34801561010457600080fd5b5061006b6101133660046106dd565b6104b0565b34801561012457600080fd5b506100d3610517565b60006101577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b905073ffffffffffffffffffffffffffffffffffffffff8116610201576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f50726f78793a20696d706c656d656e746174696f6e206e6f7420696e6974696160448201527f6c697a656400000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b3660008037600080366000845af43d6000803e8061021e573d6000fd5b503d6000f35b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16148061027d575033155b1561028e5761028b816105a3565b50565b61028b61012d565b60606102c07fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102f7575033155b1561040a57610305846105a3565b6000808573ffffffffffffffffffffffffffffffffffffffff16858560405161032f9291906107ee565b600060405180830381855af49150503d806000811461036a576040519150601f19603f3d011682016040523d82523d6000602084013e61036f565b606091505b509150915081610401576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f50726f78793a2064656c656761746563616c6c20746f206e657720696d706c6560448201527f6d656e746174696f6e20636f6e7472616374206661696c65640000000000000060648201526084016101f8565b91506104129050565b61041261012d565b9392505050565b60006104437fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16148061047a575033155b156104a557507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b6104ad61012d565b90565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610509575033155b1561028e5761028b8161060c565b60006105417fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610578575033155b156104a557507fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc81815560405173ffffffffffffffffffffffffffffffffffffffff8316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a25050565b60006106367fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61038381556040805173ffffffffffffffffffffffffffffffffffffffff80851682528616602082015292935090917f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f910160405180910390a1505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146106d857600080fd5b919050565b6000602082840312156106ef57600080fd5b610412826106b4565b60008060006040848603121561070d57600080fd5b610716846106b4565b9250602084013567ffffffffffffffff8082111561073357600080fd5b818601915086601f83011261074757600080fd5b81358181111561075657600080fd5b87602082850101111561076857600080fd5b6020830194508093505050509250925092565b600060208083528351808285015260005b818110156107a85785810183015185820160400152820161078c565b818111156107ba576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b818382376000910190815291905056fea164736f6c634300080f000a"
func init() { func init() {
if err := json.Unmarshal([]byte(ProxyStorageLayoutJSON), ProxyStorageLayout); err != nil { if err := json.Unmarshal([]byte(ProxyStorageLayoutJSON), ProxyStorageLayout); err != nil {
......
package main
import (
"os"
"path/filepath"
"strings"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum/go-ethereum/log"
)
func main() {
log.Root().SetHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(isatty.IsTerminal(os.Stderr.Fd()))))
app := &cli.App{
Name: "check-deploy-config",
Usage: "Check that a deploy config is valid",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "path",
Required: true,
Usage: "File system path to the deploy config",
},
},
Action: entrypoint,
}
if err := app.Run(os.Args); err != nil {
log.Crit("error checking deploy config", "err", err)
}
}
func entrypoint(ctx *cli.Context) error {
path := ctx.String("path")
name := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
log.Info("Checking deploy config", "name", name, "path", path)
config, err := genesis.NewDeployConfig(path)
if err != nil {
return err
}
// Check the config, no need to call `CheckAddresses()`
if err := config.Check(); err != nil {
return err
}
log.Info("Valid deploy config")
return nil
}
package genesis package genesis
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
...@@ -31,6 +32,7 @@ var ( ...@@ -31,6 +32,7 @@ var (
) )
// DeployConfig represents the deployment configuration for an OP Stack chain. // DeployConfig represents the deployment configuration for an OP Stack chain.
// It is used to deploy the L1 contracts as well as create the L2 genesis state.
type DeployConfig struct { type DeployConfig struct {
// L1StartingBlockTag is used to fill in the storage of the L1Block info predeploy. The rollup // L1StartingBlockTag is used to fill in the storage of the L1Block info predeploy. The rollup
// config script uses this to fill the L1 genesis info for the rollup. The Output oracle deploy // config script uses this to fill the L1 genesis info for the rollup. The Output oracle deploy
...@@ -172,10 +174,10 @@ type DeployConfig struct { ...@@ -172,10 +174,10 @@ type DeployConfig struct {
EIP1559Elasticity uint64 `json:"eip1559Elasticity"` EIP1559Elasticity uint64 `json:"eip1559Elasticity"`
// EIP1559Denominator is the denominator of EIP1559 base fee market. // EIP1559Denominator is the denominator of EIP1559 base fee market.
EIP1559Denominator uint64 `json:"eip1559Denominator"` EIP1559Denominator uint64 `json:"eip1559Denominator"`
// FundDevAccounts configures whether or not to fund the dev accounts. Should only be used // SystemConfigStartBlock represents the block at which the op-node should start syncing
// during devnet deployments. // from. It is an override to set this value on legacy networks where it is not set by
FundDevAccounts bool `json:"fundDevAccounts"` // default. It can be removed once all networks have this value set in their storage.
SystemConfigStartBlock uint64 `json:"systemConfigStartBlock"`
// FaultGameAbsolutePrestate is the absolute prestate of Cannon. This is computed // FaultGameAbsolutePrestate is the absolute prestate of Cannon. This is computed
// by generating a proof from the 0th -> 1st instruction and grabbing the prestate from // by generating a proof from the 0th -> 1st instruction and grabbing the prestate from
// the output JSON. All honest challengers should agree on the setup state of the program. // the output JSON. All honest challengers should agree on the setup state of the program.
...@@ -193,6 +195,9 @@ type DeployConfig struct { ...@@ -193,6 +195,9 @@ type DeployConfig struct {
// game can run for before it is ready to be resolved. Each side receives half of this value // game can run for before it is ready to be resolved. Each side receives half of this value
// on their chess clock at the inception of the dispute. // on their chess clock at the inception of the dispute.
FaultGameMaxDuration uint64 `json:"faultGameMaxDuration"` FaultGameMaxDuration uint64 `json:"faultGameMaxDuration"`
// FundDevAccounts configures whether or not to fund the dev accounts. Should only be used
// during devnet deployments.
FundDevAccounts bool `json:"fundDevAccounts"`
} }
// Copy will deeply copy the DeployConfig. This does a JSON roundtrip to copy // Copy will deeply copy the DeployConfig. This does a JSON roundtrip to copy
...@@ -293,21 +298,6 @@ func (d *DeployConfig) Check() error { ...@@ -293,21 +298,6 @@ func (d *DeployConfig) Check() error {
if d.GasPriceOracleScalar == 0 { if d.GasPriceOracleScalar == 0 {
return fmt.Errorf("%w: GasPriceOracleScalar cannot be 0", ErrInvalidDeployConfig) return fmt.Errorf("%w: GasPriceOracleScalar cannot be 0", ErrInvalidDeployConfig)
} }
if d.L1StandardBridgeProxy == (common.Address{}) {
return fmt.Errorf("%w: L1StandardBridgeProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.L1CrossDomainMessengerProxy == (common.Address{}) {
return fmt.Errorf("%w: L1CrossDomainMessengerProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.L1ERC721BridgeProxy == (common.Address{}) {
return fmt.Errorf("%w: L1ERC721BridgeProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.SystemConfigProxy == (common.Address{}) {
return fmt.Errorf("%w: SystemConfigProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.OptimismPortalProxy == (common.Address{}) {
return fmt.Errorf("%w: OptimismPortalProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.EIP1559Denominator == 0 { if d.EIP1559Denominator == 0 {
return fmt.Errorf("%w: EIP1559Denominator cannot be 0", ErrInvalidDeployConfig) return fmt.Errorf("%w: EIP1559Denominator cannot be 0", ErrInvalidDeployConfig)
} }
...@@ -343,6 +333,29 @@ func (d *DeployConfig) Check() error { ...@@ -343,6 +333,29 @@ func (d *DeployConfig) Check() error {
return nil return nil
} }
// CheckAddresses will return an error if the addresses are not set.
// These values are required to create the L2 genesis state and are present in the deploy config
// even though the deploy config is required to deploy the contracts on L1. This creates a
// circular dependency that should be resolved in the future.
func (d *DeployConfig) CheckAddresses() error {
if d.L1StandardBridgeProxy == (common.Address{}) {
return fmt.Errorf("%w: L1StandardBridgeProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.L1CrossDomainMessengerProxy == (common.Address{}) {
return fmt.Errorf("%w: L1CrossDomainMessengerProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.L1ERC721BridgeProxy == (common.Address{}) {
return fmt.Errorf("%w: L1ERC721BridgeProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.SystemConfigProxy == (common.Address{}) {
return fmt.Errorf("%w: SystemConfigProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.OptimismPortalProxy == (common.Address{}) {
return fmt.Errorf("%w: OptimismPortalProxy cannot be address(0)", ErrInvalidDeployConfig)
}
return nil
}
// SetDeployments will merge a Deployments into a DeployConfig. // SetDeployments will merge a Deployments into a DeployConfig.
func (d *DeployConfig) SetDeployments(deployments *L1Deployments) { func (d *DeployConfig) SetDeployments(deployments *L1Deployments) {
d.L1StandardBridgeProxy = deployments.L1StandardBridgeProxy d.L1StandardBridgeProxy = deployments.L1StandardBridgeProxy
...@@ -469,8 +482,11 @@ func NewDeployConfig(path string) (*DeployConfig, error) { ...@@ -469,8 +482,11 @@ func NewDeployConfig(path string) (*DeployConfig, error) {
return nil, fmt.Errorf("deploy config at %s not found: %w", path, err) return nil, fmt.Errorf("deploy config at %s not found: %w", path, err)
} }
dec := json.NewDecoder(bytes.NewReader(file))
dec.DisallowUnknownFields()
var config DeployConfig var config DeployConfig
if err := json.Unmarshal(file, &config); err != nil { if err := dec.Decode(&config); err != nil {
return nil, fmt.Errorf("cannot unmarshal deploy config: %w", err) return nil, fmt.Errorf("cannot unmarshal deploy config: %w", err)
} }
......
...@@ -66,5 +66,6 @@ ...@@ -66,5 +66,6 @@
"fundDevAccounts": true, "fundDevAccounts": true,
"faultGameAbsolutePrestate": "0x0000000000000000000000000000000000000000000000000000000000000000", "faultGameAbsolutePrestate": "0x0000000000000000000000000000000000000000000000000000000000000000",
"faultGameMaxDepth": 63, "faultGameMaxDepth": 63,
"faultGameMaxDuration": 604800 "faultGameMaxDuration": 604800,
"systemConfigStartBlock": 0
} }
...@@ -50,17 +50,17 @@ type CannonTraceProvider struct { ...@@ -50,17 +50,17 @@ type CannonTraceProvider struct {
lastProof *proofData lastProof *proofData
} }
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, gameAddr common.Address) (*CannonTraceProvider, error) {
l2Client, err := ethclient.DialContext(ctx, cfg.CannonL2) l2Client, err := ethclient.DialContext(ctx, cfg.CannonL2)
if err != nil { if err != nil {
return nil, fmt.Errorf("dial l2 client %v: %w", cfg.CannonL2, err) return nil, fmt.Errorf("dial l2 client %v: %w", cfg.CannonL2, err)
} }
defer l2Client.Close() // Not needed after fetching the inputs defer l2Client.Close() // Not needed after fetching the inputs
gameCaller, err := bindings.NewFaultDisputeGameCaller(cfg.GameAddress, l1Client) gameCaller, err := bindings.NewFaultDisputeGameCaller(gameAddr, l1Client)
if err != nil { if err != nil {
return nil, fmt.Errorf("create caller for game %v: %w", cfg.GameAddress, err) return nil, fmt.Errorf("create caller for game %v: %w", gameAddr, err)
} }
localInputs, err := fetchLocalInputs(ctx, cfg.GameAddress, gameCaller, l2Client) localInputs, err := fetchLocalInputs(ctx, gameAddr, gameCaller, l2Client)
if err != nil { if err != nil {
return nil, fmt.Errorf("fetch local game inputs: %w", err) return nil, fmt.Errorf("fetch local game inputs: %w", err)
} }
......
...@@ -31,11 +31,6 @@ type FaultDisputeGame struct { ...@@ -31,11 +31,6 @@ type FaultDisputeGame struct {
Proxy common.Address Proxy common.Address
} }
// GameLoader is a minimal interface for fetching on chain dispute games.
type GameLoader interface {
FetchAllGamesAtBlock(ctx context.Context) ([]FaultDisputeGame, error)
}
type gameLoader struct { type gameLoader struct {
caller MinimalDisputeGameFactoryCaller caller MinimalDisputeGameFactoryCaller
} }
......
package fault
import (
"context"
"fmt"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/fault/cannon"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
)
type Actor interface {
Act(ctx context.Context) error
}
type GameInfo interface {
GetGameStatus(context.Context) (types.GameStatus, error)
LogGameInfo(ctx context.Context)
}
type GamePlayer struct {
agent Actor
agreeWithProposedOutput bool
caller GameInfo
logger log.Logger
}
func NewGamePlayer(
ctx context.Context,
logger log.Logger,
cfg *config.Config,
addr common.Address,
txMgr txmgr.TxManager,
client *ethclient.Client,
) (*GamePlayer, error) {
logger = logger.New("game", addr)
contract, err := bindings.NewFaultDisputeGameCaller(addr, client)
if err != nil {
return nil, fmt.Errorf("failed to bind the fault dispute game contract: %w", err)
}
loader := NewLoader(contract)
gameDepth, err := loader.FetchGameDepth(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch the game depth: %w", err)
}
var provider types.TraceProvider
var updater types.OracleUpdater
switch cfg.TraceType {
case config.TraceTypeCannon:
provider, err = cannon.NewTraceProvider(ctx, logger, cfg, client, addr)
if err != nil {
return nil, fmt.Errorf("create cannon trace provider: %w", err)
}
updater, err = cannon.NewOracleUpdater(ctx, logger, txMgr, addr, client)
if err != nil {
return nil, fmt.Errorf("failed to create the cannon updater: %w", err)
}
case config.TraceTypeAlphabet:
provider = alphabet.NewTraceProvider(cfg.AlphabetTrace, gameDepth)
updater = alphabet.NewOracleUpdater(logger)
default:
return nil, fmt.Errorf("unsupported trace type: %v", cfg.TraceType)
}
if err := ValidateAbsolutePrestate(ctx, provider, loader); err != nil {
return nil, fmt.Errorf("failed to validate absolute prestate: %w", err)
}
responder, err := NewFaultResponder(logger, txMgr, addr)
if err != nil {
return nil, fmt.Errorf("failed to create the responder: %w", err)
}
caller, err := NewFaultCallerFromBindings(addr, client, logger)
if err != nil {
return nil, fmt.Errorf("failed to bind the fault contract: %w", err)
}
return &GamePlayer{
agent: NewAgent(loader, int(gameDepth), provider, responder, updater, cfg.AgreeWithProposedOutput, logger),
agreeWithProposedOutput: cfg.AgreeWithProposedOutput,
caller: caller,
logger: logger,
}, nil
}
func (g *GamePlayer) ProgressGame(ctx context.Context) bool {
g.logger.Trace("Checking if actions are required")
if err := g.agent.Act(ctx); err != nil {
g.logger.Error("Error when acting on game", "err", err)
}
if status, err := g.caller.GetGameStatus(ctx); err != nil {
g.logger.Warn("Unable to retrieve game status", "err", err)
} else if status != 0 {
var expectedStatus types.GameStatus
if g.agreeWithProposedOutput {
expectedStatus = types.GameStatusChallengerWon
} else {
expectedStatus = types.GameStatusDefenderWon
}
if expectedStatus == status {
g.logger.Info("Game won", "status", status)
} else {
g.logger.Error("Game lost", "status", status)
}
return true
} else {
g.caller.LogGameInfo(ctx)
}
return false
}
package fault
import (
"context"
"errors"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
func TestProgressGameAndLogState(t *testing.T) {
_, game, actor, gameInfo := setupProgressGameTest(t, true)
done := game.ProgressGame(context.Background())
require.False(t, done, "should not be done")
require.Equal(t, 1, actor.callCount, "should perform next actions")
require.Equal(t, 1, gameInfo.logCount, "should log latest game state")
}
func TestProgressGame_LogErrorFromAct(t *testing.T) {
handler, game, actor, gameInfo := setupProgressGameTest(t, true)
actor.err = errors.New("boom")
done := game.ProgressGame(context.Background())
require.False(t, done, "should not be done")
require.Equal(t, 1, actor.callCount, "should perform next actions")
require.Equal(t, 1, gameInfo.logCount, "should log latest game state")
errLog := handler.FindLog(log.LvlError, "Error when acting on game")
require.NotNil(t, errLog, "should log error")
require.Equal(t, actor.err, errLog.GetContextValue("err"))
}
func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
tests := []struct {
name string
status types.GameStatus
agreeWithOutput bool
logLevel log.Lvl
logMsg string
}{
{
name: "GameLostAsDefender",
status: types.GameStatusChallengerWon,
agreeWithOutput: false,
logLevel: log.LvlError,
logMsg: "Game lost",
},
{
name: "GameLostAsChallenger",
status: types.GameStatusDefenderWon,
agreeWithOutput: true,
logLevel: log.LvlError,
logMsg: "Game lost",
},
{
name: "GameWonAsDefender",
status: types.GameStatusDefenderWon,
agreeWithOutput: false,
logLevel: log.LvlInfo,
logMsg: "Game won",
},
{
name: "GameWonAsChallenger",
status: types.GameStatusChallengerWon,
agreeWithOutput: true,
logLevel: log.LvlInfo,
logMsg: "Game won",
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
handler, game, _, gameInfo := setupProgressGameTest(t, test.agreeWithOutput)
gameInfo.status = test.status
done := game.ProgressGame(context.Background())
require.True(t, done, "should be done")
require.Equal(t, 0, gameInfo.logCount, "should not log latest game state")
errLog := handler.FindLog(test.logLevel, test.logMsg)
require.NotNil(t, errLog, "should log game result")
require.Equal(t, test.status, errLog.GetContextValue("status"))
})
}
}
func setupProgressGameTest(t *testing.T, agreeWithProposedRoot bool) (*testlog.CapturingHandler, *GamePlayer, *stubActor, *stubGameInfo) {
logger := testlog.Logger(t, log.LvlDebug)
handler := &testlog.CapturingHandler{
Delegate: logger.GetHandler(),
}
logger.SetHandler(handler)
actor := &stubActor{}
gameInfo := &stubGameInfo{}
game := &GamePlayer{
agent: actor,
agreeWithProposedOutput: agreeWithProposedRoot,
caller: gameInfo,
logger: logger,
}
return handler, game, actor, gameInfo
}
type stubActor struct {
callCount int
err error
}
func (a *stubActor) Act(ctx context.Context) error {
a.callCount++
return a.err
}
type stubGameInfo struct {
status types.GameStatus
err error
logCount int
}
func (s *stubGameInfo) GetGameStatus(ctx context.Context) (types.GameStatus, error) {
return s.status, s.err
}
func (s *stubGameInfo) LogGameInfo(ctx context.Context) {
s.logCount++
}
...@@ -2,63 +2,95 @@ package fault ...@@ -2,63 +2,95 @@ package fault
import ( import (
"context" "context"
"fmt"
"math/big"
"time" "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
) )
type GameInfo interface { type gamePlayer interface {
GetGameStatus(context.Context) (types.GameStatus, error) ProgressGame(ctx context.Context) bool
LogGameInfo(ctx context.Context)
} }
type Actor interface { type playerCreator func(address common.Address) (gamePlayer, error)
Act(ctx context.Context) error type blockNumberFetcher func(ctx context.Context) (uint64, error)
// gameSource loads information about the games available to play
type gameSource interface {
FetchAllGamesAtBlock(ctx context.Context, blockNumber *big.Int) ([]FaultDisputeGame, error)
} }
func MonitorGame(ctx context.Context, logger log.Logger, agreeWithProposedOutput bool, actor Actor, caller GameInfo) error { type gameMonitor struct {
logger.Info("Monitoring fault dispute game", "agreeWithOutput", agreeWithProposedOutput) logger log.Logger
source gameSource
createPlayer playerCreator
fetchBlockNumber blockNumberFetcher
allowedGame common.Address
players map[common.Address]gamePlayer
}
for { func newGameMonitor(logger log.Logger, fetchBlockNumber blockNumberFetcher, allowedGame common.Address, source gameSource, createGame playerCreator) *gameMonitor {
done := progressGame(ctx, logger, agreeWithProposedOutput, actor, caller) return &gameMonitor{
if done { logger: logger,
return nil source: source,
createPlayer: createGame,
fetchBlockNumber: fetchBlockNumber,
allowedGame: allowedGame,
players: make(map[common.Address]gamePlayer),
}
}
func (m *gameMonitor) progressGames(ctx context.Context) error {
blockNum, err := m.fetchBlockNumber(ctx)
if err != nil {
return fmt.Errorf("failed to load current block number: %w", err)
}
games, err := m.source.FetchAllGamesAtBlock(ctx, new(big.Int).SetUint64(blockNum))
if err != nil {
return fmt.Errorf("failed to load games: %w", err)
}
for _, game := range games {
if m.allowedGame != (common.Address{}) && m.allowedGame != game.Proxy {
m.logger.Debug("Skipping game not on allow list", "game", game.Proxy)
continue
} }
select { player, err := m.fetchOrCreateGamePlayer(game)
case <-time.After(300 * time.Millisecond): if err != nil {
// Continue m.logger.Error("Error while progressing game", "game", game.Proxy, "err", err)
case <-ctx.Done(): continue
return ctx.Err()
} }
player.ProgressGame(ctx)
} }
return nil
} }
// progressGame checks the current state of the game, and attempts to progress it by performing moves, steps or resolving func (m *gameMonitor) fetchOrCreateGamePlayer(gameData FaultDisputeGame) (gamePlayer, error) {
// Returns true if the game is complete or false if it needs to be monitored further if player, ok := m.players[gameData.Proxy]; ok {
func progressGame(ctx context.Context, logger log.Logger, agreeWithProposedOutput bool, actor Actor, caller GameInfo) bool { return player, nil
logger.Trace("Checking if actions are required") }
if err := actor.Act(ctx); err != nil { player, err := m.createPlayer(gameData.Proxy)
logger.Error("Error when acting on game", "err", err) if err != nil {
return nil, fmt.Errorf("failed to create game player %v: %w", gameData.Proxy, err)
} }
if status, err := caller.GetGameStatus(ctx); err != nil { m.players[gameData.Proxy] = player
logger.Warn("Unable to retrieve game status", "err", err) return player, nil
} else if status != 0 { }
var expectedStatus types.GameStatus
if agreeWithProposedOutput { func (m *gameMonitor) MonitorGames(ctx context.Context) error {
expectedStatus = types.GameStatusChallengerWon m.logger.Info("Monitoring fault dispute games")
} else {
expectedStatus = types.GameStatusDefenderWon for {
err := m.progressGames(ctx)
if err != nil {
m.logger.Error("Failed to progress games", "err", err)
} }
if expectedStatus == status { select {
logger.Info("Game won", "status", status) case <-time.After(300 * time.Millisecond):
} else { // Continue
logger.Error("Game lost", "status", status) case <-ctx.Done():
return ctx.Err()
} }
return true
} else {
caller.LogGameInfo(ctx)
} }
return false
} }
...@@ -2,130 +2,123 @@ package fault ...@@ -2,130 +2,123 @@ package fault
import ( import (
"context" "context"
"errors" "math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum-optimism/optimism/op-node/testlog" "github.com/ethereum-optimism/optimism/op-node/testlog"
) )
func TestMonitorExitsWhenContextDone(t *testing.T) { func TestMonitorExitsWhenContextDone(t *testing.T) {
logger := testlog.Logger(t, log.LvlDebug) monitor, _, _ := setupMonitorTest(t, common.Address{})
actor := &stubActor{}
gameInfo := &stubGameInfo{}
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
cancel() cancel()
err := MonitorGame(ctx, logger, true, actor, gameInfo) err := monitor.MonitorGames(ctx)
require.ErrorIs(t, err, context.Canceled) require.ErrorIs(t, err, context.Canceled)
} }
func TestProgressGameAndLogState(t *testing.T) { func TestMonitorCreateAndProgressGameAgents(t *testing.T) {
logger, _, actor, gameInfo := setupProgressGameTest(t) monitor, source, games := setupMonitorTest(t, common.Address{})
done := progressGame(context.Background(), logger, true, actor, gameInfo)
require.False(t, done, "should not be done")
require.Equal(t, 1, actor.callCount, "should perform next actions")
require.Equal(t, 1, gameInfo.logCount, "should log latest game state")
}
func TestProgressGame_LogErrorFromAct(t *testing.T) { addr1 := common.Address{0xaa}
logger, handler, actor, gameInfo := setupProgressGameTest(t) addr2 := common.Address{0xbb}
actor.err = errors.New("Boom") source.games = []FaultDisputeGame{
done := progressGame(context.Background(), logger, true, actor, gameInfo)
require.False(t, done, "should not be done")
require.Equal(t, 1, actor.callCount, "should perform next actions")
require.Equal(t, 1, gameInfo.logCount, "should log latest game state")
errLog := handler.FindLog(log.LvlError, "Error when acting on game")
require.NotNil(t, errLog, "should log error")
require.Equal(t, actor.err, errLog.GetContextValue("err"))
}
func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
tests := []struct {
name string
status types.GameStatus
agreeWithOutput bool
logLevel log.Lvl
logMsg string
}{
{ {
name: "GameLostAsDefender", Proxy: addr1,
status: types.GameStatusChallengerWon, Timestamp: 9999,
agreeWithOutput: false,
logLevel: log.LvlError,
logMsg: "Game lost",
}, },
{ {
name: "GameLostAsChallenger", Proxy: addr2,
status: types.GameStatusDefenderWon, Timestamp: 9999,
agreeWithOutput: true,
logLevel: log.LvlError,
logMsg: "Game lost",
}, },
}
err := monitor.progressGames(context.Background())
require.NoError(t, err)
require.Len(t, games.created, 2, "should create game agents")
require.Contains(t, games.created, addr1)
require.Contains(t, games.created, addr2)
require.Equal(t, 1, games.created[addr1].progressCount)
require.Equal(t, 1, games.created[addr2].progressCount)
// The stub will fail the test if a game is created with the same address multiple times
require.NoError(t, monitor.progressGames(context.Background()), "should only create games once")
require.Equal(t, 2, games.created[addr1].progressCount)
require.Equal(t, 2, games.created[addr2].progressCount)
}
func TestMonitorOnlyCreateSpecifiedGame(t *testing.T) {
addr1 := common.Address{0xaa}
addr2 := common.Address{0xbb}
monitor, source, games := setupMonitorTest(t, addr2)
source.games = []FaultDisputeGame{
{ {
name: "GameWonAsDefender", Proxy: addr1,
status: types.GameStatusDefenderWon, Timestamp: 9999,
agreeWithOutput: false,
logLevel: log.LvlInfo,
logMsg: "Game won",
}, },
{ {
name: "GameWonAsChallenger", Proxy: addr2,
status: types.GameStatusChallengerWon, Timestamp: 9999,
agreeWithOutput: true,
logLevel: log.LvlInfo,
logMsg: "Game won",
}, },
} }
for _, test := range tests {
test := test err := monitor.progressGames(context.Background())
t.Run(test.name, func(t *testing.T) { require.NoError(t, err)
logger, handler, actor, gameInfo := setupProgressGameTest(t)
gameInfo.status = test.status require.Len(t, games.created, 1, "should only create allowed game")
require.Contains(t, games.created, addr2)
done := progressGame(context.Background(), logger, test.agreeWithOutput, actor, gameInfo) require.NotContains(t, games.created, addr1)
require.True(t, done, "should be done") require.Equal(t, 1, games.created[addr2].progressCount)
require.Equal(t, 0, gameInfo.logCount, "should not log latest game state")
errLog := handler.FindLog(test.logLevel, test.logMsg)
require.NotNil(t, errLog, "should log game result")
require.Equal(t, test.status, errLog.GetContextValue("status"))
})
}
} }
func setupProgressGameTest(t *testing.T) (log.Logger, *testlog.CapturingHandler, *stubActor, *stubGameInfo) { func setupMonitorTest(t *testing.T, allowedGame common.Address) (*gameMonitor, *stubGameSource, *createdGames) {
logger := testlog.Logger(t, log.LvlDebug) logger := testlog.Logger(t, log.LvlDebug)
handler := &testlog.CapturingHandler{ source := &stubGameSource{}
Delegate: logger.GetHandler(), games := &createdGames{
t: t,
created: make(map[common.Address]*stubGame),
} }
logger.SetHandler(handler) fetchBlockNum := func(ctx context.Context) (uint64, error) {
actor := &stubActor{} return 1234, nil
gameInfo := &stubGameInfo{} }
return logger, handler, actor, gameInfo monitor := newGameMonitor(logger, fetchBlockNum, allowedGame, source, games.CreateGame)
return monitor, source, games
}
type stubGameSource struct {
games []FaultDisputeGame
} }
type stubActor struct { func (s *stubGameSource) FetchAllGamesAtBlock(ctx context.Context, blockNumber *big.Int) ([]FaultDisputeGame, error) {
callCount int return s.games, nil
err error
} }
func (a *stubActor) Act(ctx context.Context) error { type stubGame struct {
a.callCount++ addr common.Address
return a.err progressCount int
done bool
} }
type stubGameInfo struct { func (g *stubGame) ProgressGame(ctx context.Context) bool {
status types.GameStatus g.progressCount++
err error return g.done
logCount int
} }
func (s *stubGameInfo) GetGameStatus(ctx context.Context) (types.GameStatus, error) { type createdGames struct {
return s.status, s.err t *testing.T
created map[common.Address]*stubGame
} }
func (s *stubGameInfo) LogGameInfo(ctx context.Context) { func (c *createdGames) CreateGame(addr common.Address) (gamePlayer, error) {
s.logCount++ if _, exists := c.created[addr]; exists {
c.t.Fatalf("game %v already exists", addr)
}
game := &stubGame{addr: addr}
c.created[addr] = game
return game, nil
} }
...@@ -7,16 +7,14 @@ import ( ...@@ -7,16 +7,14 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/fault/cannon"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/metrics" "github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-challenger/version" "github.com/ethereum-optimism/optimism/op-challenger/version"
"github.com/ethereum-optimism/optimism/op-service/client" "github.com/ethereum-optimism/optimism/op-service/client"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
"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/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -28,11 +26,9 @@ type Service interface { ...@@ -28,11 +26,9 @@ type Service interface {
} }
type service struct { type service struct {
agent *Agent logger log.Logger
agreeWithProposedOutput bool metrics metrics.Metricer
caller *FaultCaller monitor *gameMonitor
logger log.Logger
metrics metrics.Metricer
} }
// NewService creates a new Service. // NewService creates a new Service.
...@@ -69,78 +65,23 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se ...@@ -69,78 +65,23 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se
m.StartBalanceMetrics(ctx, logger, client, txMgr.From()) m.StartBalanceMetrics(ctx, logger, client, txMgr.From())
} }
contract, err := bindings.NewFaultDisputeGameCaller(cfg.GameAddress, client) factory, err := bindings.NewDisputeGameFactory(cfg.GameFactoryAddress, client)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to bind the fault dispute game contract: %w", err) return nil, fmt.Errorf("failed to bind the fault dispute game factory contract: %w", err)
} }
loader := NewGameLoader(factory)
loader := NewLoader(contract) monitor := newGameMonitor(logger, client.BlockNumber, cfg.GameAddress, loader, func(addr common.Address) (gamePlayer, error) {
return NewGamePlayer(ctx, logger, cfg, addr, txMgr, client)
})
gameDepth, err := loader.FetchGameDepth(ctx) m.RecordInfo(version.SimpleWithMeta)
if err != nil { m.RecordUp()
return nil, fmt.Errorf("failed to fetch the game depth: %w", err)
}
gameDepth = uint64(gameDepth)
var trace types.TraceProvider
var updater types.OracleUpdater
switch cfg.TraceType {
case config.TraceTypeCannon:
trace, err = cannon.NewTraceProvider(ctx, logger, cfg, client)
if err != nil {
return nil, fmt.Errorf("create cannon trace provider: %w", err)
}
updater, err = cannon.NewOracleUpdater(ctx, logger, txMgr, cfg.GameAddress, client)
if err != nil {
return nil, fmt.Errorf("failed to create the cannon updater: %w", err)
}
case config.TraceTypeAlphabet:
trace = alphabet.NewTraceProvider(cfg.AlphabetTrace, gameDepth)
updater = alphabet.NewOracleUpdater(logger)
default:
return nil, fmt.Errorf("unsupported trace type: %v", cfg.TraceType)
}
return newTypedService(ctx, logger, cfg, loader, gameDepth, client, trace, updater, txMgr, m)
}
// newTypedService creates a new Service from a provided trace provider.
func newTypedService(ctx context.Context,
logger log.Logger,
cfg *config.Config,
loader Loader,
gameDepth uint64,
client *ethclient.Client,
provider types.TraceProvider,
updater types.OracleUpdater,
txMgr txmgr.TxManager,
metrics metrics.Metricer,
) (*service, error) {
if err := ValidateAbsolutePrestate(ctx, provider, loader); err != nil {
return nil, fmt.Errorf("failed to validate absolute prestate: %w", err)
}
gameLogger := logger.New("game", cfg.GameAddress)
responder, err := NewFaultResponder(gameLogger, txMgr, cfg.GameAddress)
if err != nil {
return nil, fmt.Errorf("failed to create the responder: %w", err)
}
caller, err := NewFaultCallerFromBindings(cfg.GameAddress, client, gameLogger)
if err != nil {
return nil, fmt.Errorf("failed to bind the fault contract: %w", err)
}
metrics.RecordInfo(version.SimpleWithMeta)
metrics.RecordUp()
return &service{ return &service{
agent: NewAgent(loader, int(gameDepth), provider, responder, updater, cfg.AgreeWithProposedOutput, gameLogger), logger: logger,
agreeWithProposedOutput: cfg.AgreeWithProposedOutput, metrics: m,
caller: caller, monitor: monitor,
logger: gameLogger,
metrics: metrics,
}, nil }, nil
} }
...@@ -163,5 +104,5 @@ func ValidateAbsolutePrestate(ctx context.Context, trace types.TraceProvider, lo ...@@ -163,5 +104,5 @@ func ValidateAbsolutePrestate(ctx context.Context, trace types.TraceProvider, lo
// MonitorGame monitors the fault dispute game and attempts to progress it. // MonitorGame monitors the fault dispute game and attempts to progress it.
func (s *service) MonitorGame(ctx context.Context) error { func (s *service) MonitorGame(ctx context.Context) error {
return MonitorGame(ctx, s.logger, s.agreeWithProposedOutput, s.agent, s.caller) return s.monitor.MonitorGames(ctx)
} }
...@@ -38,7 +38,7 @@ func (g *CannonGameHelper) CreateHonestActor(ctx context.Context, rollupCfg *rol ...@@ -38,7 +38,7 @@ func (g *CannonGameHelper) CreateHonestActor(ctx context.Context, rollupCfg *rol
} }
opts = append(opts, options...) opts = append(opts, options...)
cfg := challenger.NewChallengerConfig(g.t, l1Endpoint, opts...) cfg := challenger.NewChallengerConfig(g.t, l1Endpoint, opts...)
provider, err := cannon.NewTraceProvider(ctx, testlog.Logger(g.t, log.LvlInfo).New("role", "CorrectTrace"), cfg, l1Client) provider, err := cannon.NewTraceProvider(ctx, testlog.Logger(g.t, log.LvlInfo).New("role", "CorrectTrace"), cfg, l1Client, g.addr)
g.require.NoError(err, "create cannon trace provider") g.require.NoError(err, "create cannon trace provider")
return &HonestHelper{ return &HonestHelper{
......
...@@ -12,8 +12,7 @@ import ( ...@@ -12,8 +12,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestCannonMultipleGames(t *testing.T) { func TestMultipleAlphabetGames(t *testing.T) {
t.Skip("Challenger doesn't yet support multiple games")
InitParallel(t) InitParallel(t)
ctx := context.Background() ctx := context.Background()
...@@ -41,6 +40,13 @@ func TestCannonMultipleGames(t *testing.T) { ...@@ -41,6 +40,13 @@ func TestCannonMultipleGames(t *testing.T) {
game2.WaitForClaimCount(ctx, 4) game2.WaitForClaimCount(ctx, 4)
game1.Defend(ctx, 1, common.Hash{0xaa}) game1.Defend(ctx, 1, common.Hash{0xaa})
game1.WaitForClaimCount(ctx, 4) game1.WaitForClaimCount(ctx, 4)
gameDuration := game1.GameDuration(ctx)
sys.TimeTravelClock.AdvanceTime(gameDuration)
require.NoError(t, wait.ForNextBlock(ctx, l1Client))
game1.WaitForGameStatus(ctx, disputegame.StatusChallengerWins)
game2.WaitForGameStatus(ctx, disputegame.StatusChallengerWins)
} }
func TestMultipleCannonGames(t *testing.T) { func TestMultipleCannonGames(t *testing.T) {
......
...@@ -63,11 +63,15 @@ var Subcommands = cli.Commands{ ...@@ -63,11 +63,15 @@ var Subcommands = cli.Commands{
config.SetDeployments(deployments) config.SetDeployments(deployments)
} }
// Do the check after setting the deployments
if err := config.Check(); err != nil { if err := config.Check(); err != nil {
return fmt.Errorf("deploy config at %s invalid: %w", deployConfig, err) return fmt.Errorf("deploy config at %s invalid: %w", deployConfig, err)
} }
// Check the addresses after setting the deployments
if err := config.CheckAddresses(); err != nil {
return fmt.Errorf("deploy config at %s invalid: %w", deployConfig, err)
}
var dump *state.Dump var dump *state.Dump
if l1Allocs := ctx.String("l1-allocs"); l1Allocs != "" { if l1Allocs := ctx.String("l1-allocs"); l1Allocs != "" {
dump, err = genesis.NewStateDump(l1Allocs) dump, err = genesis.NewStateDump(l1Allocs)
......
...@@ -34,31 +34,31 @@ ...@@ -34,31 +34,31 @@
"url": "https://github.com/ethereum-optimism/optimism.git" "url": "https://github.com/ethereum-optimism/optimism.git"
}, },
"dependencies": { "dependencies": {
"@eth-optimism/core-utils": "0.12.3", "@eth-optimism/core-utils": "workspace:*",
"@sentry/node": "^6.3.1", "@sentry/node": "^7.64.0",
"bcfg": "^0.1.7", "bcfg": "^0.2.1",
"body-parser": "^1.20.0", "body-parser": "^1.20.2",
"commander": "^9.0.0", "commander": "^11.0.0",
"dotenv": "^16.0.0", "dotenv": "^16.3.1",
"envalid": "^7.2.2", "envalid": "^7.3.1",
"ethers": "^5.7.0", "ethers": "^5.7.2",
"express": "^4.17.1", "express": "^4.18.2",
"express-prom-bundle": "^6.4.1", "express-prom-bundle": "^6.6.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"pino": "^8.15.0", "pino": "^8.15.0",
"pino-multi-stream": "^5.3.0", "pino-multi-stream": "^6.0.0",
"pino-sentry": "^0.14.0", "pino-sentry": "^0.14.0",
"prom-client": "^13.1.0" "prom-client": "^14.2.0"
}, },
"devDependencies": { "devDependencies": {
"@ethersproject/abstract-provider": "^5.7.0", "@ethersproject/abstract-provider": "^5.7.0",
"@ethersproject/abstract-signer": "^5.7.0", "@ethersproject/abstract-signer": "^5.7.0",
"@types/express": "^4.17.13", "@types/express": "^4.17.17",
"@types/morgan": "^1.9.3", "@types/morgan": "^1.9.4",
"@types/pino": "^6.3.6", "@types/pino": "^7.0.5",
"@types/pino-multi-stream": "^5.1.1", "@types/pino-multi-stream": "^5.1.3",
"chai": "^4.3.4", "chai": "^4.3.7",
"supertest": "^6.1.4" "supertest": "^6.3.3"
} }
} }
{ {
"extends": "../../tsconfig.json", "extends": "../../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"rootDir": "./src", "rootDir": "./src",
"outDir": "./dist" "outDir": "./dist",
"skipLibCheck": true
}, },
"include": [ "include": [
......
...@@ -25,6 +25,7 @@ Bytes_slice_Test:test_slice_fromZeroIdx_works() (gas: 20804) ...@@ -25,6 +25,7 @@ Bytes_slice_Test:test_slice_fromZeroIdx_works() (gas: 20804)
Bytes_toNibbles_Test:test_toNibbles_expectedResult128Bytes_works() (gas: 78882) Bytes_toNibbles_Test:test_toNibbles_expectedResult128Bytes_works() (gas: 78882)
Bytes_toNibbles_Test:test_toNibbles_expectedResult5Bytes_works() (gas: 3992) Bytes_toNibbles_Test:test_toNibbles_expectedResult5Bytes_works() (gas: 3992)
Bytes_toNibbles_Test:test_toNibbles_zeroLengthInput_works() (gas: 823) Bytes_toNibbles_Test:test_toNibbles_zeroLengthInput_works() (gas: 823)
Constants_Test:test_eip1967Constants_succeeds() (gas: 453)
CrossDomainMessenger_BaseGas_Test:test_baseGas_succeeds() (gas: 20435) CrossDomainMessenger_BaseGas_Test:test_baseGas_succeeds() (gas: 20435)
CrossDomainOwnable2_Test:test_onlyOwner_notMessenger_reverts() (gas: 8461) CrossDomainOwnable2_Test:test_onlyOwner_notMessenger_reverts() (gas: 8461)
CrossDomainOwnable2_Test:test_onlyOwner_notOwner2_reverts() (gas: 57515) CrossDomainOwnable2_Test:test_onlyOwner_notOwner2_reverts() (gas: 57515)
...@@ -239,7 +240,7 @@ L2OutputOracleUpgradeable_Test:test_initValuesOnImpl_succeeds() (gas: 23902) ...@@ -239,7 +240,7 @@ L2OutputOracleUpgradeable_Test:test_initValuesOnImpl_succeeds() (gas: 23902)
L2OutputOracleUpgradeable_Test:test_initValuesOnProxy_succeeds() (gas: 46800) L2OutputOracleUpgradeable_Test:test_initValuesOnProxy_succeeds() (gas: 46800)
L2OutputOracleUpgradeable_Test:test_initializeImpl_alreadyInitialized_reverts() (gas: 15216) L2OutputOracleUpgradeable_Test:test_initializeImpl_alreadyInitialized_reverts() (gas: 15216)
L2OutputOracleUpgradeable_Test:test_initializeProxy_alreadyInitialized_reverts() (gas: 20216) L2OutputOracleUpgradeable_Test:test_initializeProxy_alreadyInitialized_reverts() (gas: 20216)
L2OutputOracleUpgradeable_Test:test_upgrading_succeeds() (gas: 191346) L2OutputOracleUpgradeable_Test:test_upgrading_succeeds() (gas: 191348)
L2OutputOracle_constructor_Test:test_constructor_l2BlockTimeZero_reverts() (gas: 39212) L2OutputOracle_constructor_Test:test_constructor_l2BlockTimeZero_reverts() (gas: 39212)
L2OutputOracle_constructor_Test:test_constructor_submissionInterval_reverts() (gas: 39222) L2OutputOracle_constructor_Test:test_constructor_submissionInterval_reverts() (gas: 39222)
L2OutputOracle_constructor_Test:test_constructor_succeeds() (gas: 51777) L2OutputOracle_constructor_Test:test_constructor_succeeds() (gas: 51777)
...@@ -430,7 +431,7 @@ OptimismMintableTokenFactory_Test:test_createStandardL2Token_succeeds() (gas: 12 ...@@ -430,7 +431,7 @@ OptimismMintableTokenFactory_Test:test_createStandardL2Token_succeeds() (gas: 12
OptimismPortalUpgradeable_Test:test_initialize_cannotInitImpl_reverts() (gas: 11178) OptimismPortalUpgradeable_Test:test_initialize_cannotInitImpl_reverts() (gas: 11178)
OptimismPortalUpgradeable_Test:test_initialize_cannotInitProxy_reverts() (gas: 16111) OptimismPortalUpgradeable_Test:test_initialize_cannotInitProxy_reverts() (gas: 16111)
OptimismPortalUpgradeable_Test:test_params_initValuesOnProxy_succeeds() (gas: 26781) OptimismPortalUpgradeable_Test:test_params_initValuesOnProxy_succeeds() (gas: 26781)
OptimismPortalUpgradeable_Test:test_upgradeToAndCall_upgrading_succeeds() (gas: 191412) OptimismPortalUpgradeable_Test:test_upgradeToAndCall_upgrading_succeeds() (gas: 191414)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifOutputRootChanges_reverts() (gas: 178667) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifOutputRootChanges_reverts() (gas: 178667)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifOutputTimestampIsNotFinalized_reverts() (gas: 182306) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifOutputTimestampIsNotFinalized_reverts() (gas: 182306)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifWithdrawalNotProven_reverts() (gas: 41780) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifWithdrawalNotProven_reverts() (gas: 41780)
...@@ -533,11 +534,11 @@ ProxyAdmin_Test:test_delegateResolvedGetProxyAdmin_succeeds() (gas: 17691) ...@@ -533,11 +534,11 @@ ProxyAdmin_Test:test_delegateResolvedGetProxyAdmin_succeeds() (gas: 17691)
ProxyAdmin_Test:test_delegateResolvedGetProxyImplementation_succeeds() (gas: 62028) ProxyAdmin_Test:test_delegateResolvedGetProxyImplementation_succeeds() (gas: 62028)
ProxyAdmin_Test:test_delegateResolvedUpgradeAndCall_succeeds() (gas: 98039) ProxyAdmin_Test:test_delegateResolvedUpgradeAndCall_succeeds() (gas: 98039)
ProxyAdmin_Test:test_delegateResolvedUpgrade_succeeds() (gas: 58482) ProxyAdmin_Test:test_delegateResolvedUpgrade_succeeds() (gas: 58482)
ProxyAdmin_Test:test_erc1967ChangeProxyAdmin_succeeds() (gas: 33812) ProxyAdmin_Test:test_erc1967ChangeProxyAdmin_succeeds() (gas: 33820)
ProxyAdmin_Test:test_erc1967GetProxyAdmin_succeeds() (gas: 15616) ProxyAdmin_Test:test_erc1967GetProxyAdmin_succeeds() (gas: 15616)
ProxyAdmin_Test:test_erc1967GetProxyImplementation_succeeds() (gas: 52071) ProxyAdmin_Test:test_erc1967GetProxyImplementation_succeeds() (gas: 52073)
ProxyAdmin_Test:test_erc1967UpgradeAndCall_succeeds() (gas: 78969) ProxyAdmin_Test:test_erc1967UpgradeAndCall_succeeds() (gas: 78971)
ProxyAdmin_Test:test_erc1967Upgrade_succeeds() (gas: 50078) ProxyAdmin_Test:test_erc1967Upgrade_succeeds() (gas: 50080)
ProxyAdmin_Test:test_isUpgrading_succeeds() (gas: 19442) ProxyAdmin_Test:test_isUpgrading_succeeds() (gas: 19442)
ProxyAdmin_Test:test_onlyOwner_notOwner_reverts() (gas: 22767) ProxyAdmin_Test:test_onlyOwner_notOwner_reverts() (gas: 22767)
ProxyAdmin_Test:test_owner_succeeds() (gas: 9738) ProxyAdmin_Test:test_owner_succeeds() (gas: 9738)
...@@ -547,16 +548,16 @@ ProxyAdmin_Test:test_setImplementationName_notOwner_reverts() (gas: 11111) ...@@ -547,16 +548,16 @@ ProxyAdmin_Test:test_setImplementationName_notOwner_reverts() (gas: 11111)
ProxyAdmin_Test:test_setImplementationName_succeeds() (gas: 38945) ProxyAdmin_Test:test_setImplementationName_succeeds() (gas: 38945)
ProxyAdmin_Test:test_setProxyType_notOwner_reverts() (gas: 10814) ProxyAdmin_Test:test_setProxyType_notOwner_reverts() (gas: 10814)
Proxy_Test:test_delegatesToImpl_succeeds() (gas: 45207) Proxy_Test:test_delegatesToImpl_succeeds() (gas: 45207)
Proxy_Test:test_implementationKey_succeeds() (gas: 20909) Proxy_Test:test_implementationKey_succeeds() (gas: 20911)
Proxy_Test:test_implementation_isZeroAddress_reverts() (gas: 47626) Proxy_Test:test_implementation_isZeroAddress_reverts() (gas: 47628)
Proxy_Test:test_implementation_zeroAddressCaller_succeeds() (gas: 14752) Proxy_Test:test_implementation_zeroAddressCaller_succeeds() (gas: 14752)
Proxy_Test:test_ownerKey_succeeds() (gas: 19059) Proxy_Test:test_ownerKey_succeeds() (gas: 19067)
Proxy_Test:test_ownerProxyCall_notAdmin_succeeds() (gas: 34615) Proxy_Test:test_ownerProxyCall_notAdmin_succeeds() (gas: 34623)
Proxy_Test:test_proxyCallToImp_notAdmin_succeeds() (gas: 30008) Proxy_Test:test_proxyCallToImp_notAdmin_succeeds() (gas: 30010)
Proxy_Test:test_upgradeToAndCall_functionDoesNotExist_reverts() (gas: 104565) Proxy_Test:test_upgradeToAndCall_functionDoesNotExist_reverts() (gas: 104567)
Proxy_Test:test_upgradeToAndCall_isPayable_succeeds() (gas: 53742) Proxy_Test:test_upgradeToAndCall_isPayable_succeeds() (gas: 53744)
Proxy_Test:test_upgradeToAndCall_succeeds() (gas: 125190) Proxy_Test:test_upgradeToAndCall_succeeds() (gas: 125192)
Proxy_Test:test_upgradeTo_clashingFunctionSignatures_succeeds() (gas: 101359) Proxy_Test:test_upgradeTo_clashingFunctionSignatures_succeeds() (gas: 101363)
RLPReader_readBytes_Test:test_readBytes_bytestring00_succeeds() (gas: 1863) RLPReader_readBytes_Test:test_readBytes_bytestring00_succeeds() (gas: 1863)
RLPReader_readBytes_Test:test_readBytes_bytestring01_succeeds() (gas: 1840) RLPReader_readBytes_Test:test_readBytes_bytestring01_succeeds() (gas: 1840)
RLPReader_readBytes_Test:test_readBytes_bytestring7f_succeeds() (gas: 1861) RLPReader_readBytes_Test:test_readBytes_bytestring7f_succeeds() (gas: 1861)
...@@ -631,7 +632,7 @@ ResourceMetering_Test:test_meter_useMax_succeeds() (gas: 20020816) ...@@ -631,7 +632,7 @@ ResourceMetering_Test:test_meter_useMax_succeeds() (gas: 20020816)
ResourceMetering_Test:test_meter_useMoreThanMax_reverts() (gas: 19549) ResourceMetering_Test:test_meter_useMoreThanMax_reverts() (gas: 19549)
SafeCall_Test:test_callWithMinGas_noLeakageHigh_succeeds() (gas: 1021670598) SafeCall_Test:test_callWithMinGas_noLeakageHigh_succeeds() (gas: 1021670598)
SafeCall_Test:test_callWithMinGas_noLeakageLow_succeeds() (gas: 1095190710) SafeCall_Test:test_callWithMinGas_noLeakageLow_succeeds() (gas: 1095190710)
Semver_Test:test_behindProxy_succeeds() (gas: 506748) Semver_Test:test_behindProxy_succeeds() (gas: 507558)
Semver_Test:test_version_succeeds() (gas: 9418) Semver_Test:test_version_succeeds() (gas: 9418)
SequencerFeeVault_L2Withdrawal_Test:test_withdraw_toL2_succeeds() (gas: 78333) SequencerFeeVault_L2Withdrawal_Test:test_withdraw_toL2_succeeds() (gas: 78333)
SequencerFeeVault_L2Withdrawal_Test:test_withdraw_toL2recipientReverts_fails() (gas: 46486) SequencerFeeVault_L2Withdrawal_Test:test_withdraw_toL2recipientReverts_fails() (gas: 46486)
...@@ -643,11 +644,11 @@ SequencerFeeVault_Test:test_withdraw_toL1_succeeds() (gas: 171357) ...@@ -643,11 +644,11 @@ SequencerFeeVault_Test:test_withdraw_toL1_succeeds() (gas: 171357)
SetPrevBaseFee_Test:test_setPrevBaseFee_succeeds() (gas: 11549) SetPrevBaseFee_Test:test_setPrevBaseFee_succeeds() (gas: 11549)
StandardBridge_Stateless_Test:test_isCorrectTokenPair_succeeds() (gas: 49936) StandardBridge_Stateless_Test:test_isCorrectTokenPair_succeeds() (gas: 49936)
StandardBridge_Stateless_Test:test_isOptimismMintableERC20_succeeds() (gas: 33072) StandardBridge_Stateless_Test:test_isOptimismMintableERC20_succeeds() (gas: 33072)
SystemConfig_Initialize_Test:test_initialize_events_succeeds() (gas: 72059) SystemConfig_Initialize_Test:test_initialize_events_succeeds() (gas: 72060)
SystemConfig_Initialize_Test:test_initialize_startBlockOverride_succeeds() (gas: 65240) SystemConfig_Initialize_Test:test_initialize_startBlockOverride_succeeds() (gas: 65242)
SystemConfig_Initialize_Test:test_initialize_values_succeeds() (gas: 64946) SystemConfig_Initialize_Test:test_initialize_values_succeeds() (gas: 64946)
SystemConfig_Initialize_TestFail:test_initialize_lowGasLimit_reverts() (gas: 55653) SystemConfig_Initialize_TestFail:test_initialize_lowGasLimit_reverts() (gas: 55655)
SystemConfig_Initialize_TestFail:test_initialize_startBlock_reverts() (gas: 78350) SystemConfig_Initialize_TestFail:test_initialize_startBlock_reverts() (gas: 78352)
SystemConfig_Setters_TestFail:test_setBatcherHash_notOwner_reverts() (gas: 15607) SystemConfig_Setters_TestFail:test_setBatcherHash_notOwner_reverts() (gas: 15607)
SystemConfig_Setters_TestFail:test_setGasConfig_notOwner_reverts() (gas: 15577) SystemConfig_Setters_TestFail:test_setGasConfig_notOwner_reverts() (gas: 15577)
SystemConfig_Setters_TestFail:test_setGasLimit_notOwner_reverts() (gas: 15676) SystemConfig_Setters_TestFail:test_setGasLimit_notOwner_reverts() (gas: 15676)
......
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
"l1StartingBlockTag": "earliest", "l1StartingBlockTag": "earliest",
"l1ChainID": 900, "l1ChainID": 900,
"l2ChainID": 901, "l2ChainID": 901,
"l2BlockTime": 2, "l2BlockTime": 1,
"l1BlockTime": 2,
"maxSequencerDrift": 300, "maxSequencerDrift": 300,
"sequencerWindowSize": 15, "sequencerWindowSize": 15,
"channelTimeout": 40, "channelTimeout": 40,
...@@ -35,7 +36,6 @@ ...@@ -35,7 +36,6 @@
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "governanceTokenOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
"finalizationPeriodSeconds": 2, "finalizationPeriodSeconds": 2,
"numDeployConfirmations": 1,
"eip1559Denominator": 50, "eip1559Denominator": 50,
"eip1559Elasticity": 10, "eip1559Elasticity": 10,
"l2GenesisRegolithTimeOffset": "0x0", "l2GenesisRegolithTimeOffset": "0x0",
......
{ {
"finalSystemOwner": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF", "finalSystemOwner": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF",
"portalGuardian": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF", "portalGuardian": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF",
"controller": "0x2d30335B0b807bBa1682C487BaAFD2Ad6da5D675",
"l1StartingBlockTag": "0x19c7e6b18fe156e45f4cfef707294fd8f079fa9c30a7b7cd6ec1ce3682ec6a2e", "l1StartingBlockTag": "0x19c7e6b18fe156e45f4cfef707294fd8f079fa9c30a7b7cd6ec1ce3682ec6a2e",
"l1ChainID": 5, "l1ChainID": 5,
"l2ChainID": 998, "l2ChainID": 998,
"l2BlockTime": 2, "l2BlockTime": 2,
"l1BlockTime": 12,
"maxSequencerDrift": 1200, "maxSequencerDrift": 1200,
"sequencerWindowSize": 3600, "sequencerWindowSize": 3600,
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
"l1ChainID": 1, "l1ChainID": 1,
"l2ChainID": 10, "l2ChainID": 10,
"l2BlockTime": 2, "l2BlockTime": 2,
"l1BlockTime": 12,
"maxSequencerDrift": 600, "maxSequencerDrift": 600,
"sequencerWindowSize": 3600, "sequencerWindowSize": 3600,
"channelTimeout": 300, "channelTimeout": 300,
...@@ -32,7 +33,6 @@ ...@@ -32,7 +33,6 @@
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenOwner": "0x5C4e7Ba1E219E47948e6e3F55019A647bA501005", "governanceTokenOwner": "0x5C4e7Ba1E219E47948e6e3F55019A647bA501005",
"l2GenesisBlockGasLimit": "0x1c9c380", "l2GenesisBlockGasLimit": "0x1c9c380",
"l2GenesisBlockCoinbase": "0x4200000000000000000000000000000000000011",
"l2GenesisBlockBaseFeePerGas": "0x3b9aca00", "l2GenesisBlockBaseFeePerGas": "0x3b9aca00",
"gasPriceOracleOverhead": 188, "gasPriceOracleOverhead": 188,
"gasPriceOracleScalar": 684000, "gasPriceOracleScalar": 684000,
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
"gas-snapshot": "pnpm build:differential && pnpm build:fuzz && forge snapshot --no-match-test 'testDiff|testFuzz|invariant|generateArtifact'", "gas-snapshot": "pnpm build:differential && pnpm build:fuzz && forge snapshot --no-match-test 'testDiff|testFuzz|invariant|generateArtifact'",
"storage-snapshot": "./scripts/storage-snapshot.sh", "storage-snapshot": "./scripts/storage-snapshot.sh",
"semver-lock": "forge script scripts/SemverLock.s.sol", "semver-lock": "forge script scripts/SemverLock.s.sol",
"validate-deploy-configs": "./scripts/validate-deploy-configs.sh", "validate-deploy-configs": "./scripts/check-deploy-configs.sh",
"validate-spacers": "pnpm build && npx tsx scripts/validate-spacers.ts", "validate-spacers": "pnpm build && npx tsx scripts/validate-spacers.ts",
"slither": "./scripts/slither.sh", "slither": "./scripts/slither.sh",
"slither:triage": "TRIAGE_MODE=1 ./scripts/slither.sh", "slither:triage": "TRIAGE_MODE=1 ./scripts/slither.sh",
......
#!/usr/bin/env bash
# This script is used to check for valid deploy configs.
# It should check all configs and return a non-zero exit code if any of them are invalid.
# getting-started.json isn't valid JSON so its skipped.
code=$?
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
CONTRACTS_BASE=$(dirname $SCRIPT_DIR)
MONOREPO_BASE=$(dirname $(dirname $CONTRACTS_BASE))
for config in $CONTRACTS_BASE/deploy-config/*.json; do
if grep -q "getting-started" <<< "$config"; then
echo "Skipping getting-started.json"
continue
fi
go run $MONOREPO_BASE/op-chain-ops/cmd/check-deploy-config/main.go --path $config
[ $? -eq 0 ] || code=$?
done
exit $code
...@@ -83,7 +83,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -83,7 +83,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
bytes calldata _extraData bytes calldata _extraData
) )
external external
returns (IDisputeGame proxy) returns (IDisputeGame proxy_)
{ {
// Grab the implementation contract for the given `GameType`. // Grab the implementation contract for the given `GameType`.
IDisputeGame impl = gameImpls[_gameType]; IDisputeGame impl = gameImpls[_gameType];
...@@ -92,8 +92,8 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -92,8 +92,8 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
if (address(impl) == address(0)) revert NoImplementation(_gameType); if (address(impl) == address(0)) revert NoImplementation(_gameType);
// Clone the implementation contract and initialize it with the given parameters. // Clone the implementation contract and initialize it with the given parameters.
proxy = IDisputeGame(address(impl).clone(abi.encodePacked(_rootClaim, _extraData))); proxy_ = IDisputeGame(address(impl).clone(abi.encodePacked(_rootClaim, _extraData)));
proxy.initialize(); proxy_.initialize();
// Compute the unique identifier for the dispute game. // Compute the unique identifier for the dispute game.
Hash uuid = getGameUUID(_gameType, _rootClaim, _extraData); Hash uuid = getGameUUID(_gameType, _rootClaim, _extraData);
...@@ -101,12 +101,12 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -101,12 +101,12 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
// If a dispute game with the same UUID already exists, revert. // If a dispute game with the same UUID already exists, revert.
if (GameId.unwrap(_disputeGames[uuid]) != bytes32(0)) revert GameAlreadyExists(uuid); if (GameId.unwrap(_disputeGames[uuid]) != bytes32(0)) revert GameAlreadyExists(uuid);
GameId id = LibGameId.pack(_gameType, Timestamp.wrap(uint64(block.timestamp)), proxy); GameId id = LibGameId.pack(_gameType, Timestamp.wrap(uint64(block.timestamp)), proxy_);
// Store the dispute game id in the mapping & emit the `DisputeGameCreated` event. // Store the dispute game id in the mapping & emit the `DisputeGameCreated` event.
_disputeGames[uuid] = id; _disputeGames[uuid] = id;
_disputeGameList.push(id); _disputeGameList.push(id);
emit DisputeGameCreated(address(proxy), _gameType, _rootClaim); emit DisputeGameCreated(address(proxy_), _gameType, _rootClaim);
} }
/// @inheritdoc IDisputeGameFactory /// @inheritdoc IDisputeGameFactory
...@@ -117,7 +117,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -117,7 +117,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
) )
public public
pure pure
returns (Hash _uuid) returns (Hash uuid_)
{ {
assembly { assembly {
// Grab the offsets of the other memory locations we will need to temporarily overwrite. // Grab the offsets of the other memory locations we will need to temporarily overwrite.
...@@ -141,7 +141,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -141,7 +141,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
let hashLen := and(add(mload(_extraData), 0x9F), not(0x1F)) let hashLen := and(add(mload(_extraData), 0x9F), not(0x1F))
// Hash the memory to produce the UUID digest // Hash the memory to produce the UUID digest
_uuid := keccak256(gameTypeOffset, hashLen) uuid_ := keccak256(gameTypeOffset, hashLen)
// Restore the memory prior to `extraData` // Restore the memory prior to `extraData`
mstore(gameTypeOffset, tempA) mstore(gameTypeOffset, tempA)
......
...@@ -26,16 +26,16 @@ interface IDisputeGameFactory { ...@@ -26,16 +26,16 @@ interface IDisputeGameFactory {
/// @notice `games` queries an internal mapping that maps the hash of /// @notice `games` queries an internal mapping that maps the hash of
/// `gameType ++ rootClaim ++ extraData` to the deployed `DisputeGame` clone. /// `gameType ++ rootClaim ++ extraData` to the deployed `DisputeGame` clone.
/// @dev `++` equates to concatenation. /// @dev `++` equates to concatenation.
/// @param gameType The type of the DisputeGame - used to decide the proxy implementation /// @param _gameType The type of the DisputeGame - used to decide the proxy implementation
/// @param rootClaim The root claim of the DisputeGame. /// @param _rootClaim The root claim of the DisputeGame.
/// @param extraData Any extra data that should be provided to the created dispute game. /// @param _extraData Any extra data that should be provided to the created dispute game.
/// @return proxy_ The clone of the `DisputeGame` created with the given parameters. /// @return proxy_ The clone of the `DisputeGame` created with the given parameters.
/// Returns `address(0)` if nonexistent. /// Returns `address(0)` if nonexistent.
/// @return timestamp_ The timestamp of the creation of the dispute game. /// @return timestamp_ The timestamp of the creation of the dispute game.
function games( function games(
GameType gameType, GameType _gameType,
Claim rootClaim, Claim _rootClaim,
bytes calldata extraData bytes calldata _extraData
) )
external external
view view
...@@ -56,9 +56,9 @@ interface IDisputeGameFactory { ...@@ -56,9 +56,9 @@ interface IDisputeGameFactory {
/// @notice `gameImpls` is a mapping that maps `GameType`s to their respective /// @notice `gameImpls` is a mapping that maps `GameType`s to their respective
/// `IDisputeGame` implementations. /// `IDisputeGame` implementations.
/// @param _gameType The type of the dispute game. /// @param _gameType The type of the dispute game.
/// @return _impl The address of the implementation of the game type. /// @return impl_ The address of the implementation of the game type.
/// Will be cloned on creation of a new dispute game with the given `gameType`. /// Will be cloned on creation of a new dispute game with the given `gameType`.
function gameImpls(GameType _gameType) external view returns (IDisputeGame _impl); function gameImpls(GameType _gameType) external view returns (IDisputeGame impl_);
/// @notice Creates a new DisputeGame proxy contract. /// @notice Creates a new DisputeGame proxy contract.
/// @param _gameType The type of the DisputeGame - used to decide the proxy implementation. /// @param _gameType The type of the DisputeGame - used to decide the proxy implementation.
...@@ -85,7 +85,7 @@ interface IDisputeGameFactory { ...@@ -85,7 +85,7 @@ interface IDisputeGameFactory {
/// @param _gameType The type of the DisputeGame. /// @param _gameType The type of the DisputeGame.
/// @param _rootClaim The root claim of the DisputeGame. /// @param _rootClaim The root claim of the DisputeGame.
/// @param _extraData Any extra data that should be provided to the created dispute game. /// @param _extraData Any extra data that should be provided to the created dispute game.
/// @return _uuid The unique identifier for the given dispute game parameters. /// @return uuid_ The unique identifier for the given dispute game parameters.
function getGameUUID( function getGameUUID(
GameType _gameType, GameType _gameType,
Claim _rootClaim, Claim _rootClaim,
...@@ -93,5 +93,5 @@ interface IDisputeGameFactory { ...@@ -93,5 +93,5 @@ interface IDisputeGameFactory {
) )
external external
pure pure
returns (Hash _uuid); returns (Hash uuid_);
} }
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Constants } from "../libraries/Constants.sol";
/// @title IL1ChugSplashDeployer /// @title IL1ChugSplashDeployer
interface IL1ChugSplashDeployer { interface IL1ChugSplashDeployer {
function isUpgrading() external view returns (bool); function isUpgrading() external view returns (bool);
...@@ -21,12 +23,6 @@ contract L1ChugSplashProxy { ...@@ -21,12 +23,6 @@ contract L1ChugSplashProxy {
/// contract, the appended bytecode will be deployed as given. /// contract, the appended bytecode will be deployed as given.
bytes13 internal constant DEPLOY_CODE_PREFIX = 0x600D380380600D6000396000f3; bytes13 internal constant DEPLOY_CODE_PREFIX = 0x600D380380600D6000396000f3;
/// @notice bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
bytes32 internal constant IMPLEMENTATION_KEY = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// @notice bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)
bytes32 internal constant OWNER_KEY = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/// @notice Blocks a function from being called when the parent signals that the system should /// @notice Blocks a function from being called when the parent signals that the system should
/// be paused via an isUpgrading function. /// be paused via an isUpgrading function.
modifier onlyWhenNotPaused() { modifier onlyWhenNotPaused() {
...@@ -160,16 +156,18 @@ contract L1ChugSplashProxy { ...@@ -160,16 +156,18 @@ contract L1ChugSplashProxy {
/// @notice Sets the implementation address. /// @notice Sets the implementation address.
/// @param _implementation New implementation address. /// @param _implementation New implementation address.
function _setImplementation(address _implementation) internal { function _setImplementation(address _implementation) internal {
bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
assembly { assembly {
sstore(IMPLEMENTATION_KEY, _implementation) sstore(proxyImplementation, _implementation)
} }
} }
/// @notice Changes the owner of the proxy contract. /// @notice Changes the owner of the proxy contract.
/// @param _owner New owner of the proxy contract. /// @param _owner New owner of the proxy contract.
function _setOwner(address _owner) internal { function _setOwner(address _owner) internal {
bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
assembly { assembly {
sstore(OWNER_KEY, _owner) sstore(proxyOwner, _owner)
} }
} }
...@@ -203,8 +201,9 @@ contract L1ChugSplashProxy { ...@@ -203,8 +201,9 @@ contract L1ChugSplashProxy {
/// @return Implementation address. /// @return Implementation address.
function _getImplementation() internal view returns (address) { function _getImplementation() internal view returns (address) {
address implementation; address implementation;
bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
assembly { assembly {
implementation := sload(IMPLEMENTATION_KEY) implementation := sload(proxyImplementation)
} }
return implementation; return implementation;
} }
...@@ -213,8 +212,9 @@ contract L1ChugSplashProxy { ...@@ -213,8 +212,9 @@ contract L1ChugSplashProxy {
/// @return Owner address. /// @return Owner address.
function _getOwner() internal view returns (address) { function _getOwner() internal view returns (address) {
address owner; address owner;
bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
assembly { assembly {
owner := sload(OWNER_KEY) owner := sload(proxyOwner)
} }
return owner; return owner;
} }
......
...@@ -21,6 +21,15 @@ library Constants { ...@@ -21,6 +21,15 @@ library Constants {
/// non-zero to reduce the gas cost of message passing transactions. /// non-zero to reduce the gas cost of message passing transactions.
address internal constant DEFAULT_L2_SENDER = 0x000000000000000000000000000000000000dEaD; address internal constant DEFAULT_L2_SENDER = 0x000000000000000000000000000000000000dEaD;
/// @notice The storage slot that holds the address of a proxy implementation.
/// @dev `bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)`
bytes32 internal constant PROXY_IMPLEMENTATION_ADDRESS =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// @notice The storage slot that holds the address of the owner.
/// @dev `bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)`
bytes32 internal constant PROXY_OWNER_ADDRESS = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/// @notice Returns the default values for the ResourceConfig. These are the recommended values /// @notice Returns the default values for the ResourceConfig. These are the recommended values
/// for a production network. /// for a production network.
function DEFAULT_RESOURCE_CONFIG() internal pure returns (ResourceMetering.ResourceConfig memory) { function DEFAULT_RESOURCE_CONFIG() internal pure returns (ResourceMetering.ResourceConfig memory) {
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Constants } from "../libraries/Constants.sol";
/// @title Proxy /// @title Proxy
/// @notice Proxy is a transparent proxy that passes through the call if the caller is the owner or /// @notice Proxy is a transparent proxy that passes through the call if the caller is the owner or
/// if the caller is address(0), meaning that the call originated from an off-chain /// if the caller is address(0), meaning that the call originated from an off-chain
/// simulation. /// simulation.
contract Proxy { contract Proxy {
/// @notice The storage slot that holds the address of the implementation.
/// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
bytes32 internal constant IMPLEMENTATION_KEY = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// @notice The storage slot that holds the address of the owner.
/// bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)
bytes32 internal constant OWNER_KEY = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/// @notice An event that is emitted each time the implementation is changed. This event is part /// @notice An event that is emitted each time the implementation is changed. This event is part
/// of the EIP-1967 specification. /// of the EIP-1967 specification.
/// @param implementation The address of the implementation contract /// @param implementation The address of the implementation contract
...@@ -107,8 +101,9 @@ contract Proxy { ...@@ -107,8 +101,9 @@ contract Proxy {
/// @notice Sets the implementation address. /// @notice Sets the implementation address.
/// @param _implementation New implementation address. /// @param _implementation New implementation address.
function _setImplementation(address _implementation) internal { function _setImplementation(address _implementation) internal {
bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
assembly { assembly {
sstore(IMPLEMENTATION_KEY, _implementation) sstore(proxyImplementation, _implementation)
} }
emit Upgraded(_implementation); emit Upgraded(_implementation);
} }
...@@ -117,8 +112,9 @@ contract Proxy { ...@@ -117,8 +112,9 @@ contract Proxy {
/// @param _admin New owner of the proxy contract. /// @param _admin New owner of the proxy contract.
function _changeAdmin(address _admin) internal { function _changeAdmin(address _admin) internal {
address previous = _getAdmin(); address previous = _getAdmin();
bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
assembly { assembly {
sstore(OWNER_KEY, _admin) sstore(proxyOwner, _admin)
} }
emit AdminChanged(previous, _admin); emit AdminChanged(previous, _admin);
} }
...@@ -152,8 +148,9 @@ contract Proxy { ...@@ -152,8 +148,9 @@ contract Proxy {
/// @return Implementation address. /// @return Implementation address.
function _getImplementation() internal view returns (address) { function _getImplementation() internal view returns (address) {
address impl; address impl;
bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
assembly { assembly {
impl := sload(IMPLEMENTATION_KEY) impl := sload(proxyImplementation)
} }
return impl; return impl;
} }
...@@ -162,8 +159,9 @@ contract Proxy { ...@@ -162,8 +159,9 @@ contract Proxy {
/// @return Owner address. /// @return Owner address.
function _getAdmin() internal view returns (address) { function _getAdmin() internal view returns (address) {
address owner; address owner;
bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
assembly { assembly {
owner := sload(OWNER_KEY) owner := sload(proxyOwner)
} }
return owner; return owner;
} }
......
...@@ -5,6 +5,7 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; ...@@ -5,6 +5,7 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Proxy } from "./Proxy.sol"; import { Proxy } from "./Proxy.sol";
import { AddressManager } from "../legacy/AddressManager.sol"; import { AddressManager } from "../legacy/AddressManager.sol";
import { L1ChugSplashProxy } from "../legacy/L1ChugSplashProxy.sol"; import { L1ChugSplashProxy } from "../legacy/L1ChugSplashProxy.sol";
import { Constants } from "../libraries/Constants.sol";
/// @title IStaticERC1967Proxy /// @title IStaticERC1967Proxy
/// @notice IStaticERC1967Proxy is a static version of the ERC1967 proxy interface. /// @notice IStaticERC1967Proxy is a static version of the ERC1967 proxy interface.
...@@ -163,9 +164,7 @@ contract ProxyAdmin is Ownable { ...@@ -163,9 +164,7 @@ contract ProxyAdmin is Ownable {
Proxy(_proxy).upgradeTo(_implementation); Proxy(_proxy).upgradeTo(_implementation);
} else if (ptype == ProxyType.CHUGSPLASH) { } else if (ptype == ProxyType.CHUGSPLASH) {
L1ChugSplashProxy(_proxy).setStorage( L1ChugSplashProxy(_proxy).setStorage(
// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) Constants.PROXY_IMPLEMENTATION_ADDRESS, bytes32(uint256(uint160(_implementation)))
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc,
bytes32(uint256(uint160(_implementation)))
); );
} else if (ptype == ProxyType.RESOLVED) { } else if (ptype == ProxyType.RESOLVED) {
string memory name = implementationName[_proxy]; string memory name = implementationName[_proxy];
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol";
import { Constants } from "src/libraries/Constants.sol";
contract Constants_Test is Test {
/// @notice Check EIP1967 related constants.
function test_eip1967Constants_succeeds() external {
assertEq(
bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1), Constants.PROXY_IMPLEMENTATION_ADDRESS
);
assertEq(bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1), Constants.PROXY_OWNER_ADDRESS);
}
}
This diff is collapsed.
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