Commit 5b749451 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-challenger: Delay claiming credit until games are complete (#9720)

Avoids paying additional transaction costs while a game's claims are being resolved and works better with bond locking.
parent 39881a87
......@@ -17,7 +17,7 @@ type BondClaimMetrics interface {
}
type BondContract interface {
GetCredit(ctx context.Context, receipient common.Address) (*big.Int, error)
GetCredit(ctx context.Context, receipient common.Address) (*big.Int, types.GameStatus, error)
ClaimCredit(receipient common.Address) (txmgr.TxCandidate, error)
}
......@@ -55,11 +55,15 @@ func (c *Claimer) claimBond(ctx context.Context, game types.GameMetadata) error
if err != nil {
return fmt.Errorf("failed to create bond contract bindings: %w", err)
}
credit, err := contract.GetCredit(ctx, c.txSender.From())
credit, status, err := contract.GetCredit(ctx, c.txSender.From())
if err != nil {
return fmt.Errorf("failed to get credit: %w", err)
}
if status == types.GameStatusInProgress {
c.logger.Debug("Not claiming credit from in progress game", "game", game.Proxy, "status", status)
return nil
}
if credit.Cmp(big.NewInt(0)) == 0 {
c.logger.Debug("No credit to claim", "game", game.Proxy)
return nil
......
......@@ -40,6 +40,17 @@ func TestClaimer_ClaimBonds(t *testing.T) {
require.Equal(t, 1, m.RecordBondClaimedCalls)
})
t.Run("BondClaimSkippedForInProgressGame", func(t *testing.T) {
gameAddr := common.HexToAddress("0x1234")
c, m, contract, txSender := newTestClaimer(t, gameAddr)
contract.credit = 1
contract.status = types.GameStatusInProgress
err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}})
require.NoError(t, err)
require.Equal(t, 0, txSender.sends)
require.Equal(t, 0, m.RecordBondClaimedCalls)
})
t.Run("BondClaimFails", func(t *testing.T) {
gameAddr := common.HexToAddress("0x1234")
c, m, contract, txSender := newTestClaimer(t, gameAddr)
......@@ -77,7 +88,7 @@ func newTestClaimer(t *testing.T, gameAddr common.Address) (*Claimer, *mockClaim
logger := testlog.Logger(t, log.LvlDebug)
m := &mockClaimMetrics{}
txSender := &mockTxSender{}
bondContract := &stubBondContract{}
bondContract := &stubBondContract{status: types.GameStatusChallengerWon}
contractCreator := func(game types.GameMetadata) (BondContract, error) {
return bondContract, nil
}
......@@ -116,10 +127,11 @@ func (s *mockTxSender) SendAndWait(_ string, _ ...txmgr.TxCandidate) ([]*ethtype
type stubBondContract struct {
credit int64
status types.GameStatus
}
func (s *stubBondContract) GetCredit(_ context.Context, _ common.Address) (*big.Int, error) {
return big.NewInt(s.credit), nil
func (s *stubBondContract) GetCredit(_ context.Context, _ common.Address) (*big.Int, types.GameStatus, error) {
return big.NewInt(s.credit), s.status, nil
}
func (s *stubBondContract) ClaimCredit(_ common.Address) (txmgr.TxCandidate, error) {
......
......@@ -119,12 +119,22 @@ func (c *FaultDisputeGameContract) GetSplitDepth(ctx context.Context) (types.Dep
return types.Depth(splitDepth.GetBigInt(0).Uint64()), nil
}
func (c *FaultDisputeGameContract) GetCredit(ctx context.Context, recipient common.Address) (*big.Int, error) {
if credits, err := c.GetCredits(ctx, batching.BlockLatest, recipient); err != nil {
return nil, err
} else {
return credits[0], nil
func (c *FaultDisputeGameContract) GetCredit(ctx context.Context, recipient common.Address) (*big.Int, gameTypes.GameStatus, error) {
results, err := c.multiCaller.Call(ctx, batching.BlockLatest,
c.contract.Call(methodCredit, recipient),
c.contract.Call(methodStatus))
if err != nil {
return nil, gameTypes.GameStatusInProgress, err
}
if len(results) != 2 {
return nil, gameTypes.GameStatusInProgress, fmt.Errorf("expected 2 results but got %v", len(results))
}
credit := results[0].GetBigInt(0)
status, err := gameTypes.GameStatusFromUint8(results[1].GetUint8(0))
if err != nil {
return nil, gameTypes.GameStatusInProgress, fmt.Errorf("invalid game status %v: %w", status, err)
}
return credit, status, nil
}
func (c *FaultDisputeGameContract) GetCredits(ctx context.Context, block batching.Block, recipients ...common.Address) ([]*big.Int, error) {
......
......@@ -377,12 +377,15 @@ func TestFaultDisputeGame_UpdateOracleTx(t *testing.T) {
func TestFaultDisputeGame_GetCredit(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t)
addr := common.Address{0x01}
expected := big.NewInt(4284)
stubRpc.SetResponse(fdgAddr, methodCredit, batching.BlockLatest, []interface{}{addr}, []interface{}{expected})
expectedCredit := big.NewInt(4284)
expectedStatus := types.GameStatusChallengerWon
stubRpc.SetResponse(fdgAddr, methodCredit, batching.BlockLatest, []interface{}{addr}, []interface{}{expectedCredit})
stubRpc.SetResponse(fdgAddr, methodStatus, batching.BlockLatest, nil, []interface{}{expectedStatus})
actual, err := game.GetCredit(context.Background(), addr)
actualCredit, actualStatus, err := game.GetCredit(context.Background(), addr)
require.NoError(t, err)
require.Equal(t, expected, actual)
require.Equal(t, expectedCredit, actualCredit)
require.Equal(t, expectedStatus, actualStatus)
}
func TestFaultDisputeGame_GetCredits(t *testing.T) {
......
......@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/claims"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/preimages"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/responder"
......@@ -44,6 +45,7 @@ type GamePlayer struct {
type GameContract interface {
preimages.PreimageGameContract
responder.GameContract
claims.BondContract
GameInfo
ClaimLoader
GetStatus(ctx context.Context) (gameTypes.GameStatus, error)
......
......@@ -24,8 +24,6 @@ type GameContract interface {
DefendTx(parentContractIndex uint64, pivot common.Hash) (txmgr.TxCandidate, error)
StepTx(claimIdx uint64, isAttack bool, stateData []byte, proof []byte) (txmgr.TxCandidate, error)
GetRequiredBond(ctx context.Context, position types.Position) (*big.Int, error)
GetCredit(ctx context.Context, receipient common.Address) (*big.Int, error)
ClaimCredit(receipient common.Address) (txmgr.TxCandidate, error)
}
type Oracle interface {
......
......@@ -123,7 +123,7 @@ func (s stubPreimageOracle) GetActivePreimages(_ context.Context, _ common.Hash)
type stubBondContract struct{}
func (s *stubBondContract) GetCredit(ctx context.Context, receipient common.Address) (*big.Int, error) {
func (s *stubBondContract) GetCredit(ctx context.Context, receipient common.Address) (*big.Int, types.GameStatus, error) {
panic("not supported")
}
......
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