Skip to content

Commit

Permalink
e2e: Convert more fault proof tests to use the custom contract bindin…
Browse files Browse the repository at this point in the history
…gs. (#10716)
  • Loading branch information
ajsutton committed Jun 3, 2024
1 parent f638bfa commit 5e23d3a
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 76 deletions.
5 changes: 3 additions & 2 deletions op-challenger/game/fault/contracts/faultdisputegame.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ func (f *FaultDisputeGameContractLatest) getDelayedWETH(ctx context.Context, blo

func (f *FaultDisputeGameContractLatest) GetOracle(ctx context.Context) (*PreimageOracleContract, error) {
defer f.metrics.StartContractRequest("GetOracle")()
vm, err := f.vm(ctx)
vm, err := f.Vm(ctx)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -458,7 +458,7 @@ func (f *FaultDisputeGameContractLatest) IsResolved(ctx context.Context, block r
return resolved, nil
}

func (f *FaultDisputeGameContractLatest) 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)
Expand Down Expand Up @@ -623,4 +623,5 @@ type FaultDisputeGameContract interface {
ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, error)
CallResolve(ctx context.Context) (gameTypes.GameStatus, error)
ResolveTx() (txmgr.TxCandidate, error)
Vm(ctx context.Context) (*VMContract, error)
}
4 changes: 4 additions & 0 deletions op-challenger/game/fault/contracts/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ func NewVMContract(addr common.Address, caller *batching.MultiCaller) *VMContrac
}
}

func (c *VMContract) Addr() common.Address {
return c.contract.Addr()
}

