Commit 113e2744 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-challenger, op-dispute-mon: Support both old and new versions of the...

op-challenger, op-dispute-mon: Support both old and new versions of the dispute game contracts (#10302)

* op-challenger, op-dispute-mon: Support both old and new versions of the dispute game contracts.

* op-challenger: Update fault dispute game contract tests to cover multiple versions.
parent ba174f4d
package main
import (
"context"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/flags"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
contractMetrics "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics"
"github.com/ethereum-optimism/optimism/op-challenger/tools"
opservice "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/urfave/cli/v2"
......@@ -38,7 +41,10 @@ func CreateGame(ctx *cli.Context) error {
traceType := ctx.Uint64(TraceTypeFlag.Name)
l2BlockNum := ctx.Uint64(L2BlockNumFlag.Name)
contract, txMgr, err := NewContractWithTxMgr[*contracts.DisputeGameFactoryContract](ctx, flags.FactoryAddressFlag.Name, contracts.NewDisputeGameFactoryContract)
contract, txMgr, err := NewContractWithTxMgr[*contracts.DisputeGameFactoryContract](ctx, flags.FactoryAddressFlag.Name,
func(ctx context.Context, metricer contractMetrics.ContractMetricer, address common.Address, caller *batching.MultiCaller) (*contracts.DisputeGameFactoryContract, error) {
return contracts.NewDisputeGameFactoryContract(metricer, address, caller), nil
})
if err != nil {
return fmt.Errorf("failed to create dispute game factory bindings: %w", err)
}
......
......@@ -48,11 +48,14 @@ func ListClaims(ctx *cli.Context) error {
defer l1Client.Close()
caller := batching.NewMultiCaller(l1Client.Client(), batching.DefaultBatchSize)
contract := contracts.NewFaultDisputeGameContract(metrics.NoopContractMetrics, gameAddr, caller)
contract, err := contracts.NewFaultDisputeGameContract(ctx.Context, metrics.NoopContractMetrics, gameAddr, caller)
if err != nil {
return err
}
return listClaims(ctx.Context, contract)
}
func listClaims(ctx context.Context, game *contracts.FaultDisputeGameContract) error {
func listClaims(ctx context.Context, game contracts.FaultDisputeGameContract) error {
maxDepth, err := game.GetMaxGameDepth(ctx)
if err != nil {
return fmt.Errorf("failed to retrieve max depth: %w", err)
......
......@@ -66,7 +66,10 @@ func listGames(ctx context.Context, caller *batching.MultiCaller, factory *contr
infos := make([]*gameInfo, len(games))
var wg sync.WaitGroup
for idx, game := range games {
gameContract := contracts.NewFaultDisputeGameContract(metrics.NoopContractMetrics, game.Proxy, caller)
gameContract, err := contracts.NewFaultDisputeGameContract(ctx, metrics.NoopContractMetrics, game.Proxy, caller)
if err != nil {
return fmt.Errorf("failed to create dispute game contract: %w", err)
}
info := gameInfo{GameMetadata: game}
infos[idx] = &info
gameProxy := game.Proxy
......
......@@ -46,7 +46,7 @@ func Move(ctx *cli.Context) error {
return fmt.Errorf("both attack and defense flags cannot be set")
}
contract, txMgr, err := NewContractWithTxMgr[*contracts.FaultDisputeGameContract](ctx, GameAddressFlag.Name, contracts.NewFaultDisputeGameContract)
contract, txMgr, err := NewContractWithTxMgr[contracts.FaultDisputeGameContract](ctx, GameAddressFlag.Name, contracts.NewFaultDisputeGameContract)
if err != nil {
return fmt.Errorf("failed to create dispute game bindings: %w", err)
}
......
......@@ -12,7 +12,7 @@ import (
)
func Resolve(ctx *cli.Context) error {
contract, txMgr, err := NewContractWithTxMgr[*contracts.FaultDisputeGameContract](ctx, GameAddressFlag.Name, contracts.NewFaultDisputeGameContract)
contract, txMgr, err := NewContractWithTxMgr[contracts.FaultDisputeGameContract](ctx, GameAddressFlag.Name, contracts.NewFaultDisputeGameContract)
if err != nil {
return fmt.Errorf("failed to create dispute game bindings: %w", err)
}
......
package main
import (
"context"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/flags"
......@@ -14,7 +15,7 @@ import (
"github.com/urfave/cli/v2"
)
type ContractCreator[T any] func(contractMetrics.ContractMetricer, common.Address, *batching.MultiCaller) T
type ContractCreator[T any] func(context.Context, contractMetrics.ContractMetricer, common.Address, *batching.MultiCaller) (T, error)
// NewContractWithTxMgr creates a new contract and a transaction manager.
func NewContractWithTxMgr[T any](ctx *cli.Context, flagName string, creator ContractCreator[T]) (T, txmgr.TxManager, error) {
......@@ -40,7 +41,10 @@ func newContractFromCLI[T any](ctx *cli.Context, flagName string, caller *batchi
return contract, err
}
created := creator(contractMetrics.NoopContractMetrics, gameAddr, caller)
created, err := creator(ctx.Context, contractMetrics.NoopContractMetrics, gameAddr, caller)
if err != nil {
return contract, fmt.Errorf("failed to create contract bindings: %w", err)
}
return created, nil
}
......
[
{
"inputs": [
{
"internalType": "GameType",
"name": "_gameType",
"type": "uint32"
},
{
"internalType": "Claim",
"name": "_absolutePrestate",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "_maxGameDepth",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_splitDepth",
"type": "uint256"
},
{
"internalType": "Duration",
"name": "_gameDuration",
"type": "uint64"
},
{
"internalType": "contract IBigStepper",
"name": "_vm",
"type": "address"
},
{
"internalType": "contract IDelayedWETH",
"name": "_weth",
"type": "address"
},
{
"internalType": "contract IAnchorStateRegistry",
"name": "_anchorStateRegistry",
"type": "address"
},
{
"internalType": "uint256",
"name": "_l2ChainId",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"stateMutability": "payable",
"type": "fallback"
},
{
"stateMutability": "payable",
"type": "receive"
},
{
"inputs": [],
"name": "absolutePrestate",
"outputs": [
{
"internalType": "Claim",
"name": "absolutePrestate_",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_ident",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_execLeafIdx",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_partOffset",
"type": "uint256"
}
],
"name": "addLocalData",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_parentIndex",
"type": "uint256"
},
{
"internalType": "Claim",
"name": "_claim",
"type": "bytes32"
}
],
"name": "attack",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_recipient",
"type": "address"
}
],
"name": "claimCredit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "claimData",
"outputs": [
{
"internalType": "uint32",
"name": "parentIndex",
"type": "uint32"
},
{
"internalType": "address",
"name": "counteredBy",
"type": "address"
},
{
"internalType": "address",
"name": "claimant",
"type": "address"
},
{
"internalType": "uint128",
"name": "bond",
"type": "uint128"
},
{
"internalType": "Claim",
"name": "claim",
"type": "bytes32"
},
{
"internalType": "Position",
"name": "position",
"type": "uint128"
},
{
"internalType": "Clock",
"name": "clock",
"type": "uint128"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "claimDataLen",
"outputs": [
{
"internalType": "uint256",
"name": "len_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "createdAt",
"outputs": [
{
"internalType": "Timestamp",
"name": "",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "credit",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_parentIndex",
"type": "uint256"
},
{
"internalType": "Claim",
"name": "_claim",
"type": "bytes32"
}
],
"name": "defend",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "extraData",
"outputs": [
{
"internalType": "bytes",
"name": "extraData_",
"type": "bytes"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "gameData",
"outputs": [
{
"internalType": "GameType",
"name": "gameType_",
"type": "uint32"
},
{
"internalType": "Claim",
"name": "rootClaim_",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "extraData_",
"type": "bytes"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "gameDuration",
"outputs": [
{
"internalType": "Duration",
"name": "gameDuration_",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "gameType",
"outputs": [
{
"internalType": "GameType",
"name": "gameType_",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "Position",
"name": "_position",
"type": "uint128"
}
],
"name": "getRequiredBond",
"outputs": [
{
"internalType": "uint256",
"name": "requiredBond_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "initialize",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "l1Head",
"outputs": [
{
"internalType": "Hash",
"name": "l1Head_",
"type": "bytes32"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "l2BlockNumber",
"outputs": [
{
"internalType": "uint256",
"name": "l2BlockNumber_",
"type": "uint256"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "l2ChainId",
"outputs": [
{
"internalType": "uint256",
"name": "l2ChainId_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "maxGameDepth",
"outputs": [
{
"internalType": "uint256",
"name": "maxGameDepth_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_challengeIndex",
"type": "uint256"
},
{
"internalType": "Claim",
"name": "_claim",
"type": "bytes32"
},
{
"internalType": "bool",
"name": "_isAttack",
"type": "bool"
}
],
"name": "move",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "resolve",
"outputs": [
{
"internalType": "enum GameStatus",
"name": "status_",
"type": "uint8"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_claimIndex",
"type": "uint256"
}
],
"name": "resolveClaim",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "resolvedAt",
"outputs": [
{
"internalType": "Timestamp",
"name": "",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "rootClaim",
"outputs": [
{
"internalType": "Claim",
"name": "rootClaim_",
"type": "bytes32"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "splitDepth",
"outputs": [
{
"internalType": "uint256",
"name": "splitDepth_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "startingBlockNumber",
"outputs": [
{
"internalType": "uint256",
"name": "startingBlockNumber_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "startingOutputRoot",
"outputs": [
{
"internalType": "Hash",
"name": "root",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "l2BlockNumber",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "startingRootHash",
"outputs": [
{
"internalType": "Hash",
"name": "startingRootHash_",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "status",
"outputs": [
{
"internalType": "enum GameStatus",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_claimIndex",
"type": "uint256"
},
{
"internalType": "bool",
"name": "_isAttack",
"type": "bool"
},
{
"internalType": "bytes",
"name": "_stateData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "_proof",
"type": "bytes"
}
],
"name": "step",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "version",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "vm",
"outputs": [
{
"internalType": "contract IBigStepper",
"name": "vm_",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "weth",
"outputs": [
{
"internalType": "contract IDelayedWETH",
"name": "weth_",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "parentIndex",
"type": "uint256"
},
{
"indexed": true,
"internalType": "Claim",
"name": "claim",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "claimant",
"type": "address"
}
],
"name": "Move",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "enum GameStatus",
"name": "status",
"type": "uint8"
}
],
"name": "Resolved",
"type": "event"
},
{
"inputs": [],
"name": "AlreadyInitialized",
"type": "error"
},
{
"inputs": [],
"name": "AnchorRootNotFound",
"type": "error"
},
{
"inputs": [],
"name": "BondTransferFailed",
"type": "error"
},
{
"inputs": [],
"name": "CannotDefendRootClaim",
"type": "error"
},
{
"inputs": [],
"name": "ClaimAboveSplit",
"type": "error"
},
{
"inputs": [],
"name": "ClaimAlreadyExists",
"type": "error"
},
{
"inputs": [],
"name": "ClaimAlreadyResolved",
"type": "error"
},
{
"inputs": [],
"name": "ClockNotExpired",
"type": "error"
},
{
"inputs": [],
"name": "ClockTimeExceeded",
"type": "error"
},
{
"inputs": [],
"name": "DuplicateStep",
"type": "error"
},
{
"inputs": [],
"name": "GameDepthExceeded",
"type": "error"
},
{
"inputs": [],
"name": "GameNotInProgress",
"type": "error"
},
{
"inputs": [],
"name": "IncorrectBondAmount",
"type": "error"
},
{
"inputs": [],
"name": "InvalidLocalIdent",
"type": "error"
},
{
"inputs": [],
"name": "InvalidParent",
"type": "error"
},
{
"inputs": [],
"name": "InvalidPrestate",
"type": "error"
},
{
"inputs": [],
"name": "InvalidSplitDepth",
"type": "error"
},
{
"inputs": [],
"name": "NoCreditToClaim",
"type": "error"
},
{
"inputs": [],
"name": "OutOfOrderResolution",
"type": "error"
},
{
"inputs": [
{
"internalType": "Claim",
"name": "rootClaim",
"type": "bytes32"
}
],
"name": "UnexpectedRootClaim",
"type": "error"
},
{
"inputs": [],
"name": "ValidStep",
"type": "error"
}
]
\ No newline at end of file
package contracts
import (
"bytes"
"context"
"errors"
"fmt"
"math"
"math/big"
"strings"
"time"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics"
......@@ -15,6 +17,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/packages/contracts-bedrock/snapshots"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
)
......@@ -22,6 +25,7 @@ import (
var maxChildChecks = big.NewInt(512)
var (
methodVersion = "version"
methodMaxClockDuration = "maxClockDuration"
methodMaxGameDepth = "maxGameDepth"
methodAbsolutePrestate = "absolutePrestate"
......@@ -50,7 +54,7 @@ var (
var ErrSimulationFailed = errors.New("tx simulation failed")
type FaultDisputeGameContract struct {
type FaultDisputeGameContractLatest struct {
metrics metrics.ContractMetricer
multiCaller *batching.MultiCaller
contract *batching.BoundContract
......@@ -61,20 +65,46 @@ type Proposal struct {
OutputRoot common.Hash
}
func NewFaultDisputeGameContract(metrics metrics.ContractMetricer, addr common.Address, caller *batching.MultiCaller) *FaultDisputeGameContract {
func NewFaultDisputeGameContract(ctx context.Context, metrics metrics.ContractMetricer, addr common.Address, caller *batching.MultiCaller) (FaultDisputeGameContract, error) {
contractAbi := snapshots.LoadFaultDisputeGameABI()
return &FaultDisputeGameContract{
result, err := caller.SingleCall(ctx, rpcblock.Latest, batching.NewContractCall(contractAbi, addr, methodVersion))
if err != nil {
return nil, fmt.Errorf("failed to retrieve version of dispute game %v: %w", addr, err)
}
version := result.GetString(0)
if strings.HasPrefix(version, "0.8.") {
// Detected an older version of contracts, use a compatibility shim.
legacyAbi := mustParseAbi(faultDisputeGameAbi020)
return &FaultDisputeGameContract080{
FaultDisputeGameContractLatest: FaultDisputeGameContractLatest{
metrics: metrics,
multiCaller: caller,
contract: batching.NewBoundContract(legacyAbi, addr),
},
}, nil
} else {
return &FaultDisputeGameContractLatest{
metrics: metrics,
multiCaller: caller,
contract: batching.NewBoundContract(contractAbi, addr),
}, nil
}
}
func mustParseAbi(json []byte) *abi.ABI {
loaded, err := abi.JSON(bytes.NewReader(json))
if err != nil {
panic(err)
}
return &loaded
}
// GetBalance returns the total amount of ETH controlled by this contract.
// Note that the ETH is actually held by the DelayedWETH contract which may be shared by multiple games.
// Returns the balance and the address of the contract that actually holds the balance.
func (f *FaultDisputeGameContract) GetBalance(ctx context.Context, block rpcblock.Block) (*big.Int, common.Address, error) {
func (f *FaultDisputeGameContractLatest) GetBalance(ctx context.Context, block rpcblock.Block) (*big.Int, common.Address, error) {
defer f.metrics.StartContractRequest("GetBalance")()
result, err := f.multiCaller.SingleCall(ctx, block, f.contract.Call(methodWETH))
if err != nil {
......@@ -90,7 +120,7 @@ func (f *FaultDisputeGameContract) GetBalance(ctx context.Context, block rpcbloc
// GetBlockRange returns the block numbers of the absolute pre-state block (typically genesis or the bedrock activation block)
// and the post-state block (that the proposed output root is for).
func (f *FaultDisputeGameContract) GetBlockRange(ctx context.Context) (prestateBlock uint64, poststateBlock uint64, retErr error) {
func (f *FaultDisputeGameContractLatest) GetBlockRange(ctx context.Context) (prestateBlock uint64, poststateBlock uint64, retErr error) {
defer f.metrics.StartContractRequest("GetBlockRange")()
results, err := f.multiCaller.Call(ctx, rpcblock.Latest,
f.contract.Call(methodStartingBlockNumber),
......@@ -109,7 +139,7 @@ func (f *FaultDisputeGameContract) GetBlockRange(ctx context.Context) (prestateB
}
// GetGameMetadata returns the game's L1 head, L2 block number, root claim, status, and max clock duration.
func (f *FaultDisputeGameContract) GetGameMetadata(ctx context.Context, block rpcblock.Block) (common.Hash, uint64, common.Hash, gameTypes.GameStatus, uint64, error) {
func (f *FaultDisputeGameContractLatest) GetGameMetadata(ctx context.Context, block rpcblock.Block) (common.Hash, uint64, common.Hash, gameTypes.GameStatus, uint64, error) {
defer f.metrics.StartContractRequest("GetGameMetadata")()
results, err := f.multiCaller.Call(ctx, block,
f.contract.Call(methodL1Head),
......@@ -134,7 +164,7 @@ func (f *FaultDisputeGameContract) GetGameMetadata(ctx context.Context, block rp
return l1Head, l2BlockNumber, rootClaim, status, duration, nil
}
func (f *FaultDisputeGameContract) GetStartingRootHash(ctx context.Context) (common.Hash, error) {
func (f *FaultDisputeGameContractLatest) GetStartingRootHash(ctx context.Context) (common.Hash, error) {
defer f.metrics.StartContractRequest("GetStartingRootHash")()
startingRootHash, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodStartingRootHash))
if err != nil {
......@@ -143,7 +173,7 @@ func (f *FaultDisputeGameContract) GetStartingRootHash(ctx context.Context) (com
return startingRootHash.GetHash(0), nil
}
func (f *FaultDisputeGameContract) GetSplitDepth(ctx context.Context) (types.Depth, error) {
func (f *FaultDisputeGameContractLatest) GetSplitDepth(ctx context.Context) (types.Depth, error) {
defer f.metrics.StartContractRequest("GetSplitDepth")()
splitDepth, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodSplitDepth))
if err != nil {
......@@ -152,7 +182,7 @@ func (f *FaultDisputeGameContract) GetSplitDepth(ctx context.Context) (types.Dep
return types.Depth(splitDepth.GetBigInt(0).Uint64()), nil
}
func (f *FaultDisputeGameContract) GetCredit(ctx context.Context, recipient common.Address) (*big.Int, gameTypes.GameStatus, error) {
func (f *FaultDisputeGameContractLatest) GetCredit(ctx context.Context, recipient common.Address) (*big.Int, gameTypes.GameStatus, error) {
defer f.metrics.StartContractRequest("GetCredit")()
results, err := f.multiCaller.Call(ctx, rpcblock.Latest,
f.contract.Call(methodCredit, recipient),
......@@ -171,7 +201,7 @@ func (f *FaultDisputeGameContract) GetCredit(ctx context.Context, recipient comm
return credit, status, nil
}
func (f *FaultDisputeGameContract) GetRequiredBonds(ctx context.Context, block rpcblock.Block, positions ...*big.Int) ([]*big.Int, error) {
func (f *FaultDisputeGameContractLatest) GetRequiredBonds(ctx context.Context, block rpcblock.Block, positions ...*big.Int) ([]*big.Int, error) {
calls := make([]batching.Call, 0, len(positions))
for _, position := range positions {
calls = append(calls, f.contract.Call(methodRequiredBond, position))
......@@ -187,7 +217,7 @@ func (f *FaultDisputeGameContract) GetRequiredBonds(ctx context.Context, block r
return requiredBonds, nil
}
func (f *FaultDisputeGameContract) GetCredits(ctx context.Context, block rpcblock.Block, recipients ...common.Address) ([]*big.Int, error) {
func (f *FaultDisputeGameContractLatest) GetCredits(ctx context.Context, block rpcblock.Block, recipients ...common.Address) ([]*big.Int, error) {
defer f.metrics.StartContractRequest("GetCredits")()
calls := make([]batching.Call, 0, len(recipients))
for _, recipient := range recipients {
......@@ -204,7 +234,7 @@ func (f *FaultDisputeGameContract) GetCredits(ctx context.Context, block rpcbloc
return credits, nil
}
func (f *FaultDisputeGameContract) ClaimCreditTx(ctx context.Context, recipient common.Address) (txmgr.TxCandidate, error) {
func (f *FaultDisputeGameContractLatest) ClaimCreditTx(ctx context.Context, recipient common.Address) (txmgr.TxCandidate, error) {
defer f.metrics.StartContractRequest("ClaimCredit")()
call := f.contract.Call(methodClaimCredit, recipient)
_, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, call)
......@@ -214,7 +244,7 @@ func (f *FaultDisputeGameContract) ClaimCreditTx(ctx context.Context, recipient
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract) GetRequiredBond(ctx context.Context, position types.Position) (*big.Int, error) {
func (f *FaultDisputeGameContractLatest) GetRequiredBond(ctx context.Context, position types.Position) (*big.Int, error) {
defer f.metrics.StartContractRequest("GetRequiredBond")()
bond, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodRequiredBond, position.ToGIndex()))
if err != nil {
......@@ -223,14 +253,14 @@ func (f *FaultDisputeGameContract) GetRequiredBond(ctx context.Context, position
return bond.GetBigInt(0), nil
}
func (f *FaultDisputeGameContract) UpdateOracleTx(ctx context.Context, claimIdx uint64, data *types.PreimageOracleData) (txmgr.TxCandidate, error) {
func (f *FaultDisputeGameContractLatest) UpdateOracleTx(ctx context.Context, claimIdx uint64, data *types.PreimageOracleData) (txmgr.TxCandidate, error) {
if data.IsLocal {
return f.addLocalDataTx(claimIdx, data)
}
return f.addGlobalDataTx(ctx, data)
}
func (f *FaultDisputeGameContract) addLocalDataTx(claimIdx uint64, data *types.PreimageOracleData) (txmgr.TxCandidate, error) {
func (f *FaultDisputeGameContractLatest) addLocalDataTx(claimIdx uint64, data *types.PreimageOracleData) (txmgr.TxCandidate, error) {
call := f.contract.Call(
methodAddLocalData,
data.GetIdent(),
......@@ -240,7 +270,7 @@ func (f *FaultDisputeGameContract) addLocalDataTx(claimIdx uint64, data *types.P
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract) addGlobalDataTx(ctx context.Context, data *types.PreimageOracleData) (txmgr.TxCandidate, error) {
func (f *FaultDisputeGameContractLatest) addGlobalDataTx(ctx context.Context, data *types.PreimageOracleData) (txmgr.TxCandidate, error) {
oracle, err := f.GetOracle(ctx)
if err != nil {
return txmgr.TxCandidate{}, err
......@@ -248,7 +278,7 @@ func (f *FaultDisputeGameContract) addGlobalDataTx(ctx context.Context, data *ty
return oracle.AddGlobalDataTx(data)
}
func (f *FaultDisputeGameContract) GetWithdrawals(ctx context.Context, block rpcblock.Block, gameAddr common.Address, recipients ...common.Address) ([]*WithdrawalRequest, error) {
func (f *FaultDisputeGameContractLatest) GetWithdrawals(ctx context.Context, block rpcblock.Block, gameAddr common.Address, recipients ...common.Address) ([]*WithdrawalRequest, error) {
defer f.metrics.StartContractRequest("GetWithdrawals")()
delayedWETH, err := f.getDelayedWETH(ctx)
if err != nil {
......@@ -257,7 +287,7 @@ func (f *FaultDisputeGameContract) GetWithdrawals(ctx context.Context, block rpc
return delayedWETH.GetWithdrawals(ctx, block, gameAddr, recipients...)
}
func (f *FaultDisputeGameContract) getDelayedWETH(ctx context.Context) (*DelayedWETHContract, error) {
func (f *FaultDisputeGameContractLatest) getDelayedWETH(ctx context.Context) (*DelayedWETHContract, error) {
defer f.metrics.StartContractRequest("GetDelayedWETH")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodWETH))
if err != nil {
......@@ -266,7 +296,7 @@ func (f *FaultDisputeGameContract) getDelayedWETH(ctx context.Context) (*Delayed
return NewDelayedWETHContract(f.metrics, result.GetAddress(0), f.multiCaller), nil
}
func (f *FaultDisputeGameContract) GetOracle(ctx context.Context) (*PreimageOracleContract, error) {
func (f *FaultDisputeGameContractLatest) GetOracle(ctx context.Context) (*PreimageOracleContract, error) {
defer f.metrics.StartContractRequest("GetOracle")()
vm, err := f.vm(ctx)
if err != nil {
......@@ -275,7 +305,7 @@ func (f *FaultDisputeGameContract) GetOracle(ctx context.Context) (*PreimageOrac
return vm.Oracle(ctx)
}
func (f *FaultDisputeGameContract) GetMaxClockDuration(ctx context.Context) (time.Duration, error) {
func (f *FaultDisputeGameContractLatest) GetMaxClockDuration(ctx context.Context) (time.Duration, error) {
defer f.metrics.StartContractRequest("GetMaxClockDuration")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodMaxClockDuration))
if err != nil {
......@@ -284,7 +314,7 @@ func (f *FaultDisputeGameContract) GetMaxClockDuration(ctx context.Context) (tim
return time.Duration(result.GetUint64(0)) * time.Second, nil
}
func (f *FaultDisputeGameContract) GetMaxGameDepth(ctx context.Context) (types.Depth, error) {
func (f *FaultDisputeGameContractLatest) GetMaxGameDepth(ctx context.Context) (types.Depth, error) {
defer f.metrics.StartContractRequest("GetMaxGameDepth")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodMaxGameDepth))
if err != nil {
......@@ -293,7 +323,7 @@ func (f *FaultDisputeGameContract) GetMaxGameDepth(ctx context.Context) (types.D
return types.Depth(result.GetBigInt(0).Uint64()), nil
}
func (f *FaultDisputeGameContract) GetAbsolutePrestateHash(ctx context.Context) (common.Hash, error) {
func (f *FaultDisputeGameContractLatest) GetAbsolutePrestateHash(ctx context.Context) (common.Hash, error) {
defer f.metrics.StartContractRequest("GetAbsolutePrestateHash")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodAbsolutePrestate))
if err != nil {
......@@ -302,7 +332,7 @@ func (f *FaultDisputeGameContract) GetAbsolutePrestateHash(ctx context.Context)
return result.GetHash(0), nil
}
func (f *FaultDisputeGameContract) GetL1Head(ctx context.Context) (common.Hash, error) {
func (f *FaultDisputeGameContractLatest) GetL1Head(ctx context.Context) (common.Hash, error) {
defer f.metrics.StartContractRequest("GetL1Head")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodL1Head))
if err != nil {
......@@ -311,7 +341,7 @@ func (f *FaultDisputeGameContract) GetL1Head(ctx context.Context) (common.Hash,
return result.GetHash(0), nil
}
func (f *FaultDisputeGameContract) GetStatus(ctx context.Context) (gameTypes.GameStatus, error) {
func (f *FaultDisputeGameContractLatest) GetStatus(ctx context.Context) (gameTypes.GameStatus, error) {
defer f.metrics.StartContractRequest("GetStatus")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodStatus))
if err != nil {
......@@ -320,7 +350,7 @@ func (f *FaultDisputeGameContract) GetStatus(ctx context.Context) (gameTypes.Gam
return gameTypes.GameStatusFromUint8(result.GetUint8(0))
}
func (f *FaultDisputeGameContract) GetClaimCount(ctx context.Context) (uint64, error) {
func (f *FaultDisputeGameContractLatest) GetClaimCount(ctx context.Context) (uint64, error) {
defer f.metrics.StartContractRequest("GetClaimCount")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodClaimCount))
if err != nil {
......@@ -329,7 +359,7 @@ func (f *FaultDisputeGameContract) GetClaimCount(ctx context.Context) (uint64, e
return result.GetBigInt(0).Uint64(), nil
}
func (f *FaultDisputeGameContract) GetClaim(ctx context.Context, idx uint64) (types.Claim, error) {
func (f *FaultDisputeGameContractLatest) GetClaim(ctx context.Context, idx uint64) (types.Claim, error) {
defer f.metrics.StartContractRequest("GetClaim")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodClaim, new(big.Int).SetUint64(idx)))
if err != nil {
......@@ -338,7 +368,7 @@ func (f *FaultDisputeGameContract) GetClaim(ctx context.Context, idx uint64) (ty
return f.decodeClaim(result, int(idx)), nil
}
func (f *FaultDisputeGameContract) GetAllClaims(ctx context.Context, block rpcblock.Block) ([]types.Claim, error) {
func (f *FaultDisputeGameContractLatest) GetAllClaims(ctx context.Context, block rpcblock.Block) ([]types.Claim, error) {
defer f.metrics.StartContractRequest("GetAllClaims")()
results, err := batching.ReadArray(ctx, f.multiCaller, block, f.contract.Call(methodClaimCount), func(i *big.Int) *batching.ContractCall {
return f.contract.Call(methodClaim, i)
......@@ -354,7 +384,7 @@ func (f *FaultDisputeGameContract) GetAllClaims(ctx context.Context, block rpcbl
return claims, nil
}
func (f *FaultDisputeGameContract) IsResolved(ctx context.Context, block rpcblock.Block, claims ...types.Claim) ([]bool, error) {
func (f *FaultDisputeGameContractLatest) IsResolved(ctx context.Context, block rpcblock.Block, claims ...types.Claim) ([]bool, error) {
defer f.metrics.StartContractRequest("IsResolved")()
calls := make([]batching.Call, 0, len(claims))
for _, claim := range claims {
......@@ -371,7 +401,7 @@ func (f *FaultDisputeGameContract) IsResolved(ctx context.Context, block rpcbloc
return resolved, nil
}
func (f *FaultDisputeGameContract) vm(ctx context.Context) (*VMContract, error) {
func (f *FaultDisputeGameContractLatest) vm(ctx context.Context) (*VMContract, error) {
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodVM))
if err != nil {
return nil, fmt.Errorf("failed to fetch VM addr: %w", err)
......@@ -380,22 +410,22 @@ func (f *FaultDisputeGameContract) vm(ctx context.Context) (*VMContract, error)
return NewVMContract(vmAddr, f.multiCaller), nil
}
func (f *FaultDisputeGameContract) AttackTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error) {
func (f *FaultDisputeGameContractLatest) AttackTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodAttack, new(big.Int).SetUint64(parentContractIndex), pivot)
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract) DefendTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error) {
func (f *FaultDisputeGameContractLatest) DefendTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodDefend, new(big.Int).SetUint64(parentContractIndex), pivot)
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract) StepTx(claimIdx uint64, isAttack bool, stateData []byte, proof []byte) (txmgr.TxCandidate, error) {
func (f *FaultDisputeGameContractLatest) StepTx(claimIdx uint64, isAttack bool, stateData []byte, proof []byte) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodStep, new(big.Int).SetUint64(claimIdx), isAttack, stateData, proof)
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract) CallResolveClaim(ctx context.Context, claimIdx uint64) error {
func (f *FaultDisputeGameContractLatest) CallResolveClaim(ctx context.Context, claimIdx uint64) error {
defer f.metrics.StartContractRequest("CallResolveClaim")()
call := f.resolveClaimCall(claimIdx)
_, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, call)
......@@ -405,16 +435,16 @@ func (f *FaultDisputeGameContract) CallResolveClaim(ctx context.Context, claimId
return nil
}
func (f *FaultDisputeGameContract) ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, error) {
func (f *FaultDisputeGameContractLatest) ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, error) {
call := f.resolveClaimCall(claimIdx)
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract) resolveClaimCall(claimIdx uint64) *batching.ContractCall {
func (f *FaultDisputeGameContractLatest) resolveClaimCall(claimIdx uint64) *batching.ContractCall {
return f.contract.Call(methodResolveClaim, new(big.Int).SetUint64(claimIdx), maxChildChecks)
}
func (f *FaultDisputeGameContract) CallResolve(ctx context.Context) (gameTypes.GameStatus, error) {
func (f *FaultDisputeGameContractLatest) CallResolve(ctx context.Context) (gameTypes.GameStatus, error) {
defer f.metrics.StartContractRequest("CallResolve")()
call := f.resolveCall()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, call)
......@@ -424,12 +454,12 @@ func (f *FaultDisputeGameContract) CallResolve(ctx context.Context) (gameTypes.G
return gameTypes.GameStatusFromUint8(result.GetUint8(0))
}
func (f *FaultDisputeGameContract) ResolveTx() (txmgr.TxCandidate, error) {
func (f *FaultDisputeGameContractLatest) ResolveTx() (txmgr.TxCandidate, error) {
call := f.resolveCall()
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract) resolveCall() *batching.ContractCall {
func (f *FaultDisputeGameContractLatest) resolveCall() *batching.ContractCall {
return f.contract.Call(methodResolve)
}
......@@ -448,7 +478,7 @@ func packClock(c types.Clock) *big.Int {
return new(big.Int).Or(encoded, big.NewInt(c.Timestamp.Unix()))
}
func (f *FaultDisputeGameContract) decodeClaim(result *batching.CallResult, contractIndex int) types.Claim {
func (f *FaultDisputeGameContractLatest) decodeClaim(result *batching.CallResult, contractIndex int) types.Claim {
parentIndex := result.GetUint32(0)
counteredBy := result.GetAddress(1)
claimant := result.GetAddress(2)
......@@ -469,3 +499,35 @@ func (f *FaultDisputeGameContract) decodeClaim(result *batching.CallResult, cont
ParentContractIndex: int(parentIndex),
}
}
type FaultDisputeGameContract interface {
GetBalance(ctx context.Context, block rpcblock.Block) (*big.Int, common.Address, error)
GetBlockRange(ctx context.Context) (prestateBlock uint64, poststateBlock uint64, retErr error)
GetGameMetadata(ctx context.Context, block rpcblock.Block) (common.Hash, uint64, common.Hash, gameTypes.GameStatus, uint64, error)
GetStartingRootHash(ctx context.Context) (common.Hash, error)
GetSplitDepth(ctx context.Context) (types.Depth, error)
GetCredit(ctx context.Context, recipient common.Address) (*big.Int, gameTypes.GameStatus, error)
GetRequiredBonds(ctx context.Context, block rpcblock.Block, positions ...*big.Int) ([]*big.Int, error)
GetCredits(ctx context.Context, block rpcblock.Block, recipients ...common.Address) ([]*big.Int, error)
ClaimCreditTx(ctx context.Context, recipient common.Address) (txmgr.TxCandidate, error)
GetRequiredBond(ctx context.Context, position types.Position) (*big.Int, error)
UpdateOracleTx(ctx context.Context, claimIdx uint64, data *types.PreimageOracleData) (txmgr.TxCandidate, error)
GetWithdrawals(ctx context.Context, block rpcblock.Block, gameAddr common.Address, recipients ...common.Address) ([]*WithdrawalRequest, error)
GetOracle(ctx context.Context) (*PreimageOracleContract, error)
GetMaxClockDuration(ctx context.Context) (time.Duration, error)
GetMaxGameDepth(ctx context.Context) (types.Depth, error)
GetAbsolutePrestateHash(ctx context.Context) (common.Hash, error)
GetL1Head(ctx context.Context) (common.Hash, error)
GetStatus(ctx context.Context) (gameTypes.GameStatus, error)
GetClaimCount(ctx context.Context) (uint64, error)
GetClaim(ctx context.Context, idx uint64) (types.Claim, error)
GetAllClaims(ctx context.Context, block rpcblock.Block) ([]types.Claim, error)
IsResolved(ctx context.Context, block rpcblock.Block, claims ...types.Claim) ([]bool, error)
AttackTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error)
DefendTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error)
StepTx(claimIdx uint64, isAttack bool, stateData []byte, proof []byte) (txmgr.TxCandidate, error)
CallResolveClaim(ctx context.Context, claimIdx uint64) error
ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, error)
CallResolve(ctx context.Context) (gameTypes.GameStatus, error)
ResolveTx() (txmgr.TxCandidate, error)
}
package contracts
import (
"context"
_ "embed"
"fmt"
"math/big"
"time"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
)
//go:embed abis/FaultDisputeGame-0.8.0.json
var faultDisputeGameAbi020 []byte
var resolvedBondAmount = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 128), big.NewInt(1))
var (
methodGameDuration = "gameDuration"
)
type FaultDisputeGameContract080 struct {
FaultDisputeGameContractLatest
}
// GetGameMetadata returns the game's L1 head, L2 block number, root claim, status, and max clock duration.
func (f *FaultDisputeGameContract080) GetGameMetadata(ctx context.Context, block rpcblock.Block) (common.Hash, uint64, common.Hash, gameTypes.GameStatus, uint64, error) {
defer f.metrics.StartContractRequest("GetGameMetadata")()
results, err := f.multiCaller.Call(ctx, block,
f.contract.Call(methodL1Head),
f.contract.Call(methodL2BlockNumber),
f.contract.Call(methodRootClaim),
f.contract.Call(methodStatus),
f.contract.Call(methodGameDuration))
if err != nil {
return common.Hash{}, 0, common.Hash{}, 0, 0, fmt.Errorf("failed to retrieve game metadata: %w", err)
}
if len(results) != 5 {
return common.Hash{}, 0, common.Hash{}, 0, 0, fmt.Errorf("expected 3 results but got %v", len(results))
}
l1Head := results[0].GetHash(0)
l2BlockNumber := results[1].GetBigInt(0).Uint64()
rootClaim := results[2].GetHash(0)
status, err := gameTypes.GameStatusFromUint8(results[3].GetUint8(0))
if err != nil {
return common.Hash{}, 0, common.Hash{}, 0, 0, fmt.Errorf("failed to convert game status: %w", err)
}
duration := results[4].GetUint64(0)
return l1Head, l2BlockNumber, rootClaim, status, duration / 2, nil
}
func (f *FaultDisputeGameContract080) GetMaxClockDuration(ctx context.Context) (time.Duration, error) {
defer f.metrics.StartContractRequest("GetMaxClockDuration")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodGameDuration))
if err != nil {
return 0, fmt.Errorf("failed to fetch game duration: %w", err)
}
return time.Duration(result.GetUint64(0)) * time.Second / 2, nil
}
func (f *FaultDisputeGameContract080) GetClaim(ctx context.Context, idx uint64) (types.Claim, error) {
claim, err := f.FaultDisputeGameContractLatest.GetClaim(ctx, idx)
if err != nil {
return types.Claim{}, err
}
// Replace the resolved sentinel with what the bond would have been
if claim.Bond.Cmp(resolvedBondAmount) == 0 {
bond, err := f.GetRequiredBond(ctx, claim.Position)
if err != nil {
return types.Claim{}, err
}
claim.Bond = bond
}
return claim, nil
}
func (f *FaultDisputeGameContract080) GetAllClaims(ctx context.Context, block rpcblock.Block) ([]types.Claim, error) {
claims, err := f.FaultDisputeGameContractLatest.GetAllClaims(ctx, block)
if err != nil {
return nil, err
}
resolvedClaims := make([]*types.Claim, 0, len(claims))
positions := make([]*big.Int, 0, len(claims))
for i, claim := range claims {
if claim.Bond.Cmp(resolvedBondAmount) == 0 {
resolvedClaims = append(resolvedClaims, &claims[i])
positions = append(positions, claim.Position.ToGIndex())
}
}
bonds, err := f.GetRequiredBonds(ctx, block, positions...)
if err != nil {
return nil, fmt.Errorf("failed to get required bonds for resolved claims: %w", err)
}
for i, bond := range bonds {
resolvedClaims[i].Bond = bond
}
return claims, nil
}
func (f *FaultDisputeGameContract080) IsResolved(ctx context.Context, block rpcblock.Block, claims ...types.Claim) ([]bool, error) {
rawClaims, err := f.FaultDisputeGameContractLatest.GetAllClaims(ctx, block)
if err != nil {
return nil, fmt.Errorf("failed to get raw claim data: %w", err)
}
results := make([]bool, len(claims))
for i, claim := range claims {
results[i] = rawClaims[claim.ContractIndex].Bond.Cmp(resolvedBondAmount) == 0
}
return results, nil
}
func (f *FaultDisputeGameContract080) CallResolveClaim(ctx context.Context, claimIdx uint64) error {
defer f.metrics.StartContractRequest("CallResolveClaim")()
call := f.resolveClaimCall(claimIdx)
_, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, call)
if err != nil {
return fmt.Errorf("failed to call resolve claim: %w", err)
}
return nil
}
func (f *FaultDisputeGameContract080) ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, error) {
call := f.resolveClaimCall(claimIdx)
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract080) resolveClaimCall(claimIdx uint64) *batching.ContractCall {
return f.contract.Call(methodResolveClaim, new(big.Int).SetUint64(claimIdx))
}
......@@ -16,6 +16,7 @@ import (
batchingTest "github.com/ethereum-optimism/optimism/op-service/sources/batching/test"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/packages/contracts-bedrock/snapshots"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
......@@ -26,6 +27,29 @@ var (
oracleAddr = common.HexToAddress("0x44442842371dFC380576ebb09Ae16Cb6B6ca4444")
)
type contractVersion struct {
version string
loadAbi func() *abi.ABI
}
const (
vers080 = "0.8.0"
versLatest = "0.18.0"
)
var versions = []contractVersion{
{
version: vers080,
loadAbi: func() *abi.ABI {
return mustParseAbi(faultDisputeGameAbi020)
},
},
{
version: versLatest,
loadAbi: snapshots.LoadFaultDisputeGameABI,
},
}
func TestSimpleGetters(t *testing.T) {
tests := []struct {
methodAlias string
......@@ -33,13 +57,14 @@ func TestSimpleGetters(t *testing.T) {
args []interface{}
result interface{}
expected interface{} // Defaults to expecting the same as result
call func(game *FaultDisputeGameContract) (any, error)
call func(game FaultDisputeGameContract) (any, error)
applies func(version contractVersion) bool
}{
{
methodAlias: "status",
method: methodStatus,
result: types.GameStatusChallengerWon,
call: func(game *FaultDisputeGameContract) (any, error) {
call: func(game FaultDisputeGameContract) (any, error) {
return game.GetStatus(context.Background())
},
},
......@@ -48,16 +73,31 @@ func TestSimpleGetters(t *testing.T) {
method: methodMaxClockDuration,
result: uint64(5566),
expected: 5566 * time.Second,
call: func(game *FaultDisputeGameContract) (any, error) {
call: func(game FaultDisputeGameContract) (any, error) {
return game.GetMaxClockDuration(context.Background())
},
applies: func(version contractVersion) bool {
return version.version != vers080
},
},
{
methodAlias: "gameDuration",
method: methodGameDuration,
result: uint64(5566) * 2,
expected: 5566 * time.Second,
call: func(game FaultDisputeGameContract) (any, error) {
return game.GetMaxClockDuration(context.Background())
},
applies: func(version contractVersion) bool {
return version.version == vers080
},
},
{
methodAlias: "maxGameDepth",
method: methodMaxGameDepth,
result: big.NewInt(128),
expected: faultTypes.Depth(128),
call: func(game *FaultDisputeGameContract) (any, error) {
call: func(game FaultDisputeGameContract) (any, error) {
return game.GetMaxGameDepth(context.Background())
},
},
......@@ -65,7 +105,7 @@ func TestSimpleGetters(t *testing.T) {
methodAlias: "absolutePrestate",
method: methodAbsolutePrestate,
result: common.Hash{0xab},
call: func(game *FaultDisputeGameContract) (any, error) {
call: func(game FaultDisputeGameContract) (any, error) {
return game.GetAbsolutePrestateHash(context.Background())
},
},
......@@ -74,7 +114,7 @@ func TestSimpleGetters(t *testing.T) {
method: methodClaimCount,
result: big.NewInt(9876),
expected: uint64(9876),
call: func(game *FaultDisputeGameContract) (any, error) {
call: func(game FaultDisputeGameContract) (any, error) {
return game.GetClaimCount(context.Background())
},
},
......@@ -82,7 +122,7 @@ func TestSimpleGetters(t *testing.T) {
methodAlias: "l1Head",
method: methodL1Head,
result: common.Hash{0xdd, 0xbb},
call: func(game *FaultDisputeGameContract) (any, error) {
call: func(game FaultDisputeGameContract) (any, error) {
return game.GetL1Head(context.Background())
},
},
......@@ -90,15 +130,21 @@ func TestSimpleGetters(t *testing.T) {
methodAlias: "resolve",
method: methodResolve,
result: types.GameStatusInProgress,
call: func(game *FaultDisputeGameContract) (any, error) {
call: func(game FaultDisputeGameContract) (any, error) {
return game.CallResolve(context.Background())
},
},
}
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
for _, test := range tests {
test := test
t.Run(test.methodAlias, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
if test.applies != nil && !test.applies(version) {
t.Skip("Skipping for this version")
}
stubRpc, game := setupFaultDisputeGameTest(t, version)
stubRpc.SetResponse(fdgAddr, test.method, rpcblock.Latest, nil, []interface{}{test.result})
status, err := test.call(game)
require.NoError(t, err)
......@@ -109,6 +155,8 @@ func TestSimpleGetters(t *testing.T) {
require.Equal(t, expected, status)
})
}
})
}
}
func TestClock_EncodingDecoding(t *testing.T) {
......@@ -150,17 +198,25 @@ func TestClock_EncodingDecoding(t *testing.T) {
}
func TestGetOracleAddr(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
stubRpc.SetResponse(fdgAddr, methodVM, rpcblock.Latest, nil, []interface{}{vmAddr})
stubRpc.SetResponse(vmAddr, methodOracle, rpcblock.Latest, nil, []interface{}{oracleAddr})
actual, err := game.GetOracle(context.Background())
require.NoError(t, err)
require.Equal(t, oracleAddr, actual.Addr())
})
}
}
func TestGetClaim(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
idx := big.NewInt(2)
parentIndex := uint32(1)
counteredBy := common.Address{0x01}
......@@ -184,10 +240,15 @@ func TestGetClaim(t *testing.T) {
ContractIndex: int(idx.Uint64()),
ParentContractIndex: 1,
}, status)
})
}
}
func TestGetAllClaims(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
claim0 := faultTypes.Claim{
ClaimData: faultTypes.ClaimData{
Value: common.Hash{0xaa},
......@@ -232,13 +293,18 @@ func TestGetAllClaims(t *testing.T) {
claims, err := game.GetAllClaims(context.Background(), block)
require.NoError(t, err)
require.Equal(t, expectedClaims, claims)
})
}
}
func TestGetBalance(t *testing.T) {
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
wethAddr := common.Address{0x11, 0x55, 0x66}
balance := big.NewInt(9995877)
block := rpcblock.ByNumber(424)
stubRpc, game := setupFaultDisputeGameTest(t)
stubRpc, game := setupFaultDisputeGameTest(t, version)
stubRpc.SetResponse(fdgAddr, methodWETH, block, nil, []interface{}{wethAddr})
stubRpc.AddExpectedCall(batchingTest.NewGetBalanceCall(wethAddr, block, balance))
......@@ -246,57 +312,97 @@ func TestGetBalance(t *testing.T) {
require.NoError(t, err)
require.Equal(t, wethAddr, actualAddr)
require.Truef(t, balance.Cmp(actualBalance) == 0, "Expected balance %v but was %v", balance, actualBalance)
})
}
}
func TestCallResolveClaim(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
if version.version == vers080 {
stubRpc.SetResponse(fdgAddr, methodResolveClaim, rpcblock.Latest, []interface{}{big.NewInt(123)}, nil)
} else {
stubRpc.SetResponse(fdgAddr, methodResolveClaim, rpcblock.Latest, []interface{}{big.NewInt(123), maxChildChecks}, nil)
}
err := game.CallResolveClaim(context.Background(), 123)
require.NoError(t, err)
})
}
}
func TestResolveClaimTxTest(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
if version.version == vers080 {
stubRpc.SetResponse(fdgAddr, methodResolveClaim, rpcblock.Latest, []interface{}{big.NewInt(123)}, nil)
} else {
stubRpc.SetResponse(fdgAddr, methodResolveClaim, rpcblock.Latest, []interface{}{big.NewInt(123), maxChildChecks}, nil)
}
tx, err := game.ResolveClaimTx(123)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
})
}
}
func TestResolveTx(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
stubRpc.SetResponse(fdgAddr, methodResolve, rpcblock.Latest, nil, nil)
tx, err := game.ResolveTx()
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
})
}
}
func TestAttackTx(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
value := common.Hash{0xaa}
stubRpc.SetResponse(fdgAddr, methodAttack, rpcblock.Latest, []interface{}{big.NewInt(111), value}, nil)
tx, err := game.AttackTx(111, value)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
})
}
}
func TestDefendTx(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
value := common.Hash{0xaa}
stubRpc.SetResponse(fdgAddr, methodDefend, rpcblock.Latest, []interface{}{big.NewInt(111), value}, nil)
tx, err := game.DefendTx(111, value)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
})
}
}
func TestStepTx(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
stateData := []byte{1, 2, 3}
proofData := []byte{4, 5, 6, 7, 8, 9}
stubRpc.SetResponse(fdgAddr, methodStep, rpcblock.Latest, []interface{}{big.NewInt(111), true, stateData, proofData}, nil)
tx, err := game.StepTx(111, true, stateData, proofData)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
})
}
}
func expectGetClaim(stubRpc *batchingTest.AbiBasedRpc, block rpcblock.Block, claim faultTypes.Claim) {
......@@ -317,7 +423,10 @@ func expectGetClaim(stubRpc *batchingTest.AbiBasedRpc, block rpcblock.Block, cla
}
func TestGetBlockRange(t *testing.T) {
stubRpc, contract := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, contract := setupFaultDisputeGameTest(t, version)
expectedStart := uint64(65)
expectedEnd := uint64(102)
stubRpc.SetResponse(fdgAddr, methodStartingBlockNumber, rpcblock.Latest, nil, []interface{}{new(big.Int).SetUint64(expectedStart)})
......@@ -326,19 +435,29 @@ func TestGetBlockRange(t *testing.T) {
require.NoError(t, err)
require.Equal(t, expectedStart, start)
require.Equal(t, expectedEnd, end)
})
}
}
func TestGetSplitDepth(t *testing.T) {
stubRpc, contract := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, contract := setupFaultDisputeGameTest(t, version)
expectedSplitDepth := faultTypes.Depth(15)
stubRpc.SetResponse(fdgAddr, methodSplitDepth, rpcblock.Latest, nil, []interface{}{new(big.Int).SetUint64(uint64(expectedSplitDepth))})
splitDepth, err := contract.GetSplitDepth(context.Background())
require.NoError(t, err)
require.Equal(t, expectedSplitDepth, splitDepth)
})
}
}
func TestGetGameMetadata(t *testing.T) {
stubRpc, contract := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, contract := setupFaultDisputeGameTest(t, version)
expectedL1Head := common.Hash{0x0a, 0x0b}
expectedL2BlockNumber := uint64(123)
expectedMaxClockDuration := uint64(456)
......@@ -349,7 +468,11 @@ func TestGetGameMetadata(t *testing.T) {
stubRpc.SetResponse(fdgAddr, methodL2BlockNumber, block, nil, []interface{}{new(big.Int).SetUint64(expectedL2BlockNumber)})
stubRpc.SetResponse(fdgAddr, methodRootClaim, block, nil, []interface{}{expectedRootClaim})
stubRpc.SetResponse(fdgAddr, methodStatus, block, nil, []interface{}{expectedStatus})
if version.version == vers080 {
stubRpc.SetResponse(fdgAddr, methodGameDuration, block, nil, []interface{}{expectedMaxClockDuration * 2})
} else {
stubRpc.SetResponse(fdgAddr, methodMaxClockDuration, block, nil, []interface{}{expectedMaxClockDuration})
}
l1Head, l2BlockNumber, rootClaim, status, duration, err := contract.GetGameMetadata(context.Background(), block)
require.NoError(t, err)
require.Equal(t, expectedL1Head, l1Head)
......@@ -357,20 +480,30 @@ func TestGetGameMetadata(t *testing.T) {
require.Equal(t, expectedRootClaim, rootClaim)
require.Equal(t, expectedStatus, status)
require.Equal(t, expectedMaxClockDuration, duration)
})
}
}
func TestGetStartingRootHash(t *testing.T) {
stubRpc, contract := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, contract := setupFaultDisputeGameTest(t, version)
expectedOutputRoot := common.HexToHash("0x1234")
stubRpc.SetResponse(fdgAddr, methodStartingRootHash, rpcblock.Latest, nil, []interface{}{expectedOutputRoot})
startingOutputRoot, err := contract.GetStartingRootHash(context.Background())
require.NoError(t, err)
require.Equal(t, expectedOutputRoot, startingOutputRoot)
})
}
}
func TestFaultDisputeGame_UpdateOracleTx(t *testing.T) {
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
t.Run("Local", func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
stubRpc, game := setupFaultDisputeGameTest(t, version)
data := faultTypes.NewPreimageOracleData(common.Hash{0x01, 0xbc}.Bytes(), []byte{1, 2, 3, 4, 5, 6, 7}, 16)
claimIdx := uint64(6)
stubRpc.SetResponse(fdgAddr, methodAddLocalData, rpcblock.Latest, []interface{}{
......@@ -384,7 +517,7 @@ func TestFaultDisputeGame_UpdateOracleTx(t *testing.T) {
})
t.Run("Global", func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
stubRpc, game := setupFaultDisputeGameTest(t, version)
data := faultTypes.NewPreimageOracleData(common.Hash{0x02, 0xbc}.Bytes(), []byte{1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15}, 16)
claimIdx := uint64(6)
stubRpc.SetResponse(fdgAddr, methodVM, rpcblock.Latest, nil, []interface{}{vmAddr})
......@@ -397,10 +530,15 @@ func TestFaultDisputeGame_UpdateOracleTx(t *testing.T) {
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
})
})
}
}
func TestFaultDisputeGame_GetCredit(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
addr := common.Address{0x01}
expectedCredit := big.NewInt(4284)
expectedStatus := types.GameStatusChallengerWon
......@@ -411,10 +549,15 @@ func TestFaultDisputeGame_GetCredit(t *testing.T) {
require.NoError(t, err)
require.Equal(t, expectedCredit, actualCredit)
require.Equal(t, expectedStatus, actualStatus)
})
}
}
func TestFaultDisputeGame_GetCredits(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
block := rpcblock.ByNumber(482)
......@@ -431,11 +574,16 @@ func TestFaultDisputeGame_GetCredits(t *testing.T) {
for i := range expected {
require.Zerof(t, expected[i].Cmp(actual[i]), "expected: %v actual: %v", expected[i], actual[i])
}
})
}
}
func TestFaultDisputeGame_ClaimCreditTx(t *testing.T) {
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
t.Run("Success", func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
stubRpc, game := setupFaultDisputeGameTest(t, version)
addr := common.Address{0xaa}
stubRpc.SetResponse(fdgAddr, methodClaimCredit, rpcblock.Latest, []interface{}{addr}, nil)
......@@ -445,7 +593,7 @@ func TestFaultDisputeGame_ClaimCreditTx(t *testing.T) {
})
t.Run("SimulationFails", func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
stubRpc, game := setupFaultDisputeGameTest(t, version)
addr := common.Address{0xaa}
stubRpc.SetError(fdgAddr, methodClaimCredit, rpcblock.Latest, []interface{}{addr}, errors.New("still locked"))
......@@ -453,10 +601,15 @@ func TestFaultDisputeGame_ClaimCreditTx(t *testing.T) {
require.ErrorIs(t, err, ErrSimulationFailed)
require.Equal(t, txmgr.TxCandidate{}, tx)
})
})
}
}
func TestFaultDisputeGame_IsResolved(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
block := rpcblock.ByNumber(482)
......@@ -468,9 +621,26 @@ func TestFaultDisputeGame_IsResolved(t *testing.T) {
claimIdxs := []*big.Int{big.NewInt(1), big.NewInt(5), big.NewInt(13)}
expected := []bool{false, true, true}
if version.version == vers080 {
claimCount := 14
stubRpc.SetResponse(fdgAddr, methodClaimCount, block, nil, []interface{}{big.NewInt(int64(claimCount))})
for idx := 0; idx < claimCount; idx++ {
bond := big.NewInt(42)
if idx == 5 || idx == 13 { // The two claims expected to be resolved
bond = resolvedBondAmount
}
expectGetClaim(stubRpc, block, faultTypes.Claim{
ContractIndex: idx,
ClaimData: faultTypes.ClaimData{
Bond: bond,
},
})
}
} else {
for i, idx := range claimIdxs {
stubRpc.SetResponse(fdgAddr, methodResolvedSubgames, block, []interface{}{idx}, []interface{}{expected[i]})
}
}
actual, err := game.IsResolved(context.Background(), block, claims...)
require.NoError(t, err)
......@@ -478,10 +648,12 @@ func TestFaultDisputeGame_IsResolved(t *testing.T) {
for i := range expected {
require.Equal(t, expected[i], actual[i])
}
})
}
}
func setupFaultDisputeGameTest(t *testing.T) (*batchingTest.AbiBasedRpc, *FaultDisputeGameContract) {
fdgAbi := snapshots.LoadFaultDisputeGameABI()
func setupFaultDisputeGameTest(t *testing.T, version contractVersion) (*batchingTest.AbiBasedRpc, FaultDisputeGameContract) {
fdgAbi := version.loadAbi()
vmAbi := snapshots.LoadMIPSABI()
oracleAbi := snapshots.LoadPreimageOracleABI()
......@@ -490,6 +662,9 @@ func setupFaultDisputeGameTest(t *testing.T) (*batchingTest.AbiBasedRpc, *FaultD
stubRpc.AddContract(vmAddr, vmAbi)
stubRpc.AddContract(oracleAddr, oracleAbi)
caller := batching.NewMultiCaller(stubRpc, batching.DefaultBatchSize)
game := NewFaultDisputeGameContract(contractMetrics.NoopContractMetrics, fdgAddr, caller)
stubRpc.SetResponse(fdgAddr, methodVersion, rpcblock.Latest, nil, []interface{}{version.version})
game, err := NewFaultDisputeGameContract(context.Background(), contractMetrics.NoopContractMetrics, fdgAddr, caller)
require.NoError(t, err)
return stubRpc, game
}
......@@ -120,7 +120,10 @@ func registerAlphabet(
claimants []common.Address,
) error {
playerCreator := func(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) {
contract := contracts.NewFaultDisputeGameContract(m, game.Proxy, caller)
contract, err := contracts.NewFaultDisputeGameContract(ctx, m, game.Proxy, caller)
if err != nil {
return nil, fmt.Errorf("failed to create fault dispute game contract: %w", err)
}
oracle, err := contract.GetOracle(ctx)
if err != nil {
return nil, fmt.Errorf("failed to load oracle for game %v: %w", game.Proxy, err)
......@@ -157,7 +160,7 @@ func registerAlphabet(
registry.RegisterGameType(faultTypes.AlphabetGameType, playerCreator)
contractCreator := func(game types.GameMetadata) (claims.BondContract, error) {
return contracts.NewFaultDisputeGameContract(m, game.Proxy, caller), nil
return contracts.NewFaultDisputeGameContract(ctx, m, game.Proxy, caller)
}
registry.RegisterBondContract(faultTypes.AlphabetGameType, contractCreator)
return nil
......@@ -168,7 +171,10 @@ func registerOracle(ctx context.Context, m metrics.Metricer, oracles OracleRegis
if err != nil {
return fmt.Errorf("failed to load implementation for game type %v: %w", gameType, err)
}
contract := contracts.NewFaultDisputeGameContract(m, implAddr, caller)
contract, err := contracts.NewFaultDisputeGameContract(ctx, m, implAddr, caller)
if err != nil {
return fmt.Errorf("failed to create fault dispute game contracts: %w", err)
}
oracle, err := contract.GetOracle(ctx)
if err != nil {
return fmt.Errorf("failed to load oracle address: %w", err)
......@@ -199,7 +205,10 @@ func registerAsterisc(
) error {
asteriscPrestateProvider := asterisc.NewPrestateProvider(cfg.AsteriscAbsolutePreState)
playerCreator := func(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) {
contract := contracts.NewFaultDisputeGameContract(m, game.Proxy, caller)
contract, err := contracts.NewFaultDisputeGameContract(ctx, m, game.Proxy, caller)
if err != nil {
return nil, fmt.Errorf("failed to create fault dispute game contracts: %w", err)
}
oracle, err := contract.GetOracle(ctx)
if err != nil {
return nil, fmt.Errorf("failed to load oracle for game %v: %w", game.Proxy, err)
......@@ -236,7 +245,7 @@ func registerAsterisc(
registry.RegisterGameType(gameType, playerCreator)
contractCreator := func(game types.GameMetadata) (claims.BondContract, error) {
return contracts.NewFaultDisputeGameContract(m, game.Proxy, caller), nil
return contracts.NewFaultDisputeGameContract(ctx, m, game.Proxy, caller)
}
registry.RegisterBondContract(gameType, contractCreator)
return nil
......@@ -276,7 +285,10 @@ func registerCannon(
return cannon.NewPrestateProvider(prestatePath), nil
})
playerCreator := func(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) {
contract := contracts.NewFaultDisputeGameContract(m, game.Proxy, caller)
contract, err := contracts.NewFaultDisputeGameContract(ctx, m, game.Proxy, caller)
if err != nil {
return nil, fmt.Errorf("failed to create fault dispute game contracts: %w", err)
}
requiredPrestatehash, err := contract.GetAbsolutePrestateHash(ctx)
if err != nil {
return nil, fmt.Errorf("failed to load prestate hash for game %v: %w", game.Proxy, err)
......@@ -324,13 +336,13 @@ func registerCannon(
registry.RegisterGameType(gameType, playerCreator)
contractCreator := func(game types.GameMetadata) (claims.BondContract, error) {
return contracts.NewFaultDisputeGameContract(m, game.Proxy, caller), nil
return contracts.NewFaultDisputeGameContract(ctx, m, game.Proxy, caller)
}
registry.RegisterBondContract(gameType, contractCreator)
return nil
}
func loadL1Head(contract *contracts.FaultDisputeGameContract, ctx context.Context, l1HeaderSource L1HeaderSource) (eth.BlockID, error) {
func loadL1Head(contract contracts.FaultDisputeGameContract, ctx context.Context, l1HeaderSource L1HeaderSource) (eth.BlockID, error) {
l1Head, err := contract.GetL1Head(ctx)
if err != nil {
return eth.BlockID{}, fmt.Errorf("failed to load L1 head: %w", err)
......
......@@ -34,7 +34,7 @@ type GameCaller interface {
type GameCallerCreator struct {
m GameCallerMetrics
cache *caching.LRUCache[common.Address, *contracts.FaultDisputeGameContract]
cache *caching.LRUCache[common.Address, contracts.FaultDisputeGameContract]
caller *batching.MultiCaller
}
......@@ -42,17 +42,20 @@ func NewGameCallerCreator(m GameCallerMetrics, caller *batching.MultiCaller) *Ga
return &GameCallerCreator{
m: m,
caller: caller,
cache: caching.NewLRUCache[common.Address, *contracts.FaultDisputeGameContract](m, metricsLabel, 100),
cache: caching.NewLRUCache[common.Address, contracts.FaultDisputeGameContract](m, metricsLabel, 100),
}
}
func (g *GameCallerCreator) CreateContract(game gameTypes.GameMetadata) (GameCaller, error) {
func (g *GameCallerCreator) CreateContract(ctx context.Context, game gameTypes.GameMetadata) (GameCaller, error) {
if fdg, ok := g.cache.Get(game.Proxy); ok {
return fdg, nil
}
switch game.GameType {
case faultTypes.CannonGameType, faultTypes.AsteriscGameType, faultTypes.AlphabetGameType:
fdg := contracts.NewFaultDisputeGameContract(g.m, game.Proxy, g.caller)
fdg, err := contracts.NewFaultDisputeGameContract(ctx, g.m, game.Proxy, g.caller)
if err != nil {
return nil, fmt.Errorf("failed to create fault dispute game contract: %w", err)
}
g.cache.Add(game.Proxy, fdg)
return fdg, nil
default:
......
package extract
import (
"context"
"fmt"
"testing"
contractMetrics "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics"
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
......@@ -49,13 +51,13 @@ func TestMetadataCreator_CreateContract(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
caller, metrics := setupMetadataLoaderTest(t)
creator := NewGameCallerCreator(metrics, caller)
_, err := creator.CreateContract(test.game)
_, err := creator.CreateContract(context.Background(), test.game)
require.Equal(t, test.expectedErr, err)
if test.expectedErr == nil {
require.Equal(t, 1, metrics.cacheAddCalls)
require.Equal(t, 1, metrics.cacheGetCalls)
}
_, err = creator.CreateContract(test.game)
_, err = creator.CreateContract(context.Background(), test.game)
require.Equal(t, test.expectedErr, err)
if test.expectedErr == nil {
require.Equal(t, 1, metrics.cacheAddCalls)
......@@ -70,6 +72,7 @@ func setupMetadataLoaderTest(t *testing.T) (*batching.MultiCaller, *mockCacheMet
require.NoError(t, err)
stubRpc := batchingTest.NewAbiBasedRpc(t, fdgAddr, fdgAbi)
caller := batching.NewMultiCaller(stubRpc, batching.DefaultBatchSize)
stubRpc.SetResponse(fdgAddr, "version", rpcblock.Latest, nil, []interface{}{"0.18.0"})
return caller, &mockCacheMetrics{}
}
......
......@@ -13,7 +13,7 @@ import (
)
type (
CreateGameCaller func(game gameTypes.GameMetadata) (GameCaller, error)
CreateGameCaller func(ctx context.Context, game gameTypes.GameMetadata) (GameCaller, error)
FactoryGameFetcher func(ctx context.Context, blockHash common.Hash, earliestTimestamp uint64) ([]gameTypes.GameMetadata, error)
)
......@@ -48,7 +48,7 @@ func (e *Extractor) Extract(ctx context.Context, blockHash common.Hash, minTimes
func (e *Extractor) enrichGames(ctx context.Context, blockHash common.Hash, games []gameTypes.GameMetadata) []*monTypes.EnrichedGameData {
var enrichedGames []*monTypes.EnrichedGameData
for _, game := range games {
caller, err := e.createContract(game)
caller, err := e.createContract(ctx, game)
if err != nil {
e.logger.Error("Failed to create game caller", "err", err)
continue
......
......@@ -167,7 +167,7 @@ type mockGameCallerCreator struct {
caller *mockGameCaller
}
func (m *mockGameCallerCreator) CreateGameCaller(_ gameTypes.GameMetadata) (GameCaller, error) {
func (m *mockGameCallerCreator) CreateGameCaller(_ context.Context, _ gameTypes.GameMetadata) (GameCaller, error) {
m.calls++
if m.err != nil {
return nil, m.err
......
......@@ -39,7 +39,8 @@ func (g *OutputAlphabetGameHelper) StartChallenger(
func (g *OutputAlphabetGameHelper) CreateHonestActor(ctx context.Context, l2Node string) *OutputHonestHelper {
logger := testlog.Logger(g.T, log.LevelInfo).New("role", "HonestHelper", "game", g.Addr)
caller := batching.NewMultiCaller(g.System.NodeClient("l1").Client(), batching.DefaultBatchSize)
contract := contracts.NewFaultDisputeGameContract(contractMetrics.NoopContractMetrics, g.Addr, caller)
contract, err := contracts.NewFaultDisputeGameContract(ctx, contractMetrics.NoopContractMetrics, g.Addr, caller)
g.Require.NoError(err)
prestateBlock, poststateBlock, err := contract.GetBlockRange(ctx)
g.Require.NoError(err, "Get block range")
splitDepth := g.SplitDepth(ctx)
......
......@@ -63,7 +63,8 @@ func (g *OutputCannonGameHelper) CreateHonestActor(ctx context.Context, l2Node s
logger := testlog.Logger(g.T, log.LevelInfo).New("role", "HonestHelper", "game", g.Addr)
l2Client := g.System.NodeClient(l2Node)
caller := batching.NewMultiCaller(g.System.NodeClient("l1").Client(), batching.DefaultBatchSize)
contract := contracts.NewFaultDisputeGameContract(contractMetrics.NoopContractMetrics, g.Addr, caller)
contract, err := contracts.NewFaultDisputeGameContract(ctx, contractMetrics.NoopContractMetrics, g.Addr, caller)
g.Require.NoError(err)
prestateBlock, poststateBlock, err := contract.GetBlockRange(ctx)
g.Require.NoError(err, "Failed to load block range")
......@@ -288,7 +289,8 @@ func (g *OutputCannonGameHelper) createCannonTraceProvider(ctx context.Context,
caller := batching.NewMultiCaller(g.System.NodeClient("l1").Client(), batching.DefaultBatchSize)
l2Client := g.System.NodeClient(l2Node)
contract := contracts.NewFaultDisputeGameContract(contractMetrics.NoopContractMetrics, g.Addr, caller)
contract, err := contracts.NewFaultDisputeGameContract(ctx, contractMetrics.NoopContractMetrics, g.Addr, caller)
g.Require.NoError(err)
prestateBlock, poststateBlock, err := contract.GetBlockRange(ctx)
g.Require.NoError(err, "Failed to load block range")
......
......@@ -713,7 +713,8 @@ func (g *OutputGameHelper) UploadPreimage(ctx context.Context, data *types.Preim
func (g *OutputGameHelper) oracle(ctx context.Context) *contracts.PreimageOracleContract {
caller := batching.NewMultiCaller(g.System.NodeClient("l1").Client(), batching.DefaultBatchSize)
contract := contracts.NewFaultDisputeGameContract(contractMetrics.NoopContractMetrics, g.Addr, caller)
contract, err := contracts.NewFaultDisputeGameContract(ctx, contractMetrics.NoopContractMetrics, g.Addr, caller)
g.Require.NoError(err)
oracle, err := contract.GetOracle(ctx)
g.Require.NoError(err, "Failed to create oracle contract")
return oracle
......
......@@ -17,11 +17,11 @@ type OutputHonestHelper struct {
t *testing.T
require *require.Assertions
game *OutputGameHelper
contract *contracts.FaultDisputeGameContract
contract contracts.FaultDisputeGameContract
correctTrace types.TraceAccessor
}
func NewOutputHonestHelper(t *testing.T, require *require.Assertions, game *OutputGameHelper, contract *contracts.FaultDisputeGameContract, correctTrace types.TraceAccessor) *OutputHonestHelper {
func NewOutputHonestHelper(t *testing.T, require *require.Assertions, game *OutputGameHelper, contract contracts.FaultDisputeGameContract, correctTrace types.TraceAccessor) *OutputHonestHelper {
return &OutputHonestHelper{
t: t,
require: require,
......
......@@ -63,3 +63,7 @@ func (c *CallResult) GetBytes32(i int) [32]byte {
func (c *CallResult) GetBytes32Slice(i int) [][32]byte {
return *abi.ConvertType(c.out[i], new([][32]byte)).(*[][32]byte)
}
func (c *CallResult) GetString(i int) string {
return *abi.ConvertType(c.out[i], new(string)).(*string)
}
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