Commit 888e8c1c authored by Adrian Sutton's avatar Adrian Sutton

op-challenger: Add global data to preimage contract

Use new contract bindings to generate transactions when adding local preimage data.
parent 23ba7576
...@@ -27,6 +27,7 @@ const ( ...@@ -27,6 +27,7 @@ const (
methodAttack = "attack" methodAttack = "attack"
methodDefend = "defend" methodDefend = "defend"
methodStep = "step" methodStep = "step"
methodAddLocalData = "addLocalData"
) )
type FaultDisputeGameContract struct { type FaultDisputeGameContract struct {
...@@ -195,6 +196,16 @@ func (f *FaultDisputeGameContract) resolveCall() *batching.ContractCall { ...@@ -195,6 +196,16 @@ func (f *FaultDisputeGameContract) resolveCall() *batching.ContractCall {
return f.contract.Call(methodResolve) return f.contract.Call(methodResolve)
} }
func (f *FaultDisputeGameContract) AddLocalDataTx(data *types.PreimageOracleData) (txmgr.TxCandidate, error) {
call := f.contract.Call(
methodAddLocalData,
data.GetIdent(),
new(big.Int).SetUint64(data.LocalContext),
new(big.Int).SetUint64(uint64(data.OracleOffset)),
)
return call.ToTxCandidate()
}
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)
......
...@@ -234,6 +234,25 @@ func TestStepTx(t *testing.T) { ...@@ -234,6 +234,25 @@ func TestStepTx(t *testing.T) {
stubRpc.VerifyTxCandidate(tx) stubRpc.VerifyTxCandidate(tx)
} }
func TestAddLocalDataTx(t *testing.T) {
stubRpc, game := setup(t)
data := &faultTypes.PreimageOracleData{
IsLocal: true,
LocalContext: 2,
OracleKey: common.Hash{0xbc}.Bytes(),
OracleData: []byte{1, 2, 3, 4, 5, 6, 7},
OracleOffset: 16,
}
stubRpc.SetResponse(methodAddLocalData, batching.BlockLatest, []interface{}{
data.GetIdent(),
new(big.Int).SetUint64(data.LocalContext),
new(big.Int).SetUint64(uint64(data.OracleOffset)),
}, nil)
tx, err := game.AddLocalDataTx(data)
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,
......
...@@ -43,7 +43,7 @@ func RegisterGameTypes( ...@@ -43,7 +43,7 @@ func RegisterGameTypes(
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("create cannon trace provider: %w", err) return nil, nil, nil, fmt.Errorf("create cannon trace provider: %w", err)
} }
updater, err := cannon.NewOracleUpdater(ctx, logger, txMgr, addr, client) updater, err := cannon.NewOracleUpdater(ctx, logger, txMgr, addr, client, contract)
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create the cannon updater: %w", err) return nil, nil, nil, fmt.Errorf("failed to create the cannon updater: %w", err)
} }
......
...@@ -16,14 +16,17 @@ import ( ...@@ -16,14 +16,17 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
type GameContract interface {
AddLocalDataTx(data *types.PreimageOracleData) (txmgr.TxCandidate, error)
}
// cannonUpdater is a [types.OracleUpdater] that exposes a method // cannonUpdater is a [types.OracleUpdater] that exposes a method
// to update onchain cannon oracles with required data. // to update onchain cannon oracles with required data.
type cannonUpdater struct { type cannonUpdater struct {
log log.Logger log log.Logger
txMgr txmgr.TxManager txMgr txmgr.TxManager
fdgAbi abi.ABI gameContract GameContract
fdgAddr common.Address
preimageOracleAbi abi.ABI preimageOracleAbi abi.ABI
preimageOracleAddr common.Address preimageOracleAddr common.Address
...@@ -36,6 +39,7 @@ func NewOracleUpdater( ...@@ -36,6 +39,7 @@ func NewOracleUpdater(
txMgr txmgr.TxManager, txMgr txmgr.TxManager,
fdgAddr common.Address, fdgAddr common.Address,
client bind.ContractCaller, client bind.ContractCaller,
gameContract GameContract,
) (*cannonUpdater, error) { ) (*cannonUpdater, error) {
gameCaller, err := bindings.NewFaultDisputeGameCaller(fdgAddr, client) gameCaller, err := bindings.NewFaultDisputeGameCaller(fdgAddr, client)
if err != nil { if err != nil {
...@@ -54,20 +58,16 @@ func NewOracleUpdater( ...@@ -54,20 +58,16 @@ func NewOracleUpdater(
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load pre-image oracle address from game %v: %w", fdgAddr, err) return nil, fmt.Errorf("failed to load pre-image oracle address from game %v: %w", fdgAddr, err)
} }
return NewOracleUpdaterWithOracle(logger, txMgr, fdgAddr, oracleAddr) return NewOracleUpdaterWithOracle(logger, txMgr, gameContract, oracleAddr)
} }
// NewOracleUpdaterWithOracle returns a new updater using a specified pre-image oracle address. // NewOracleUpdaterWithOracle returns a new updater using a specified pre-image oracle address.
func NewOracleUpdaterWithOracle( func NewOracleUpdaterWithOracle(
logger log.Logger, logger log.Logger,
txMgr txmgr.TxManager, txMgr txmgr.TxManager,
fdgAddr common.Address, gameContract GameContract,
preimageOracleAddr common.Address, preimageOracleAddr common.Address,
) (*cannonUpdater, error) { ) (*cannonUpdater, error) {
fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi()
if err != nil {
return nil, err
}
preimageOracleAbi, err := bindings.PreimageOracleMetaData.GetAbi() preimageOracleAbi, err := bindings.PreimageOracleMetaData.GetAbi()
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -77,8 +77,7 @@ func NewOracleUpdaterWithOracle( ...@@ -77,8 +77,7 @@ func NewOracleUpdaterWithOracle(
log: logger, log: logger,
txMgr: txMgr, txMgr: txMgr,
fdgAbi: *fdgAbi, gameContract: gameContract,
fdgAddr: fdgAddr,
preimageOracleAbi: *preimageOracleAbi, preimageOracleAbi: *preimageOracleAbi,
preimageOracleAddr: preimageOracleAddr, preimageOracleAddr: preimageOracleAddr,
...@@ -95,11 +94,11 @@ func (u *cannonUpdater) UpdateOracle(ctx context.Context, data *types.PreimageOr ...@@ -95,11 +94,11 @@ func (u *cannonUpdater) UpdateOracle(ctx context.Context, data *types.PreimageOr
// sendLocalOracleData sends the local oracle data to the [txmgr]. // sendLocalOracleData sends the local oracle data to the [txmgr].
func (u *cannonUpdater) sendLocalOracleData(ctx context.Context, data *types.PreimageOracleData) error { func (u *cannonUpdater) sendLocalOracleData(ctx context.Context, data *types.PreimageOracleData) error {
txData, err := u.BuildLocalOracleData(data) tx, err := u.gameContract.AddLocalDataTx(data)
if err != nil { if err != nil {
return fmt.Errorf("local oracle tx data build: %w", err) return fmt.Errorf("local oracle tx data build: %w", err)
} }
return u.sendTxAndWait(ctx, u.fdgAddr, txData) return u.sendTxAndWait(ctx, tx)
} }
// sendGlobalOracleData sends the global oracle data to the [txmgr]. // sendGlobalOracleData sends the global oracle data to the [txmgr].
...@@ -108,19 +107,11 @@ func (u *cannonUpdater) sendGlobalOracleData(ctx context.Context, data *types.Pr ...@@ -108,19 +107,11 @@ func (u *cannonUpdater) sendGlobalOracleData(ctx context.Context, data *types.Pr
if err != nil { if err != nil {
return fmt.Errorf("global oracle tx data build: %w", err) return fmt.Errorf("global oracle tx data build: %w", err)
} }
return u.sendTxAndWait(ctx, u.fdgAddr, txData) return u.sendTxAndWait(ctx, txmgr.TxCandidate{
} To: &u.preimageOracleAddr,
TxData: txData,
// BuildLocalOracleData takes the local preimage key and data GasLimit: 0,
// and creates tx data to load the key, data pair into the })
// PreimageOracle contract from the FaultDisputeGame contract call.
func (u *cannonUpdater) BuildLocalOracleData(data *types.PreimageOracleData) ([]byte, error) {
return u.fdgAbi.Pack(
"addLocalData",
data.GetIdent(),
big.NewInt(int64(data.LocalContext)),
big.NewInt(int64(data.OracleOffset)),
)
} }
// BuildGlobalOracleData takes the global preimage key and data // BuildGlobalOracleData takes the global preimage key and data
...@@ -136,12 +127,8 @@ func (u *cannonUpdater) BuildGlobalOracleData(data *types.PreimageOracleData) ([ ...@@ -136,12 +127,8 @@ func (u *cannonUpdater) BuildGlobalOracleData(data *types.PreimageOracleData) ([
// 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 (u *cannonUpdater) sendTxAndWait(ctx context.Context, addr common.Address, txData []byte) error { func (u *cannonUpdater) sendTxAndWait(ctx context.Context, tx txmgr.TxCandidate) error {
receipt, err := u.txMgr.Send(ctx, txmgr.TxCandidate{ receipt, err := u.txMgr.Send(ctx, tx)
To: &addr,
TxData: txData,
GasLimit: 0,
})
if err != nil { if err != nil {
return err return err
} }
......
...@@ -5,7 +5,6 @@ import ( ...@@ -5,7 +5,6 @@ import (
"errors" "errors"
"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"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
...@@ -20,29 +19,29 @@ import ( ...@@ -20,29 +19,29 @@ import (
) )
var ( var (
mockFdgAddress = common.HexToAddress("0x1234")
mockPreimageOracleAddress = common.HexToAddress("0x12345") mockPreimageOracleAddress = common.HexToAddress("0x12345")
mockSendError = errors.New("mock send error") mockSendError = errors.New("mock send error")
) )
type mockTxManager struct { type mockTxManager struct {
from common.Address from common.Address
sends int sent []txmgr.TxCandidate
failedSends int failedSends int
sendFails bool sendFails bool
} }
func (m *mockTxManager) Send(ctx context.Context, candidate txmgr.TxCandidate) (*ethtypes.Receipt, error) { func (m *mockTxManager) Send(ctx context.Context, candidate txmgr.TxCandidate) (*ethtypes.Receipt, error) {
m.sent = append(m.sent, candidate)
if m.sendFails { if m.sendFails {
m.failedSends++ m.failedSends++
return nil, mockSendError return nil, mockSendError
} }
m.sends++ return &ethtypes.Receipt{
return ethtypes.NewReceipt( Type: ethtypes.LegacyTxType,
[]byte{}, PostState: []byte{},
false, CumulativeGasUsed: 0,
0, Status: ethtypes.ReceiptStatusSuccessful,
), nil }, nil
} }
func (m *mockTxManager) BlockNumber(ctx context.Context) (uint64, error) { func (m *mockTxManager) BlockNumber(ctx context.Context) (uint64, error) {
...@@ -53,70 +52,80 @@ func (m *mockTxManager) From() common.Address { ...@@ -53,70 +52,80 @@ func (m *mockTxManager) From() common.Address {
return m.from return m.from
} }
func newTestCannonUpdater(t *testing.T, sendFails bool) (*cannonUpdater, *mockTxManager) { func newTestCannonUpdater(t *testing.T, sendFails bool) (*cannonUpdater, *mockTxManager, *mockGameContract) {
logger := testlog.Logger(t, log.LvlInfo) logger := testlog.Logger(t, log.LvlInfo)
txMgr := &mockTxManager{ txMgr := &mockTxManager{
from: mockFdgAddress, from: common.HexToAddress("0x1234"),
sendFails: sendFails, sendFails: sendFails,
} }
updater, err := NewOracleUpdaterWithOracle(logger, txMgr, mockFdgAddress, mockPreimageOracleAddress) gameContract := &mockGameContract{}
updater, err := NewOracleUpdaterWithOracle(logger, txMgr, gameContract, mockPreimageOracleAddress)
require.NoError(t, err) require.NoError(t, err)
return updater, txMgr return updater, txMgr, gameContract
} }
// TestCannonUpdater_UpdateOracle tests the [cannonUpdater] // TestCannonUpdater_UpdateOracle tests the [cannonUpdater]
// UpdateOracle function. // UpdateOracle function.
func TestCannonUpdater_UpdateOracle(t *testing.T) { func TestCannonUpdater_UpdateOracle(t *testing.T) {
t.Run("succeeds", func(t *testing.T) { t.Run("local_succeeds", func(t *testing.T) {
updater, mockTxMgr := newTestCannonUpdater(t, false) updater, mockTxMgr, gameContract := newTestCannonUpdater(t, false)
gameContract.tx = txmgr.TxCandidate{
TxData: []byte{5, 6, 7, 8},
}
require.NoError(t, updater.UpdateOracle(context.Background(), &types.PreimageOracleData{ require.NoError(t, updater.UpdateOracle(context.Background(), &types.PreimageOracleData{
IsLocal: true,
LocalContext: 3,
OracleKey: common.Hash{0xaa}.Bytes(),
OracleData: common.Hex2Bytes("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"),
}))
require.Len(t, mockTxMgr.sent, 1)
require.Equal(t, gameContract.tx, mockTxMgr.sent[0])
})
t.Run("local_fails", func(t *testing.T) {
updater, mockTxMgr, gameContract := newTestCannonUpdater(t, true)
gameContract.tx = txmgr.TxCandidate{
TxData: []byte{5, 6, 7, 8},
}
require.Error(t, updater.UpdateOracle(context.Background(), &types.PreimageOracleData{
IsLocal: true,
LocalContext: 3,
OracleKey: common.Hash{0xaa}.Bytes(),
OracleData: common.Hex2Bytes("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"),
}))
require.Len(t, mockTxMgr.sent, 1)
require.Equal(t, gameContract.tx, mockTxMgr.sent[0])
require.Equal(t, 1, mockTxMgr.failedSends)
})
t.Run("global_succeeds", func(t *testing.T) {
updater, mockTxMgr, _ := newTestCannonUpdater(t, false)
require.NoError(t, updater.UpdateOracle(context.Background(), &types.PreimageOracleData{
IsLocal: false,
OracleKey: common.Hash{0xaa}.Bytes(), OracleKey: common.Hash{0xaa}.Bytes(),
OracleData: common.Hex2Bytes("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"), OracleData: common.Hex2Bytes("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"),
})) }))
require.Equal(t, 1, mockTxMgr.sends) require.Len(t, mockTxMgr.sent, 1)
require.Equal(t, mockPreimageOracleAddress, *mockTxMgr.sent[0].To)
}) })
t.Run("send fails", func(t *testing.T) { t.Run("local_fails", func(t *testing.T) {
updater, mockTxMgr := newTestCannonUpdater(t, true) updater, mockTxMgr, _ := newTestCannonUpdater(t, true)
require.Error(t, updater.UpdateOracle(context.Background(), &types.PreimageOracleData{ require.Error(t, updater.UpdateOracle(context.Background(), &types.PreimageOracleData{
IsLocal: false,
OracleKey: common.Hash{0xaa}.Bytes(), OracleKey: common.Hash{0xaa}.Bytes(),
OracleData: common.Hex2Bytes("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"), OracleData: common.Hex2Bytes("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"),
})) }))
require.Len(t, mockTxMgr.sent, 1)
require.Equal(t, mockPreimageOracleAddress, *mockTxMgr.sent[0].To)
require.Equal(t, 1, mockTxMgr.failedSends) require.Equal(t, 1, mockTxMgr.failedSends)
}) })
} }
// TestCannonUpdater_BuildLocalOracleData tests the [cannonUpdater]
// builds a valid tx candidate for a local oracle update.
func TestCannonUpdater_BuildLocalOracleData(t *testing.T) {
updater, _ := newTestCannonUpdater(t, false)
oracleData := &types.PreimageOracleData{
OracleKey: common.Hex2Bytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
OracleData: common.Hex2Bytes("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"),
OracleOffset: 7,
}
txData, err := updater.BuildLocalOracleData(oracleData)
require.NoError(t, err)
fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi()
require.NoError(t, err)
addLocalDataBytes4 := fdgAbi.Methods["addLocalData"].ID[:4]
// Pack the tx data manually.
var expected []byte
expected = append(expected, addLocalDataBytes4...)
expected = append(expected, common.Hex2Bytes("00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")...)
expected = append(expected, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")...)
expected = append(expected, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000007")...)
require.Equal(t, expected, txData)
}
// TestCannonUpdater_BuildGlobalOracleData tests the [cannonUpdater] // TestCannonUpdater_BuildGlobalOracleData tests the [cannonUpdater]
// builds a valid tx candidate for a global oracle update. // builds a valid tx candidate for a global oracle update.
func TestCannonUpdater_BuildGlobalOracleData(t *testing.T) { func TestCannonUpdater_BuildGlobalOracleData(t *testing.T) {
updater, _ := newTestCannonUpdater(t, false) updater, _, _ := newTestCannonUpdater(t, false)
oracleData := &types.PreimageOracleData{ oracleData := &types.PreimageOracleData{
OracleKey: common.Hex2Bytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), OracleKey: common.Hex2Bytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
OracleData: common.Hex2Bytes("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"), OracleData: common.Hex2Bytes("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"),
...@@ -138,3 +147,12 @@ func TestCannonUpdater_BuildGlobalOracleData(t *testing.T) { ...@@ -138,3 +147,12 @@ func TestCannonUpdater_BuildGlobalOracleData(t *testing.T) {
require.Equal(t, expected, txData) require.Equal(t, expected, txData)
} }
type mockGameContract struct {
tx txmgr.TxCandidate
err error
}
func (m *mockGameContract) AddLocalDataTx(data *types.PreimageOracleData) (txmgr.TxCandidate, error) {
return m.tx, m.err
}
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