Commit 7943c1a0 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

Merge pull request #8070 from ethereum-optimism/aj/responder-contract-call

op-challenger: Use new contract bindings in responder
parents 24520406 37b2b0f0
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/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"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
...@@ -21,6 +22,11 @@ const ( ...@@ -21,6 +22,11 @@ const (
methodClaim = "claimData" methodClaim = "claimData"
methodL1Head = "l1Head" methodL1Head = "l1Head"
methodProposals = "proposals" methodProposals = "proposals"
methodResolve = "resolve"
methodResolveClaim = "resolveClaim"
methodAttack = "attack"
methodDefend = "defend"
methodStep = "step"
) )
type FaultDisputeGameContract struct { type FaultDisputeGameContract struct {
...@@ -138,6 +144,57 @@ func (f *FaultDisputeGameContract) GetAllClaims(ctx context.Context) ([]types.Cl ...@@ -138,6 +144,57 @@ func (f *FaultDisputeGameContract) GetAllClaims(ctx context.Context) ([]types.Cl
return claims, nil return claims, nil
} }
func (f *FaultDisputeGameContract) 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) {
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) {
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 {
call := f.resolveClaimCall(claimIdx)
_, err := f.multiCaller.SingleCall(ctx, batching.BlockLatest, call)
if err != nil {
return fmt.Errorf("failed to call resolve claim: %w", err)
}
return nil
}
func (f *FaultDisputeGameContract) ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, error) {
call := f.resolveClaimCall(claimIdx)
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract) resolveClaimCall(claimIdx uint64) *batching.ContractCall {
return f.contract.Call(methodResolveClaim, new(big.Int).SetUint64(claimIdx))
}
func (f *FaultDisputeGameContract) CallResolve(ctx context.Context) (gameTypes.GameStatus, error) {
call := f.resolveCall()
result, err := f.multiCaller.SingleCall(ctx, batching.BlockLatest, call)
if err != nil {
return gameTypes.GameStatusInProgress, fmt.Errorf("failed to call resolve: %w", err)
}
return gameTypes.GameStatusFromUint8(result.GetUint8(0))
}
func (f *FaultDisputeGameContract) ResolveTx() (txmgr.TxCandidate, error) {
call := f.resolveCall()
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract) resolveCall() *batching.ContractCall {
return f.contract.Call(methodResolve)
}
func (f *FaultDisputeGameContract) decodeClaim(result *batching.CallResult, contractIndex int) types.Claim { func (f *FaultDisputeGameContract) decodeClaim(result *batching.CallResult, contractIndex int) types.Claim {
parentIndex := result.GetUint32(0) parentIndex := result.GetUint32(0)
countered := result.GetBool(1) countered := result.GetBool(1)
......
...@@ -67,6 +67,13 @@ func TestSimpleGetters(t *testing.T) { ...@@ -67,6 +67,13 @@ func TestSimpleGetters(t *testing.T) {
return game.GetL1Head(context.Background()) return game.GetL1Head(context.Background())
}, },
}, },
{
method: methodResolve,
result: types.GameStatusInProgress,
call: func(game *FaultDisputeGameContract) (any, error) {
return game.CallResolve(context.Background())
},
},
} }
for _, test := range tests { for _, test := range tests {
test := test test := test
...@@ -176,6 +183,57 @@ func TestGetAllClaims(t *testing.T) { ...@@ -176,6 +183,57 @@ func TestGetAllClaims(t *testing.T) {
require.Equal(t, expectedClaims, claims) require.Equal(t, expectedClaims, claims)
} }
func TestCallResolveClaim(t *testing.T) {
stubRpc, game := setup(t)
stubRpc.SetResponse(methodResolveClaim, batching.BlockLatest, []interface{}{big.NewInt(123)}, nil)
err := game.CallResolveClaim(context.Background(), 123)
require.NoError(t, err)
}
func TestResolveClaimTx(t *testing.T) {
stubRpc, game := setup(t)
stubRpc.SetResponse(methodResolveClaim, batching.BlockLatest, []interface{}{big.NewInt(123)}, nil)
tx, err := game.ResolveClaimTx(123)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
}
func TestResolveTx(t *testing.T) {
stubRpc, game := setup(t)
stubRpc.SetResponse(methodResolve, batching.BlockLatest, nil, nil)
tx, err := game.ResolveTx()
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
}
func TestAttackTx(t *testing.T) {
stubRpc, game := setup(t)
value := common.Hash{0xaa}
stubRpc.SetResponse(methodAttack, batching.BlockLatest, []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 := setup(t)
value := common.Hash{0xaa}
stubRpc.SetResponse(methodDefend, batching.BlockLatest, []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 := setup(t)
stateData := []byte{1, 2, 3}
proofData := []byte{4, 5, 6, 7, 8, 9}
stubRpc.SetResponse(methodStep, batching.BlockLatest, []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, claim faultTypes.Claim) { func expectGetClaim(stubRpc *batchingTest.AbiBasedRpc, claim faultTypes.Claim) {
stubRpc.SetResponse( stubRpc.SetResponse(
methodClaim, methodClaim,
......
...@@ -89,7 +89,7 @@ func NewGamePlayer( ...@@ -89,7 +89,7 @@ func NewGamePlayer(
return nil, fmt.Errorf("failed to validate absolute prestate: %w", err) return nil, fmt.Errorf("failed to validate absolute prestate: %w", err)
} }
responder, err := responder.NewFaultResponder(logger, txMgr, addr) responder, err := responder.NewFaultResponder(logger, txMgr, loader)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create the responder: %w", err) return nil, fmt.Errorf("failed to create the responder: %w", err)
} }
......
package responder
import (
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
)
// setupFaultDisputeGame deploys the FaultDisputeGame contract to a simulated backend
func setupFaultDisputeGame() (common.Address, *bind.TransactOpts, *backends.SimulatedBackend, *bindings.FaultDisputeGame, error) {
privateKey, err := crypto.GenerateKey()
from := crypto.PubkeyToAddress(privateKey.PublicKey)
if err != nil {
return common.Address{}, nil, nil, nil, err
}
opts, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(1337))
if err != nil {
return common.Address{}, nil, nil, nil, err
}
backend := backends.NewSimulatedBackend(
core.GenesisAlloc{from: {Balance: big.NewInt(params.Ether)}},
50_000_000,
)
blockHashOracle, _, _, err := bindings.DeployBlockOracle(opts, backend)
if err != nil {
return common.Address{}, nil, nil, nil, err
}
_, _, contract, err := bindings.DeployFaultDisputeGame(
opts,
backend,
uint8(0), // Game Type ID
[32]byte{0x01}, // Absolute Prestate Claim
big.NewInt(15), // Max Game Depth
uint64(604800), // 7 days
common.Address{0xdd}, // VM
common.Address{0xee}, // L2OutputOracle (Not used in Alphabet Game)
blockHashOracle, // Block hash oracle
)
if err != nil {
return common.Address{}, nil, nil, nil, err
}
return from, opts, backend, contract, nil
}
// TestBuildFaultDefendData ensures that the manual ABI packing is the same as going through the bound contract.
func TestBuildFaultDefendData(t *testing.T) {
_, opts, _, contract, err := setupFaultDisputeGame()
require.NoError(t, err)
resp, _ := newTestFaultResponder(t)
data, err := resp.buildFaultDefendData(1, [32]byte{0x02, 0x03})
require.NoError(t, err)
opts.GasLimit = 100_000
tx, err := contract.Defend(opts, big.NewInt(1), [32]byte{0x02, 0x03})
require.NoError(t, err)
require.Equal(t, data, tx.Data())
}
// TestBuildFaultAttackData ensures that the manual ABI packing is the same as going through the bound contract.
func TestBuildFaultAttackData(t *testing.T) {
_, opts, _, contract, err := setupFaultDisputeGame()
require.NoError(t, err)
resp, _ := newTestFaultResponder(t)
data, err := resp.buildFaultAttackData(1, [32]byte{0x02, 0x03})
require.NoError(t, err)
opts.GasLimit = 100_000
tx, err := contract.Attack(opts, big.NewInt(1), [32]byte{0x02, 0x03})
require.NoError(t, err)
require.Equal(t, data, tx.Data())
}
// TestBuildFaultStepData ensures that the manual ABI packing is the same as going through the bound contract.
func TestBuildFaultStepData(t *testing.T) {
_, opts, _, contract, err := setupFaultDisputeGame()
require.NoError(t, err)
resp, _ := newTestFaultResponder(t)
data, err := resp.buildStepTxData(2, false, []byte{0x01}, []byte{0x02})
require.NoError(t, err)
opts.GasLimit = 100_000
tx, err := contract.Step(opts, big.NewInt(2), false, []byte{0x01}, []byte{0x02})
require.NoError(t, err)
require.Equal(t, data, tx.Data())
}
...@@ -2,153 +2,97 @@ package responder ...@@ -2,153 +2,97 @@ package responder
import ( import (
"context" "context"
"math/big"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
type GameContract interface {
CallResolve(ctx context.Context) (gameTypes.GameStatus, error)
ResolveTx() (txmgr.TxCandidate, error)
CallResolveClaim(ctx context.Context, claimIdx uint64) error
ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, 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)
}
// FaultResponder implements the [Responder] interface to send onchain transactions. // FaultResponder implements the [Responder] interface to send onchain transactions.
type FaultResponder struct { type FaultResponder struct {
log log.Logger log log.Logger
txMgr txmgr.TxManager txMgr txmgr.TxManager
contract GameContract
fdgAddr common.Address
fdgAbi *abi.ABI
} }
// NewFaultResponder returns a new [FaultResponder]. // NewFaultResponder returns a new [FaultResponder].
func NewFaultResponder(logger log.Logger, txManagr txmgr.TxManager, fdgAddr common.Address) (*FaultResponder, error) { func NewFaultResponder(logger log.Logger, txMgr txmgr.TxManager, contract GameContract) (*FaultResponder, error) {
fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi()
if err != nil {
return nil, err
}
return &FaultResponder{ return &FaultResponder{
log: logger, log: logger,
txMgr: txManagr, txMgr: txMgr,
fdgAddr: fdgAddr, contract: contract,
fdgAbi: fdgAbi,
}, nil }, nil
} }
// buildFaultDefendData creates the transaction data for the Defend function.
func (r *FaultResponder) buildFaultDefendData(parentContractIndex int, pivot [32]byte) ([]byte, error) {
return r.fdgAbi.Pack(
"defend",
big.NewInt(int64(parentContractIndex)),
pivot,
)
}
// buildFaultAttackData creates the transaction data for the Attack function.
func (r *FaultResponder) buildFaultAttackData(parentContractIndex int, pivot [32]byte) ([]byte, error) {
return r.fdgAbi.Pack(
"attack",
big.NewInt(int64(parentContractIndex)),
pivot,
)
}
// buildResolveData creates the transaction data for the Resolve function.
func (r *FaultResponder) buildResolveData() ([]byte, error) {
return r.fdgAbi.Pack("resolve")
}
// CallResolve determines if the resolve function on the fault dispute game contract // CallResolve determines if the resolve function on the fault dispute game contract
// would succeed. Returns the game status if the call would succeed, errors otherwise. // would succeed. Returns the game status if the call would succeed, errors otherwise.
func (r *FaultResponder) CallResolve(ctx context.Context) (gameTypes.GameStatus, error) { func (r *FaultResponder) CallResolve(ctx context.Context) (gameTypes.GameStatus, error) {
txData, err := r.buildResolveData() return r.contract.CallResolve(ctx)
if err != nil {
return gameTypes.GameStatusInProgress, err
}
res, err := r.txMgr.Call(ctx, ethereum.CallMsg{
To: &r.fdgAddr,
Data: txData,
}, nil)
if err != nil {
return gameTypes.GameStatusInProgress, err
}
var status uint8
if err = r.fdgAbi.UnpackIntoInterface(&status, "resolve", res); err != nil {
return gameTypes.GameStatusInProgress, err
}
return gameTypes.GameStatusFromUint8(status)
} }
// Resolve executes a resolve transaction to resolve a fault dispute game. // Resolve executes a resolve transaction to resolve a fault dispute game.
func (r *FaultResponder) Resolve(ctx context.Context) error { func (r *FaultResponder) Resolve(ctx context.Context) error {
txData, err := r.buildResolveData() candidate, err := r.contract.ResolveTx()
if err != nil { if err != nil {
return err return err
} }
return r.sendTxAndWait(ctx, txData) return r.sendTxAndWait(ctx, candidate)
}
// buildResolveClaimData creates the transaction data for the ResolveClaim function.
func (r *FaultResponder) buildResolveClaimData(claimIdx uint64) ([]byte, error) {
return r.fdgAbi.Pack("resolveClaim", big.NewInt(int64(claimIdx)))
} }
// CallResolveClaim determines if the resolveClaim function on the fault dispute game contract // CallResolveClaim determines if the resolveClaim function on the fault dispute game contract
// would succeed. // would succeed.
func (r *FaultResponder) CallResolveClaim(ctx context.Context, claimIdx uint64) error { func (r *FaultResponder) CallResolveClaim(ctx context.Context, claimIdx uint64) error {
txData, err := r.buildResolveClaimData(claimIdx) return r.contract.CallResolveClaim(ctx, claimIdx)
if err != nil {
return err
}
_, err = r.txMgr.Call(ctx, ethereum.CallMsg{
To: &r.fdgAddr,
Data: txData,
}, nil)
return err
} }
// ResolveClaim executes a resolveClaim transaction to resolve a fault dispute game. // ResolveClaim executes a resolveClaim transaction to resolve a fault dispute game.
func (r *FaultResponder) ResolveClaim(ctx context.Context, claimIdx uint64) error { func (r *FaultResponder) ResolveClaim(ctx context.Context, claimIdx uint64) error {
txData, err := r.buildResolveClaimData(claimIdx) candidate, err := r.contract.ResolveClaimTx(claimIdx)
if err != nil { if err != nil {
return err return err
} }
return r.sendTxAndWait(ctx, txData) return r.sendTxAndWait(ctx, candidate)
} }
func (r *FaultResponder) PerformAction(ctx context.Context, action types.Action) error { func (r *FaultResponder) PerformAction(ctx context.Context, action types.Action) error {
var txData []byte var candidate txmgr.TxCandidate
var err error var err error
switch action.Type { switch action.Type {
case types.ActionTypeMove: case types.ActionTypeMove:
if action.IsAttack { if action.IsAttack {
txData, err = r.buildFaultAttackData(action.ParentIdx, action.Value) candidate, err = r.contract.AttackTx(uint64(action.ParentIdx), action.Value)
} else { } else {
txData, err = r.buildFaultDefendData(action.ParentIdx, action.Value) candidate, err = r.contract.DefendTx(uint64(action.ParentIdx), action.Value)
} }
case types.ActionTypeStep: case types.ActionTypeStep:
txData, err = r.buildStepTxData(uint64(action.ParentIdx), action.IsAttack, action.PreState, action.ProofData) candidate, err = r.contract.StepTx(uint64(action.ParentIdx), action.IsAttack, action.PreState, action.ProofData)
} }
if err != nil { if err != nil {
return err return err
} }
return r.sendTxAndWait(ctx, txData) return r.sendTxAndWait(ctx, candidate)
} }
// sendTxAndWait sends a transaction through the [txmgr] and waits for a receipt. // sendTxAndWait sends a transaction through the [txmgr] and waits for a receipt.
// This sets the tx GasLimit to 0, performing gas estimation online through the [txmgr]. // This sets the tx GasLimit to 0, performing gas estimation online through the [txmgr].
func (r *FaultResponder) sendTxAndWait(ctx context.Context, txData []byte) error { func (r *FaultResponder) sendTxAndWait(ctx context.Context, candidate txmgr.TxCandidate) error {
receipt, err := r.txMgr.Send(ctx, txmgr.TxCandidate{ receipt, err := r.txMgr.Send(ctx, candidate)
To: &r.fdgAddr,
TxData: txData,
GasLimit: 0,
})
if err != nil { if err != nil {
return err return err
} }
...@@ -159,14 +103,3 @@ func (r *FaultResponder) sendTxAndWait(ctx context.Context, txData []byte) error ...@@ -159,14 +103,3 @@ func (r *FaultResponder) sendTxAndWait(ctx context.Context, txData []byte) error
} }
return nil return nil
} }
// buildStepTxData creates the transaction data for the step function.
func (r *FaultResponder) buildStepTxData(claimIdx uint64, isAttack bool, stateData []byte, proof []byte) ([]byte, error) {
return r.fdgAbi.Pack(
"step",
big.NewInt(int64(claimIdx)),
isAttack,
stateData,
proof,
)
}
...@@ -3,16 +3,13 @@ package responder ...@@ -3,16 +3,13 @@ package responder
import ( import (
"context" "context"
"errors" "errors"
"math/big"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -21,44 +18,34 @@ import ( ...@@ -21,44 +18,34 @@ import (
) )
var ( var (
mockFdgAddress = common.HexToAddress("0x1234") mockSendError = errors.New("mock send error")
mockSendError = errors.New("mock send error") mockCallError = errors.New("mock call error")
mockCallError = errors.New("mock call error")
) )
// TestCallResolve tests the [Responder.CallResolve]. // TestCallResolve tests the [Responder.CallResolve].
func TestCallResolve(t *testing.T) { func TestCallResolve(t *testing.T) {
t.Run("SendFails", func(t *testing.T) { t.Run("SendFails", func(t *testing.T) {
responder, mockTxMgr := newTestFaultResponder(t) responder, _, contract := newTestFaultResponder(t)
mockTxMgr.callFails = true contract.callFails = true
status, err := responder.CallResolve(context.Background()) status, err := responder.CallResolve(context.Background())
require.ErrorIs(t, err, mockCallError) require.ErrorIs(t, err, mockCallError)
require.Equal(t, gameTypes.GameStatusInProgress, status) require.Equal(t, gameTypes.GameStatusInProgress, status)
require.Equal(t, 0, mockTxMgr.calls) require.Equal(t, 0, contract.calls)
})
t.Run("UnpackFails", func(t *testing.T) {
responder, mockTxMgr := newTestFaultResponder(t)
mockTxMgr.callBytes = []byte{0x00, 0x01}
status, err := responder.CallResolve(context.Background())
require.Error(t, err)
require.Equal(t, gameTypes.GameStatusInProgress, status)
require.Equal(t, 1, mockTxMgr.calls)
}) })
t.Run("Success", func(t *testing.T) { t.Run("Success", func(t *testing.T) {
responder, mockTxMgr := newTestFaultResponder(t) responder, _, contract := newTestFaultResponder(t)
status, err := responder.CallResolve(context.Background()) status, err := responder.CallResolve(context.Background())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, gameTypes.GameStatusInProgress, status) require.Equal(t, gameTypes.GameStatusInProgress, status)
require.Equal(t, 1, mockTxMgr.calls) require.Equal(t, 1, contract.calls)
}) })
} }
// TestResolve tests the [Responder.Resolve] method. // TestResolve tests the [Responder.Resolve] method.
func TestResolve(t *testing.T) { func TestResolve(t *testing.T) {
t.Run("SendFails", func(t *testing.T) { t.Run("SendFails", func(t *testing.T) {
responder, mockTxMgr := newTestFaultResponder(t) responder, mockTxMgr, _ := newTestFaultResponder(t)
mockTxMgr.sendFails = true mockTxMgr.sendFails = true
err := responder.Resolve(context.Background()) err := responder.Resolve(context.Background())
require.ErrorIs(t, err, mockSendError) require.ErrorIs(t, err, mockSendError)
...@@ -66,7 +53,7 @@ func TestResolve(t *testing.T) { ...@@ -66,7 +53,7 @@ func TestResolve(t *testing.T) {
}) })
t.Run("Success", func(t *testing.T) { t.Run("Success", func(t *testing.T) {
responder, mockTxMgr := newTestFaultResponder(t) responder, mockTxMgr, _ := newTestFaultResponder(t)
err := responder.Resolve(context.Background()) err := responder.Resolve(context.Background())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, mockTxMgr.sends) require.Equal(t, 1, mockTxMgr.sends)
...@@ -75,24 +62,24 @@ func TestResolve(t *testing.T) { ...@@ -75,24 +62,24 @@ func TestResolve(t *testing.T) {
func TestCallResolveClaim(t *testing.T) { func TestCallResolveClaim(t *testing.T) {
t.Run("SendFails", func(t *testing.T) { t.Run("SendFails", func(t *testing.T) {
responder, mockTxMgr := newTestFaultResponder(t) responder, _, contract := newTestFaultResponder(t)
mockTxMgr.callFails = true contract.callFails = true
err := responder.CallResolveClaim(context.Background(), 0) err := responder.CallResolveClaim(context.Background(), 0)
require.ErrorIs(t, err, mockCallError) require.ErrorIs(t, err, mockCallError)
require.Equal(t, 0, mockTxMgr.calls) require.Equal(t, 0, contract.calls)
}) })
t.Run("Success", func(t *testing.T) { t.Run("Success", func(t *testing.T) {
responder, mockTxMgr := newTestFaultResponder(t) responder, _, contract := newTestFaultResponder(t)
err := responder.CallResolveClaim(context.Background(), 0) err := responder.CallResolveClaim(context.Background(), 0)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, mockTxMgr.calls) require.Equal(t, 1, contract.calls)
}) })
} }
func TestResolveClaim(t *testing.T) { func TestResolveClaim(t *testing.T) {
t.Run("SendFails", func(t *testing.T) { t.Run("SendFails", func(t *testing.T) {
responder, mockTxMgr := newTestFaultResponder(t) responder, mockTxMgr, _ := newTestFaultResponder(t)
mockTxMgr.sendFails = true mockTxMgr.sendFails = true
err := responder.ResolveClaim(context.Background(), 0) err := responder.ResolveClaim(context.Background(), 0)
require.ErrorIs(t, err, mockSendError) require.ErrorIs(t, err, mockSendError)
...@@ -100,7 +87,7 @@ func TestResolveClaim(t *testing.T) { ...@@ -100,7 +87,7 @@ func TestResolveClaim(t *testing.T) {
}) })
t.Run("Success", func(t *testing.T) { t.Run("Success", func(t *testing.T) {
responder, mockTxMgr := newTestFaultResponder(t) responder, mockTxMgr, _ := newTestFaultResponder(t)
err := responder.ResolveClaim(context.Background(), 0) err := responder.ResolveClaim(context.Background(), 0)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, mockTxMgr.sends) require.Equal(t, 1, mockTxMgr.sends)
...@@ -110,7 +97,7 @@ func TestResolveClaim(t *testing.T) { ...@@ -110,7 +97,7 @@ func TestResolveClaim(t *testing.T) {
// TestRespond tests the [Responder.Respond] method. // TestRespond tests the [Responder.Respond] method.
func TestPerformAction(t *testing.T) { func TestPerformAction(t *testing.T) {
t.Run("send fails", func(t *testing.T) { t.Run("send fails", func(t *testing.T) {
responder, mockTxMgr := newTestFaultResponder(t) responder, mockTxMgr, _ := newTestFaultResponder(t)
mockTxMgr.sendFails = true mockTxMgr.sendFails = true
err := responder.PerformAction(context.Background(), types.Action{ err := responder.PerformAction(context.Background(), types.Action{
Type: types.ActionTypeMove, Type: types.ActionTypeMove,
...@@ -123,7 +110,7 @@ func TestPerformAction(t *testing.T) { ...@@ -123,7 +110,7 @@ func TestPerformAction(t *testing.T) {
}) })
t.Run("sends response", func(t *testing.T) { t.Run("sends response", func(t *testing.T) {
responder, mockTxMgr := newTestFaultResponder(t) responder, mockTxMgr, _ := newTestFaultResponder(t)
err := responder.PerformAction(context.Background(), types.Action{ err := responder.PerformAction(context.Background(), types.Action{
Type: types.ActionTypeMove, Type: types.ActionTypeMove,
ParentIdx: 123, ParentIdx: 123,
...@@ -135,7 +122,7 @@ func TestPerformAction(t *testing.T) { ...@@ -135,7 +122,7 @@ func TestPerformAction(t *testing.T) {
}) })
t.Run("attack", func(t *testing.T) { t.Run("attack", func(t *testing.T) {
responder, mockTxMgr := newTestFaultResponder(t) responder, mockTxMgr, contract := newTestFaultResponder(t)
action := types.Action{ action := types.Action{
Type: types.ActionTypeMove, Type: types.ActionTypeMove,
ParentIdx: 123, ParentIdx: 123,
...@@ -145,18 +132,13 @@ func TestPerformAction(t *testing.T) { ...@@ -145,18 +132,13 @@ func TestPerformAction(t *testing.T) {
err := responder.PerformAction(context.Background(), action) err := responder.PerformAction(context.Background(), action)
require.NoError(t, err) require.NoError(t, err)
// Pack the tx data manually.
fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi()
require.NoError(t, err)
expected, err := fdgAbi.Pack("attack", big.NewInt(int64(action.ParentIdx)), action.Value)
require.NoError(t, err)
require.Len(t, mockTxMgr.sent, 1) require.Len(t, mockTxMgr.sent, 1)
require.Equal(t, expected, mockTxMgr.sent[0].TxData) require.EqualValues(t, []interface{}{uint64(action.ParentIdx), action.Value}, contract.attackArgs)
require.Equal(t, ([]byte)("attack"), mockTxMgr.sent[0].TxData)
}) })
t.Run("defend", func(t *testing.T) { t.Run("defend", func(t *testing.T) {
responder, mockTxMgr := newTestFaultResponder(t) responder, mockTxMgr, contract := newTestFaultResponder(t)
action := types.Action{ action := types.Action{
Type: types.ActionTypeMove, Type: types.ActionTypeMove,
ParentIdx: 123, ParentIdx: 123,
...@@ -166,18 +148,13 @@ func TestPerformAction(t *testing.T) { ...@@ -166,18 +148,13 @@ func TestPerformAction(t *testing.T) {
err := responder.PerformAction(context.Background(), action) err := responder.PerformAction(context.Background(), action)
require.NoError(t, err) require.NoError(t, err)
// Pack the tx data manually.
fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi()
require.NoError(t, err)
expected, err := fdgAbi.Pack("defend", big.NewInt(int64(action.ParentIdx)), action.Value)
require.NoError(t, err)
require.Len(t, mockTxMgr.sent, 1) require.Len(t, mockTxMgr.sent, 1)
require.Equal(t, expected, mockTxMgr.sent[0].TxData) require.EqualValues(t, []interface{}{uint64(action.ParentIdx), action.Value}, contract.defendArgs)
require.Equal(t, ([]byte)("defend"), mockTxMgr.sent[0].TxData)
}) })
t.Run("step", func(t *testing.T) { t.Run("step", func(t *testing.T) {
responder, mockTxMgr := newTestFaultResponder(t) responder, mockTxMgr, contract := newTestFaultResponder(t)
action := types.Action{ action := types.Action{
Type: types.ActionTypeStep, Type: types.ActionTypeStep,
ParentIdx: 123, ParentIdx: 123,
...@@ -188,36 +165,29 @@ func TestPerformAction(t *testing.T) { ...@@ -188,36 +165,29 @@ func TestPerformAction(t *testing.T) {
err := responder.PerformAction(context.Background(), action) err := responder.PerformAction(context.Background(), action)
require.NoError(t, err) require.NoError(t, err)
// Pack the tx data manually.
fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi()
require.NoError(t, err)
expected, err := fdgAbi.Pack("step", big.NewInt(int64(action.ParentIdx)), true, action.PreState, action.ProofData)
require.NoError(t, err)
require.Len(t, mockTxMgr.sent, 1) require.Len(t, mockTxMgr.sent, 1)
require.Equal(t, expected, mockTxMgr.sent[0].TxData) require.EqualValues(t, []interface{}{uint64(action.ParentIdx), action.IsAttack, action.PreState, action.ProofData}, contract.stepArgs)
require.Equal(t, ([]byte)("step"), mockTxMgr.sent[0].TxData)
}) })
} }
func newTestFaultResponder(t *testing.T) (*FaultResponder, *mockTxManager) { func newTestFaultResponder(t *testing.T) (*FaultResponder, *mockTxManager, *mockContract) {
log := testlog.Logger(t, log.LvlError) log := testlog.Logger(t, log.LvlError)
mockTxMgr := &mockTxManager{} mockTxMgr := &mockTxManager{}
responder, err := NewFaultResponder(log, mockTxMgr, mockFdgAddress) contract := &mockContract{}
responder, err := NewFaultResponder(log, mockTxMgr, contract)
require.NoError(t, err) require.NoError(t, err)
return responder, mockTxMgr return responder, mockTxMgr, contract
} }
type mockTxManager struct { type mockTxManager struct {
from common.Address from common.Address
sends int sends int
sent []txmgr.TxCandidate sent []txmgr.TxCandidate
calls int
sendFails bool sendFails bool
callFails bool
callBytes []byte
} }
func (m *mockTxManager) Send(ctx context.Context, candidate txmgr.TxCandidate) (*ethtypes.Receipt, error) { func (m *mockTxManager) Send(_ context.Context, candidate txmgr.TxCandidate) (*ethtypes.Receipt, error) {
if m.sendFails { if m.sendFails {
return nil, mockSendError return nil, mockSendError
} }
...@@ -230,23 +200,57 @@ func (m *mockTxManager) Send(ctx context.Context, candidate txmgr.TxCandidate) ( ...@@ -230,23 +200,57 @@ func (m *mockTxManager) Send(ctx context.Context, candidate txmgr.TxCandidate) (
), nil ), nil
} }
func (m *mockTxManager) Call(_ context.Context, _ ethereum.CallMsg, _ *big.Int) ([]byte, error) { func (m *mockTxManager) BlockNumber(_ context.Context) (uint64, error) {
panic("not implemented")
}
func (m *mockTxManager) From() common.Address {
return m.from
}
type mockContract struct {
calls int
callFails bool
attackArgs []interface{}
defendArgs []interface{}
stepArgs []interface{}
}
func (m *mockContract) CallResolve(_ context.Context) (gameTypes.GameStatus, error) {
if m.callFails { if m.callFails {
return nil, mockCallError return gameTypes.GameStatusInProgress, mockCallError
} }
m.calls++ m.calls++
if m.callBytes != nil { return gameTypes.GameStatusInProgress, nil
return m.callBytes, nil }
func (m *mockContract) ResolveTx() (txmgr.TxCandidate, error) {
return txmgr.TxCandidate{}, nil
}
func (m *mockContract) CallResolveClaim(_ context.Context, _ uint64) error {
if m.callFails {
return mockCallError
} }
return common.Hex2Bytes( m.calls++
"0000000000000000000000000000000000000000000000000000000000000000", return nil
), nil
} }
func (m *mockTxManager) BlockNumber(ctx context.Context) (uint64, error) { func (m *mockContract) ResolveClaimTx(_ uint64) (txmgr.TxCandidate, error) {
panic("not implemented") return txmgr.TxCandidate{}, nil
} }
func (m *mockTxManager) From() common.Address { func (m *mockContract) AttackTx(parentClaimId uint64, claim common.Hash) (txmgr.TxCandidate, error) {
return m.from m.attackArgs = []interface{}{parentClaimId, claim}
return txmgr.TxCandidate{TxData: ([]byte)("attack")}, nil
}
func (m *mockContract) DefendTx(parentClaimId uint64, claim common.Hash) (txmgr.TxCandidate, error) {
m.defendArgs = []interface{}{parentClaimId, claim}
return txmgr.TxCandidate{TxData: ([]byte)("defend")}, nil
}
func (m *mockContract) StepTx(claimIdx uint64, isAttack bool, stateData []byte, proofData []byte) (txmgr.TxCandidate, error) {
m.stepArgs = []interface{}{claimIdx, isAttack, stateData, proofData}
return txmgr.TxCandidate{TxData: ([]byte)("step")}, nil
} }
...@@ -3,7 +3,6 @@ package cannon ...@@ -3,7 +3,6 @@ package cannon
import ( import (
"context" "context"
"errors" "errors"
"math/big"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
...@@ -11,7 +10,6 @@ import ( ...@@ -11,7 +10,6 @@ import (
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -47,10 +45,6 @@ func (m *mockTxManager) Send(ctx context.Context, candidate txmgr.TxCandidate) ( ...@@ -47,10 +45,6 @@ func (m *mockTxManager) Send(ctx context.Context, candidate txmgr.TxCandidate) (
), nil ), nil
} }
func (m *mockTxManager) Call(_ context.Context, _ ethereum.CallMsg, _ *big.Int) ([]byte, error) {
panic("not implemented")
}
func (m *mockTxManager) BlockNumber(ctx context.Context) (uint64, error) { func (m *mockTxManager) BlockNumber(ctx context.Context) (uint64, error) {
panic("not implemented") panic("not implemented")
} }
......
...@@ -48,9 +48,6 @@ type fakeTxMgr struct { ...@@ -48,9 +48,6 @@ type fakeTxMgr struct {
func (f fakeTxMgr) From() common.Address { func (f fakeTxMgr) From() common.Address {
return f.from return f.from
} }
func (f fakeTxMgr) Call(_ context.Context, _ ethereum.CallMsg, _ *big.Int) ([]byte, error) {
panic("unimplemented")
}
func (f fakeTxMgr) BlockNumber(_ context.Context) (uint64, error) { func (f fakeTxMgr) BlockNumber(_ context.Context) (uint64, error) {
panic("unimplemented") panic("unimplemented")
} }
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -86,6 +87,17 @@ func toCallArg(msg ethereum.CallMsg) interface{} { ...@@ -86,6 +87,17 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
return arg return arg
} }
func (c *ContractCall) ToTxCandidate() (txmgr.TxCandidate, error) {
data, err := c.Pack()
if err != nil {
return txmgr.TxCandidate{}, fmt.Errorf("failed to pack arguments: %w", err)
}
return txmgr.TxCandidate{
TxData: data,
To: &c.Addr,
}, nil
}
type CallResult struct { type CallResult struct {
out []interface{} out []interface{}
} }
......
...@@ -30,6 +30,22 @@ func TestContractCall_ToCallArgs(t *testing.T) { ...@@ -30,6 +30,22 @@ func TestContractCall_ToCallArgs(t *testing.T) {
require.NotContains(t, argMap, "gasPrice") require.NotContains(t, argMap, "gasPrice")
} }
func TestContractCall_ToTxCandidate(t *testing.T) {
addr := common.Address{0xbd}
testAbi, err := bindings.ERC20MetaData.GetAbi()
require.NoError(t, err)
call := NewContractCall(testAbi, addr, "approve", common.Address{0xcc}, big.NewInt(1234444))
candidate, err := call.ToTxCandidate()
require.NoError(t, err)
require.Equal(t, candidate.To, &addr)
expectedData, err := call.Pack()
require.NoError(t, err)
require.Equal(t, candidate.TxData, expectedData)
require.Nil(t, candidate.Value)
require.Zero(t, candidate.GasLimit)
}
func TestContractCall_Pack(t *testing.T) { func TestContractCall_Pack(t *testing.T) {
addr := common.Address{0xbd} addr := common.Address{0xbd}
testAbi, err := bindings.ERC20MetaData.GetAbi() testAbi, err := bindings.ERC20MetaData.GetAbi()
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-service/sources/batching" "github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
...@@ -73,6 +74,11 @@ func (l *AbiBasedRpc) BatchCallContext(ctx context.Context, b []rpc.BatchElem) e ...@@ -73,6 +74,11 @@ func (l *AbiBasedRpc) BatchCallContext(ctx context.Context, b []rpc.BatchElem) e
return errors.Join(errs...) return errors.Join(errs...)
} }
func (l *AbiBasedRpc) VerifyTxCandidate(candidate txmgr.TxCandidate) {
require.EqualValues(l.t, &l.addr, candidate.To, "Incorrect To address")
l.findExpectedCall(candidate.TxData, batching.BlockLatest.ArgValue())
}
func (l *AbiBasedRpc) CallContext(_ context.Context, out interface{}, method string, args ...interface{}) error { func (l *AbiBasedRpc) CallContext(_ context.Context, out interface{}, method string, args ...interface{}) error {
require.Equal(l.t, "eth_call", method) require.Equal(l.t, "eth_call", method)
require.Len(l.t, args, 2) require.Len(l.t, args, 2)
...@@ -82,11 +88,27 @@ func (l *AbiBasedRpc) CallContext(_ context.Context, out interface{}, method str ...@@ -82,11 +88,27 @@ func (l *AbiBasedRpc) CallContext(_ context.Context, out interface{}, method str
require.Equal(l.t, &l.addr, callOpts["to"]) require.Equal(l.t, &l.addr, callOpts["to"])
data, ok := callOpts["input"].(hexutil.Bytes) data, ok := callOpts["input"].(hexutil.Bytes)
require.True(l.t, ok) require.True(l.t, ok)
call, abiMethod := l.findExpectedCall(data, actualBlockRef)
output, err := abiMethod.Outputs.Pack(call.outputs...)
require.NoErrorf(l.t, err, "Invalid outputs for method %v: %v", abiMethod.Name, call.outputs)
// I admit I do not understand Go reflection.
// So leverage json.Unmarshal to set the out value correctly.
j, err := json.Marshal(hexutil.Bytes(output))
require.NoError(l.t, err)
require.NoError(l.t, json.Unmarshal(j, out))
return nil
}
func (l *AbiBasedRpc) findExpectedCall(data []byte, actualBlockRef interface{}) (*expectedCall, *abi.Method) {
abiMethod, err := l.abi.MethodById(data[0:4]) abiMethod, err := l.abi.MethodById(data[0:4])
require.NoError(l.t, err) require.NoError(l.t, err)
argData := data[4:] argData := data[4:]
args, err = abiMethod.Inputs.Unpack(argData) args, err := abiMethod.Inputs.Unpack(argData)
require.NoError(l.t, err) require.NoError(l.t, err)
require.Len(l.t, args, len(abiMethod.Inputs)) require.Len(l.t, args, len(abiMethod.Inputs))
...@@ -100,14 +122,5 @@ func (l *AbiBasedRpc) CallContext(_ context.Context, out interface{}, method str ...@@ -100,14 +122,5 @@ func (l *AbiBasedRpc) CallContext(_ context.Context, out interface{}, method str
} }
} }
require.NotNilf(l.t, call, "No expected calls to %v at block %v with arguments: %v\nExpected calls: %v", abiMethod.Name, actualBlockRef, args, expectedCalls) require.NotNilf(l.t, call, "No expected calls to %v at block %v with arguments: %v\nExpected calls: %v", abiMethod.Name, actualBlockRef, args, expectedCalls)
return call, abiMethod
output, err := abiMethod.Outputs.Pack(call.outputs...)
require.NoErrorf(l.t, err, "Invalid outputs for method %v: %v", abiMethod.Name, call.outputs)
// I admit I do not understand Go reflection.
// So leverage json.Unmarshal to set the out value correctly.
j, err := json.Marshal(hexutil.Bytes(output))
require.NoError(l.t, err)
require.NoError(l.t, json.Unmarshal(j, out))
return nil
} }
...@@ -4,12 +4,9 @@ package mocks ...@@ -4,12 +4,9 @@ package mocks
import ( import (
context "context" context "context"
big "math/big"
common "github.com/ethereum/go-ethereum/common" common "github.com/ethereum/go-ethereum/common"
ethereum "github.com/ethereum/go-ethereum"
mock "github.com/stretchr/testify/mock" mock "github.com/stretchr/testify/mock"
txmgr "github.com/ethereum-optimism/optimism/op-service/txmgr" txmgr "github.com/ethereum-optimism/optimism/op-service/txmgr"
...@@ -46,32 +43,6 @@ func (_m *TxManager) BlockNumber(ctx context.Context) (uint64, error) { ...@@ -46,32 +43,6 @@ func (_m *TxManager) BlockNumber(ctx context.Context) (uint64, error) {
return r0, r1 return r0, r1
} }
// Call provides a mock function with given fields: ctx, msg, blockNumber
func (_m *TxManager) Call(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
ret := _m.Called(ctx, msg, blockNumber)
var r0 []byte
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, ethereum.CallMsg, *big.Int) ([]byte, error)); ok {
return rf(ctx, msg, blockNumber)
}
if rf, ok := ret.Get(0).(func(context.Context, ethereum.CallMsg, *big.Int) []byte); ok {
r0 = rf(ctx, msg, blockNumber)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]byte)
}
}
if rf, ok := ret.Get(1).(func(context.Context, ethereum.CallMsg, *big.Int) error); ok {
r1 = rf(ctx, msg, blockNumber)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// From provides a mock function with given fields: // From provides a mock function with given fields:
func (_m *TxManager) From() common.Address { func (_m *TxManager) From() common.Address {
ret := _m.Called() ret := _m.Called()
......
...@@ -43,10 +43,6 @@ type TxManager interface { ...@@ -43,10 +43,6 @@ type TxManager interface {
// NOTE: Send can be called concurrently, the nonce will be managed internally. // NOTE: Send can be called concurrently, the nonce will be managed internally.
Send(ctx context.Context, candidate TxCandidate) (*types.Receipt, error) Send(ctx context.Context, candidate TxCandidate) (*types.Receipt, error)
// Call is used to call a contract.
// Internally, it uses the [ethclient.Client.CallContract] method.
Call(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
// From returns the sending address associated with the instance of the transaction manager. // From returns the sending address associated with the instance of the transaction manager.
// It is static for a single instance of a TxManager. // It is static for a single instance of a TxManager.
From() common.Address From() common.Address
...@@ -169,12 +165,6 @@ func (m *SimpleTxManager) Send(ctx context.Context, candidate TxCandidate) (*typ ...@@ -169,12 +165,6 @@ func (m *SimpleTxManager) Send(ctx context.Context, candidate TxCandidate) (*typ
return receipt, err return receipt, err
} }
// Call is used to call a contract.
// Internally, it uses the [ethclient.Client.CallContract] method.
func (m *SimpleTxManager) Call(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
return m.backend.CallContract(ctx, msg, blockNumber)
}
// send performs the actual transaction creation and sending. // send performs the actual transaction creation and sending.
func (m *SimpleTxManager) send(ctx context.Context, candidate TxCandidate) (*types.Receipt, error) { func (m *SimpleTxManager) send(ctx context.Context, candidate TxCandidate) (*types.Receipt, error) {
if m.cfg.TxSendTimeout != 0 { if m.cfg.TxSendTimeout != 0 {
......
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