Commit 16d65a68 authored by Adrian Sutton's avatar Adrian Sutton

Add more tests

parent 8c688bc1
...@@ -12,6 +12,15 @@ import ( ...@@ -12,6 +12,15 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
const (
methodGameDuration = "GAME_DURATION"
methodMaxGameDepth = "MAX_GAME_DEPTH"
methodAbsolutePrestate = "ABSOLUTE_PRESTATE"
methodStatus = "status"
methodClaimCount = "claimDataLen"
methodClaim = "claimData"
)
type FaultDisputeGameContract struct { type FaultDisputeGameContract struct {
multiCaller *batching.MultiCaller multiCaller *batching.MultiCaller
contract *batching.BoundContract contract *batching.BoundContract
...@@ -30,15 +39,15 @@ func NewFaultDisputeGameContract(addr common.Address, caller *batching.MultiCall ...@@ -30,15 +39,15 @@ func NewFaultDisputeGameContract(addr common.Address, caller *batching.MultiCall
} }
func (f *FaultDisputeGameContract) GetGameDuration(ctx context.Context) (uint64, error) { func (f *FaultDisputeGameContract) GetGameDuration(ctx context.Context) (uint64, error) {
result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call("GAME_DURATION")) result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call(methodGameDuration))
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to fetch game duration: %w", err) return 0, fmt.Errorf("failed to fetch game duration: %w", err)
} }
return result.GetBigInt(0).Uint64(), nil return result.GetUint64(0), nil
} }
func (f *FaultDisputeGameContract) GetMaxGameDepth(ctx context.Context) (uint64, error) { func (f *FaultDisputeGameContract) GetMaxGameDepth(ctx context.Context) (uint64, error) {
result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call("MAX_GAME_DEPTH")) result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call(methodMaxGameDepth))
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to fetch max game depth: %w", err) return 0, fmt.Errorf("failed to fetch max game depth: %w", err)
} }
...@@ -46,7 +55,7 @@ func (f *FaultDisputeGameContract) GetMaxGameDepth(ctx context.Context) (uint64, ...@@ -46,7 +55,7 @@ func (f *FaultDisputeGameContract) GetMaxGameDepth(ctx context.Context) (uint64,
} }
func (f *FaultDisputeGameContract) GetAbsolutePrestateHash(ctx context.Context) (common.Hash, error) { func (f *FaultDisputeGameContract) GetAbsolutePrestateHash(ctx context.Context) (common.Hash, error) {
result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call("ABSOLUTE_PRESTATE")) result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call(methodAbsolutePrestate))
if err != nil { if err != nil {
return common.Hash{}, fmt.Errorf("failed to fetch absolute prestate hash: %w", err) return common.Hash{}, fmt.Errorf("failed to fetch absolute prestate hash: %w", err)
} }
...@@ -54,7 +63,7 @@ func (f *FaultDisputeGameContract) GetAbsolutePrestateHash(ctx context.Context) ...@@ -54,7 +63,7 @@ func (f *FaultDisputeGameContract) GetAbsolutePrestateHash(ctx context.Context)
} }
func (f *FaultDisputeGameContract) GetStatus(ctx context.Context) (gameTypes.GameStatus, error) { func (f *FaultDisputeGameContract) GetStatus(ctx context.Context) (gameTypes.GameStatus, error) {
result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call("status")) result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call(methodStatus))
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to fetch status: %w", err) return 0, fmt.Errorf("failed to fetch status: %w", err)
} }
...@@ -62,7 +71,7 @@ func (f *FaultDisputeGameContract) GetStatus(ctx context.Context) (gameTypes.Gam ...@@ -62,7 +71,7 @@ func (f *FaultDisputeGameContract) GetStatus(ctx context.Context) (gameTypes.Gam
} }
func (f *FaultDisputeGameContract) GetClaimCount(ctx context.Context) (uint64, error) { func (f *FaultDisputeGameContract) GetClaimCount(ctx context.Context) (uint64, error) {
result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call("claimDataLen")) result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call(methodClaimCount))
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to fetch claim count: %w", err) return 0, fmt.Errorf("failed to fetch claim count: %w", err)
} }
...@@ -70,7 +79,7 @@ func (f *FaultDisputeGameContract) GetClaimCount(ctx context.Context) (uint64, e ...@@ -70,7 +79,7 @@ func (f *FaultDisputeGameContract) GetClaimCount(ctx context.Context) (uint64, e
} }
func (f *FaultDisputeGameContract) GetClaim(ctx context.Context, idx uint64) (types.Claim, error) { func (f *FaultDisputeGameContract) GetClaim(ctx context.Context, idx uint64) (types.Claim, error) {
result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call("claimData", new(big.Int).SetUint64(idx))) result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call(methodClaim, new(big.Int).SetUint64(idx)))
if err != nil { if err != nil {
return types.Claim{}, fmt.Errorf("failed to fetch claim %v: %w", idx, err) return types.Claim{}, fmt.Errorf("failed to fetch claim %v: %w", idx, err)
} }
...@@ -85,7 +94,7 @@ func (f *FaultDisputeGameContract) GetAllClaims(ctx context.Context) ([]types.Cl ...@@ -85,7 +94,7 @@ func (f *FaultDisputeGameContract) GetAllClaims(ctx context.Context) ([]types.Cl
calls := make([]*batching.ContractCall, count) calls := make([]*batching.ContractCall, count)
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
calls[i] = f.contract.Call("claimData", new(big.Int).SetUint64(i)) calls[i] = f.contract.Call(methodClaim, new(big.Int).SetUint64(i))
} }
results, err := f.multiCaller.CallLatest(ctx, calls...) results, err := f.multiCaller.CallLatest(ctx, calls...)
......
...@@ -2,7 +2,6 @@ package contracts ...@@ -2,7 +2,6 @@ package contracts
import ( import (
"context" "context"
"encoding/json"
"math/big" "math/big"
"testing" "testing"
...@@ -10,19 +9,71 @@ import ( ...@@ -10,19 +9,71 @@ import (
faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" "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/go-ethereum/accounts/abi" "github.com/ethereum-optimism/optimism/op-service/sources/batching/test"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestGetStatus(t *testing.T) { func TestSimpleGetters(t *testing.T) {
stubRpc, game := setup(t) tests := []struct {
stubRpc.SetResponse("status", nil, []interface{}{types.GameStatusChallengerWon}) method string
status, err := game.GetStatus(context.Background()) args []interface{}
require.NoError(t, err) result interface{}
require.Equal(t, types.GameStatusChallengerWon, status) expected interface{} // Defaults to expecting the same as result
call func(game *FaultDisputeGameContract) (any, error)
}{
{
method: methodStatus,
result: types.GameStatusChallengerWon,
call: func(game *FaultDisputeGameContract) (any, error) {
return game.GetStatus(context.Background())
},
},
{
method: methodGameDuration,
result: uint64(5566),
call: func(game *FaultDisputeGameContract) (any, error) {
return game.GetGameDuration(context.Background())
},
},
{
method: methodMaxGameDepth,
result: big.NewInt(128),
expected: uint64(128),
call: func(game *FaultDisputeGameContract) (any, error) {
return game.GetMaxGameDepth(context.Background())
},
},
{
method: methodAbsolutePrestate,
result: common.Hash{0xab},
call: func(game *FaultDisputeGameContract) (any, error) {
return game.GetAbsolutePrestateHash(context.Background())
},
},
{
method: methodClaimCount,
result: big.NewInt(9876),
expected: uint64(9876),
call: func(game *FaultDisputeGameContract) (any, error) {
return game.GetClaimCount(context.Background())
},
},
}
for _, test := range tests {
test := test
t.Run(test.method, func(t *testing.T) {
stubRpc, game := setup(t)
stubRpc.SetResponse(test.method, nil, []interface{}{test.result})
status, err := test.call(game)
require.NoError(t, err)
expected := test.expected
if expected == nil {
expected = test.result
}
require.Equal(t, expected, status)
})
}
} }
func TestGetClaim(t *testing.T) { func TestGetClaim(t *testing.T) {
...@@ -33,7 +84,7 @@ func TestGetClaim(t *testing.T) { ...@@ -33,7 +84,7 @@ func TestGetClaim(t *testing.T) {
value := common.Hash{0xab} value := common.Hash{0xab}
position := big.NewInt(2) position := big.NewInt(2)
clock := big.NewInt(1234) clock := big.NewInt(1234)
stubRpc.SetResponse("claimData", []interface{}{idx}, []interface{}{parentIndex, countered, value, position, clock}) stubRpc.SetResponse(methodClaim, []interface{}{idx}, []interface{}{parentIndex, countered, value, position, clock})
status, err := game.GetClaim(context.Background(), idx.Uint64()) status, err := game.GetClaim(context.Background(), idx.Uint64())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, faultTypes.Claim{ require.Equal(t, faultTypes.Claim{
...@@ -48,76 +99,14 @@ func TestGetClaim(t *testing.T) { ...@@ -48,76 +99,14 @@ func TestGetClaim(t *testing.T) {
}, status) }, status)
} }
type abiBasedRpc struct { func setup(t *testing.T) (*test.AbiBasedRpc, *FaultDisputeGameContract) {
t *testing.T
abi *abi.ABI
addr common.Address
expectedArgs map[string][]interface{}
outputs map[string][]interface{}
}
func setup(t *testing.T) (*abiBasedRpc, *FaultDisputeGameContract) {
fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi() fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi()
require.NoError(t, err) require.NoError(t, err)
address := common.HexToAddress("0x24112842371dFC380576ebb09Ae16Cb6B6caD7CB") address := common.HexToAddress("0x24112842371dFC380576ebb09Ae16Cb6B6caD7CB")
stubRpc := &abiBasedRpc{ stubRpc := test.NewAbiBasedRpc(t, fdgAbi, address)
t: t, caller := batching.NewMultiCaller(stubRpc, 100)
abi: fdgAbi,
addr: address,
expectedArgs: make(map[string][]interface{}),
outputs: make(map[string][]interface{}),
}
caller := batching.NewMultiCaller(stubRpc, 1)
game, err := NewFaultDisputeGameContract(address, caller) game, err := NewFaultDisputeGameContract(address, caller)
require.NoError(t, err) require.NoError(t, err)
return stubRpc, game return stubRpc, game
} }
func (l *abiBasedRpc) SetResponse(method string, expected []interface{}, output []interface{}) {
if expected == nil {
expected = []interface{}{}
}
if output == nil {
output = []interface{}{}
}
l.expectedArgs[method] = expected
l.outputs[method] = output
}
func (l *abiBasedRpc) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error {
panic("Not implemented")
}
func (l *abiBasedRpc) CallContext(ctx context.Context, out interface{}, method string, args ...interface{}) error {
require.Equal(l.t, "eth_call", method)
require.Len(l.t, args, 2)
require.Equal(l.t, "latest", args[1])
callOpts, ok := args[0].(map[string]any)
require.True(l.t, ok)
require.Equal(l.t, &l.addr, callOpts["to"])
data, ok := callOpts["data"].(hexutil.Bytes)
require.True(l.t, ok)
abiMethod, err := l.abi.MethodById(data[0:4])
require.NoError(l.t, err)
args, err = abiMethod.Inputs.Unpack(data[4:])
require.NoError(l.t, err)
require.Len(l.t, args, len(abiMethod.Inputs))
expectedArgs, ok := l.expectedArgs[abiMethod.Name]
require.True(l.t, ok)
require.EqualValues(l.t, expectedArgs, args, "Unexpected args")
outputs, ok := l.outputs[abiMethod.Name]
require.True(l.t, ok)
output, err := abiMethod.Outputs.Pack(outputs...)
require.NoError(l.t, err)
// 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
}
...@@ -74,6 +74,10 @@ func (c *CallResult) GetUint32(i int) uint32 { ...@@ -74,6 +74,10 @@ func (c *CallResult) GetUint32(i int) uint32 {
return *abi.ConvertType(c.out[i], new(uint32)).(*uint32) return *abi.ConvertType(c.out[i], new(uint32)).(*uint32)
} }
func (c *CallResult) GetUint64(i int) uint64 {
return *abi.ConvertType(c.out[i], new(uint64)).(*uint64)
}
func (c *CallResult) GetBool(i int) bool { func (c *CallResult) GetBool(i int) bool {
return *abi.ConvertType(c.out[i], new(bool)).(*bool) return *abi.ConvertType(c.out[i], new(bool)).(*bool)
} }
......
package test
import (
"context"
"encoding/json"
"testing"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
)
type AbiBasedRpc struct {
t *testing.T
abi *abi.ABI
addr common.Address
expectedArgs map[string][]interface{}
outputs map[string][]interface{}
}
func NewAbiBasedRpc(t *testing.T, contractAbi *abi.ABI, addr common.Address) *AbiBasedRpc {
return &AbiBasedRpc{
t: t,
abi: contractAbi,
addr: addr,
expectedArgs: make(map[string][]interface{}),
outputs: make(map[string][]interface{}),
}
}
func (l *AbiBasedRpc) SetResponse(method string, expected []interface{}, output []interface{}) {
if expected == nil {
expected = []interface{}{}
}
if output == nil {
output = []interface{}{}
}
l.expectedArgs[method] = expected
l.outputs[method] = output
}
func (l *AbiBasedRpc) BatchCallContext(_ context.Context, b []rpc.BatchElem) error {
panic("Not implemented")
}
func (l *AbiBasedRpc) CallContext(_ context.Context, out interface{}, method string, args ...interface{}) error {
require.Equal(l.t, "eth_call", method)
require.Len(l.t, args, 2)
require.Equal(l.t, "latest", args[1])
callOpts, ok := args[0].(map[string]any)
require.True(l.t, ok)
require.Equal(l.t, &l.addr, callOpts["to"])
data, ok := callOpts["data"].(hexutil.Bytes)
require.True(l.t, ok)
abiMethod, err := l.abi.MethodById(data[0:4])
require.NoError(l.t, err)
args, err = abiMethod.Inputs.Unpack(data[4:])
require.NoError(l.t, err)
require.Len(l.t, args, len(abiMethod.Inputs))
expectedArgs, ok := l.expectedArgs[abiMethod.Name]
require.Truef(l.t, ok, "Unexpected call to %v", abiMethod.Name)
require.EqualValues(l.t, expectedArgs, args, "Unexpected args")
outputs, ok := l.outputs[abiMethod.Name]
require.True(l.t, ok)
output, err := abiMethod.Outputs.Pack(outputs...)
require.NoError(l.t, err)
// 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
}
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