func (c *VMContract) Oracle(ctx context.Context) (*PreimageOracleContract, error) {
results, err := c.multiCaller.SingleCall(ctx, rpcblock.Latest, c.contract.Call(methodOracle))
if err != nil {
Expand Down
14 changes: 7 additions & 7 deletions op-e2e/e2eutils/disputegame/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package disputegame

import (
"context"
"crypto/ecdsa"
"encoding/binary"
"math/big"
"testing"
Expand Down Expand Up @@ -88,6 +89,7 @@ type FactoryHelper struct {
System DisputeSystem
Client *ethclient.Client
Opts *bind.TransactOpts
PrivKey *ecdsa.PrivateKey
FactoryAddr common.Address
Factory *bindings.DisputeGameFactory
}
Expand All @@ -97,7 +99,8 @@ func NewFactoryHelper(t *testing.T, ctx context.Context, system DisputeSystem) *
client := system.NodeClient("l1")
chainID, err := client.ChainID(ctx)
require.NoError(err)
opts, err := bind.NewKeyedTransactorWithChainID(TestKey, chainID)
privKey := TestKey
opts, err := bind.NewKeyedTransactorWithChainID(privKey, chainID)
require.NoError(err)

l1Deployments := system.L1Deployments()
Expand All @@ -111,6 +114,7 @@ func NewFactoryHelper(t *testing.T, ctx context.Context, system DisputeSystem) *
System: system,
Client: client,
Opts: opts,
PrivKey: privKey,
Factory: factory,
FactoryAddr: factoryAddr,
}
Expand Down Expand Up @@ -167,8 +171,6 @@ func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, l2Node string
h.Require.Len(rcpt.Logs, 2, "should have emitted a single DisputeGameCreated event")
createdEvent, err := h.Factory.ParseDisputeGameCreated(*rcpt.Logs[1])
h.Require.NoError(err)
gameBindings, err := bindings.NewFaultDisputeGame(createdEvent.DisputeProxy, h.Client)
h.Require.NoError(err)
game, err := contracts.NewFaultDisputeGameContract(ctx, metrics.NoopContractMetrics, createdEvent.DisputeProxy, batching.NewMultiCaller(h.Client.Client(), batching.DefaultBatchSize))
h.Require.NoError(err)

Expand All @@ -182,7 +184,7 @@ func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, l2Node string
provider := outputs.NewTraceProvider(logger, prestateProvider, rollupClient, l2Client, l1Head, splitDepth, prestateBlock, poststateBlock)

return &OutputCannonGameHelper{
OutputGameHelper: *NewOutputGameHelper(h.T, h.Require, h.Client, h.Opts, game, gameBindings, h.FactoryAddr, createdEvent.DisputeProxy, provider, h.System),
OutputGameHelper: *NewOutputGameHelper(h.T, h.Require, h.Client, h.Opts, h.PrivKey, game, h.FactoryAddr, createdEvent.DisputeProxy, provider, h.System),
}
}

Expand Down Expand Up @@ -223,8 +225,6 @@ func (h *FactoryHelper) StartOutputAlphabetGame(ctx context.Context, l2Node stri
h.Require.Len(rcpt.Logs, 2, "should have emitted a single DisputeGameCreated event")
createdEvent, err := h.Factory.ParseDisputeGameCreated(*rcpt.Logs[1])
h.Require.NoError(err)
gameBindings, err := bindings.NewFaultDisputeGame(createdEvent.DisputeProxy, h.Client)
h.Require.NoError(err)
game, err := contracts.NewFaultDisputeGameContract(ctx, metrics.NoopContractMetrics, createdEvent.DisputeProxy, batching.NewMultiCaller(h.Client.Client(), batching.DefaultBatchSize))
h.Require.NoError(err)

Expand All @@ -238,7 +238,7 @@ func (h *FactoryHelper) StartOutputAlphabetGame(ctx context.Context, l2Node stri
provider := outputs.NewTraceProvider(logger, prestateProvider, rollupClient, l2Client, l1Head, splitDepth, prestateBlock, poststateBlock)

return &OutputAlphabetGameHelper{
OutputGameHelper: *NewOutputGameHelper(h.T, h.Require, h.Client, h.Opts, game, gameBindings, h.FactoryAddr, createdEvent.DisputeProxy, provider, h.System),
OutputGameHelper: *NewOutputGameHelper(h.T, h.Require, h.Client, h.Opts, h.PrivKey, game, h.FactoryAddr, createdEvent.DisputeProxy, provider, h.System),
}
}

Expand Down
17 changes: 6 additions & 11 deletions op-e2e/e2eutils/disputegame/output_cannon_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-e2e/bindings"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/transactions"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"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/testlog"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)
Expand Down Expand Up @@ -233,27 +233,22 @@ func (g *OutputCannonGameHelper) VerifyPreimage(ctx context.Context, outputRootC
g.Require.NotNil(oracleData, "Should have had required preimage oracle data")
g.Require.Equal(common.Hash(preimageKey.PreimageKey()).Bytes(), oracleData.OracleKey, "Must have correct preimage key")

tx, err := g.GameBindings.AddLocalData(g.Opts,
oracleData.GetIdent(),
big.NewInt(outputRootClaim.Index),
new(big.Int).SetUint64(uint64(oracleData.OracleOffset)))
g.Require.NoError(err)
_, err = wait.ForReceiptOK(ctx, g.Client, tx.Hash())
g.Require.NoError(err)
candidate, err := g.Game.UpdateOracleTx(ctx, uint64(outputRootClaim.Index), oracleData)
g.Require.NoError(err, "failed to get oracle")
transactions.RequireSendTx(g.T, ctx, g.Client, candidate, g.PrivKey)

expectedPostState, err := provider.Get(ctx, pos)
g.Require.NoError(err, "Failed to get expected post state")

callOpts := &bind.CallOpts{Context: ctx}
vmAddr, err := g.GameBindings.Vm(callOpts)
vm, err := g.Game.Vm(ctx)
g.Require.NoError(err, "Failed to get VM address")

abi, err := bindings.MIPSMetaData.GetAbi()
g.Require.NoError(err, "Failed to load MIPS ABI")
caller := batching.NewMultiCaller(g.Client.Client(), batching.DefaultBatchSize)
result, err := caller.SingleCall(ctx, rpcblock.Latest, &batching.ContractCall{
Abi: abi,
Addr: vmAddr,
Addr: vm.Addr(),
Method: "step",
Args: []interface{}{
prestate, proof, localContext,
Expand Down
82 changes: 27 additions & 55 deletions op-e2e/e2eutils/disputegame/output_game_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package disputegame
import (
"context"
"crypto/ecdsa"
"errors"
"fmt"
"math/big"
"testing"
Expand All @@ -15,6 +16,7 @@ import (
keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-e2e/bindings"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/transactions"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum-optimism/optimism/op-service/eth"
Expand All @@ -33,23 +35,23 @@ type OutputGameHelper struct {
Require *require.Assertions
Client *ethclient.Client
Opts *bind.TransactOpts
PrivKey *ecdsa.PrivateKey
Game contracts.FaultDisputeGameContract
GameBindings *bindings.FaultDisputeGame
FactoryAddr common.Address
Addr common.Address
CorrectOutputProvider *outputs.OutputTraceProvider
System DisputeSystem
}

func NewOutputGameHelper(t *testing.T, require *require.Assertions, client *ethclient.Client, opts *bind.TransactOpts,
game contracts.FaultDisputeGameContract, gameBindings *bindings.FaultDisputeGame, factoryAddr common.Address, addr common.Address, correctOutputProvider *outputs.OutputTraceProvider, system DisputeSystem) *OutputGameHelper {
func NewOutputGameHelper(t *testing.T, require *require.Assertions, client *ethclient.Client, opts *bind.TransactOpts, privKey *ecdsa.PrivateKey,
game contracts.FaultDisputeGameContract, factoryAddr common.Address, addr common.Address, correctOutputProvider *outputs.OutputTraceProvider, system DisputeSystem) *OutputGameHelper {
return &OutputGameHelper{
T: t,
Require: require,
Client: client,
Opts: opts,
PrivKey: privKey,
Game: game,
GameBindings: gameBindings,
FactoryAddr: factoryAddr,
Addr: addr,
CorrectOutputProvider: correctOutputProvider,
Expand Down Expand Up @@ -203,22 +205,13 @@ func (g *OutputGameHelper) AvailableCredit(ctx context.Context, addr common.Addr
}

func (g *OutputGameHelper) CreditUnlockDuration(ctx context.Context) time.Duration {
weth, err := g.GameBindings.Weth(&bind.CallOpts{Context: ctx})
g.Require.NoError(err, "Failed to get WETH contract")
contract, err := bindings.NewDelayedWETH(weth, g.Client)
g.Require.NoError(err)
period, err := contract.Delay(&bind.CallOpts{Context: ctx})
g.Require.NoError(err, "Failed to get WETH unlock period")
float, _ := period.Float64()
return time.Duration(float) * time.Second
_, delay, _, err := g.Game.GetBalanceAndDelay(ctx, rpcblock.Latest)
g.Require.NoError(err, "Failed to get withdrawal delay")
return delay
}

func (g *OutputGameHelper) WethBalance(ctx context.Context, addr common.Address) *big.Int {
weth, err := g.GameBindings.Weth(&bind.CallOpts{Context: ctx})
g.Require.NoError(err, "Failed to get WETH contract")
contract, err := bindings.NewDelayedWETH(weth, g.Client)
g.Require.NoError(err)
balance, err := contract.BalanceOf(&bind.CallOpts{Context: ctx}, addr)
balance, _, _, err := g.Game.GetBalanceAndDelay(ctx, rpcblock.Latest)
g.Require.NoError(err, "Failed to get WETH balance")
return balance
}
Expand Down Expand Up @@ -363,10 +356,9 @@ func (g *OutputGameHelper) WaitForAllClaimsCountered(ctx context.Context) {
func (g *OutputGameHelper) Resolve(ctx context.Context) {
ctx, cancel := context.WithTimeout(ctx, time.Minute)
defer cancel()
tx, err := g.GameBindings.Resolve(g.Opts)
g.Require.NoError(err)
_, err = wait.ForReceiptOK(ctx, g.Client, tx.Hash())
candidate, err := g.Game.ResolveTx()
g.Require.NoError(err)
transactions.RequireSendTx(g.T, ctx, g.Client, candidate, g.PrivKey)
}

func (g *OutputGameHelper) Status(ctx context.Context) gameTypes.GameStatus {
Expand Down Expand Up @@ -545,11 +537,10 @@ func (g *OutputGameHelper) Attack(ctx context.Context, claimIdx int64, claim com
claimData, err := g.Game.GetClaim(ctx, uint64(claimIdx))
g.Require.NoError(err, "Failed to get claim data")
attackPos := claimData.Position.Attack()
transactOpts := g.makeBondedTransactOpts(ctx, claimData.Position.Attack().ToGIndex(), cfg.Opts)

err = g.sendMove(ctx, func() (*gethtypes.Transaction, error) {
return g.GameBindings.Attack(transactOpts, claimData.Value, big.NewInt(claimIdx), claim)
})
candidate, err := g.Game.AttackTx(ctx, claimData, claim)
g.Require.NoError(err, "Failed to create tx candidate")
_, _, err = transactions.SendTx(ctx, g.Client, candidate, g.PrivKey)
if err != nil {
if cfg.ignoreDupes && g.hasClaim(ctx, claimIdx, attackPos, claim) {
return
Expand All @@ -565,11 +556,10 @@ func (g *OutputGameHelper) Defend(ctx context.Context, claimIdx int64, claim com
claimData, err := g.Game.GetClaim(ctx, uint64(claimIdx))
g.Require.NoError(err, "Failed to get claim data")
defendPos := claimData.Position.Defend()
transactOpts := g.makeBondedTransactOpts(ctx, defendPos.ToGIndex(), cfg.Opts)

err = g.sendMove(ctx, func() (*gethtypes.Transaction, error) {
return g.GameBindings.Defend(transactOpts, claimData.Value, big.NewInt(claimIdx), claim)
})
candidate, err := g.Game.DefendTx(ctx, claimData, claim)
g.Require.NoError(err, "Failed to create tx candidate")
_, _, err = transactions.SendTx(ctx, g.Client, candidate, g.PrivKey)
if err != nil {
if cfg.ignoreDupes && g.hasClaim(ctx, claimIdx, defendPos, claim) {
return
Expand All @@ -588,45 +578,27 @@ func (g *OutputGameHelper) hasClaim(ctx context.Context, parentIdx int64, pos ty
return false
}

func (g *OutputGameHelper) sendMove(ctx context.Context, send func() (*gethtypes.Transaction, error)) error {
tx, err := send()
if err != nil {
return fmt.Errorf("transaction did not send: %w", err)
}
_, err = wait.ForReceiptOK(ctx, g.Client, tx.Hash())
if err != nil {
return fmt.Errorf("transaction was not ok: %w", err)
}
return nil
}

func (g *OutputGameHelper) makeBondedTransactOpts(ctx context.Context, pos *big.Int, Opts *bind.TransactOpts) *bind.TransactOpts {
bOpts := *Opts
bond, err := g.GameBindings.GetRequiredBond(&bind.CallOpts{Context: ctx}, pos)
g.Require.NoError(err, "Failed to get required bond")
bOpts.Value = bond
return &bOpts
}

type ErrWithData interface {
ErrorData() interface{}
}

// StepFails attempts to call step and verifies that it fails with ValidStep()
func (g *OutputGameHelper) StepFails(claimIdx int64, isAttack bool, stateData []byte, proof []byte) {
func (g *OutputGameHelper) StepFails(ctx context.Context, claimIdx int64, isAttack bool, stateData []byte, proof []byte) {
g.T.Logf("Attempting step against claim %v isAttack: %v", claimIdx, isAttack)
_, err := g.GameBindings.Step(g.Opts, big.NewInt(claimIdx), isAttack, stateData, proof)
errData, ok := err.(ErrWithData)
candidate, err := g.Game.StepTx(uint64(claimIdx), isAttack, stateData, proof)
g.Require.NoError(err, "Failed to create tx candidate")
_, _, err = transactions.SendTx(ctx, g.Client, candidate, g.PrivKey, transactions.WithReceiptFail())
var errData ErrWithData
ok := errors.As(err, &errData)
g.Require.Truef(ok, "Error should provide ErrorData method: %v", err)
g.Require.Equal("0xfb4e40dd", errData.ErrorData(), "Revert reason should be abi encoded ValidStep()")
}

// ResolveClaim resolves a single subgame
func (g *OutputGameHelper) ResolveClaim(ctx context.Context, claimIdx int64) {
tx, err := g.GameBindings.ResolveClaim(g.Opts, big.NewInt(claimIdx), common.Big0)
g.Require.NoError(err, "ResolveClaim transaction did not send")
_, err = wait.ForReceiptOK(ctx, g.Client, tx.Hash())
g.Require.NoError(err, "ResolveClaim transaction was not OK")
candidate, err := g.Game.ResolveClaimTx(uint64(claimIdx))
g.Require.NoError(err, "Failed to create resolve claim candidate tx")
transactions.RequireSendTx(g.T, ctx, g.Client, candidate, g.PrivKey)
}

// ChallengePeriod returns the challenge period fetched from the PreimageOracle contract.
Expand Down
2 changes: 1 addition & 1 deletion op-e2e/e2eutils/disputegame/output_honest_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func (h *OutputHonestHelper) StepFails(ctx context.Context, claimIdx int64, isAt
}
prestate, proofData, _, err := h.correctTrace.GetStepData(ctx, game, claim, pos)
h.require.NoError(err, "Get step data")
h.game.StepFails(claimIdx, isAttack, prestate, proofData)
h.game.StepFails(ctx, claimIdx, isAttack, prestate, proofData)
}

func (h *OutputHonestHelper) loadState(ctx context.Context, claimIdx int64) (types.Game, types.Claim) {
Expand Down
Loading

0 comments on commit 5e23d3a

Please sign in to comment.