Commit d9dcc280 authored by George Knee's avatar George Knee Committed by GitHub

op-proposer: ensure uniform proposal interval across restarts (#11417)

* op-proposer: ensure uniform proposal interval across restarts

closes https://github.com/ethereum-optimism/optimism/issues/11193

* move DGF query logic into FetchDGFOutput

* merge loopL2OO and loopDGF

* tweak comments

* Apply suggestions from code review
Co-authored-by: default avatarSebastian Stammler <seb@oplabs.co>

* return err instead of defaulting to sending a proposak

flatten out control flow, remove shouldPropose var

* defer l.Log.Info("loop returning")

* improve error handling and logging

* fix logging syntax error

* make DGFContract interface

harmonize how network contexts are constructed

* modify test for new DGF behavior

* fix bugs in test code

* remove OutputRetryInterval flag

* handle gameCount = 0

* finish removing OutputRetryInterval

* driver waits one proposal interval for the first ever proposal

* do not create mock unecessarily

* do not create mockL2OOContract unecessarily

* wrap and return errors instead of logging and returning

* op-proposer: Switch to modern binding style for dispute game factory (#11472)

---------
Co-authored-by: default avatarSebastian Stammler <seb@oplabs.co>
Co-authored-by: default avatarAdrian Sutton <adrian@oplabs.co>
parent 5a5dd8f4
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"math/big" "math/big"
"time" "time"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -84,7 +85,6 @@ func NewL2Proposer(t Testing, log log.Logger, cfg *ProposerCfg, l1 *ethclient.Cl ...@@ -84,7 +85,6 @@ func NewL2Proposer(t Testing, log log.Logger, cfg *ProposerCfg, l1 *ethclient.Cl
PollInterval: time.Second, PollInterval: time.Second,
NetworkTimeout: time.Second, NetworkTimeout: time.Second,
ProposalInterval: cfg.ProposalInterval, ProposalInterval: cfg.ProposalInterval,
OutputRetryInterval: cfg.ProposalRetryInterval,
L2OutputOracleAddr: cfg.OutputOracleAddr, L2OutputOracleAddr: cfg.OutputOracleAddr,
DisputeGameFactoryAddr: cfg.DisputeGameFactoryAddr, DisputeGameFactoryAddr: cfg.DisputeGameFactoryAddr,
DisputeGameType: cfg.DisputeGameType, DisputeGameType: cfg.DisputeGameType,
...@@ -98,6 +98,7 @@ func NewL2Proposer(t Testing, log log.Logger, cfg *ProposerCfg, l1 *ethclient.Cl ...@@ -98,6 +98,7 @@ func NewL2Proposer(t Testing, log log.Logger, cfg *ProposerCfg, l1 *ethclient.Cl
Cfg: proposerConfig, Cfg: proposerConfig,
Txmgr: fakeTxMgr{from: crypto.PubkeyToAddress(cfg.ProposerKey.PublicKey)}, Txmgr: fakeTxMgr{from: crypto.PubkeyToAddress(cfg.ProposerKey.PublicKey)},
L1Client: l1, L1Client: l1,
Multicaller: batching.NewMultiCaller(l1.Client(), batching.DefaultBatchSize),
RollupProvider: rollupProvider, RollupProvider: rollupProvider,
} }
...@@ -214,8 +215,8 @@ func toCallArg(msg ethereum.CallMsg) interface{} { ...@@ -214,8 +215,8 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
func (p *L2Proposer) fetchNextOutput(t Testing) (*eth.OutputResponse, bool, error) { func (p *L2Proposer) fetchNextOutput(t Testing) (*eth.OutputResponse, bool, error) {
if e2eutils.UseFaultProofs() { if e2eutils.UseFaultProofs() {
output, err := p.driver.FetchDGFOutput(t.Ctx()) output, shouldPropose, err := p.driver.FetchDGFOutput(t.Ctx())
if err != nil { if err != nil || !shouldPropose {
return nil, false, err return nil, false, err
} }
encodedBlockNumber := make([]byte, 32) encodedBlockNumber := make([]byte, 32)
...@@ -250,8 +251,9 @@ func (p *L2Proposer) ActMakeProposalTx(t Testing) { ...@@ -250,8 +251,9 @@ func (p *L2Proposer) ActMakeProposalTx(t Testing) {
var txData []byte var txData []byte
if e2eutils.UseFaultProofs() { if e2eutils.UseFaultProofs() {
txData, _, err = p.driver.ProposeL2OutputDGFTxData(output) tx, err := p.driver.ProposeL2OutputDGFTxCandidate(context.Background(), output)
require.NoError(t, err) require.NoError(t, err)
txData = tx.TxData
} else { } else {
txData, err = p.driver.ProposeL2OutputTxData(output) txData, err = p.driver.ProposeL2OutputTxData(output)
require.NoError(t, err) require.NoError(t, err)
......
...@@ -165,7 +165,7 @@ func DefaultSystemConfig(t testing.TB) SystemConfig { ...@@ -165,7 +165,7 @@ func DefaultSystemConfig(t testing.TB) SystemConfig {
RoleVerif: testlog.Logger(t, log.LevelInfo).New("role", RoleVerif), RoleVerif: testlog.Logger(t, log.LevelInfo).New("role", RoleVerif),
RoleSeq: testlog.Logger(t, log.LevelInfo).New("role", RoleSeq), RoleSeq: testlog.Logger(t, log.LevelInfo).New("role", RoleSeq),
"batcher": testlog.Logger(t, log.LevelInfo).New("role", "batcher"), "batcher": testlog.Logger(t, log.LevelInfo).New("role", "batcher"),
"proposer": testlog.Logger(t, log.LevelCrit).New("role", "proposer"), "proposer": testlog.Logger(t, log.LevelInfo).New("role", "proposer"),
}, },
GethOptions: map[string][]geth.GethOption{}, GethOptions: map[string][]geth.GethOption{},
P2PTopology: nil, // no P2P connectivity by default P2PTopology: nil, // no P2P connectivity by default
...@@ -861,7 +861,6 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste ...@@ -861,7 +861,6 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste
ProposalInterval: 6 * time.Second, ProposalInterval: 6 * time.Second,
DisputeGameType: 254, // Fast game type DisputeGameType: 254, // Fast game type
PollInterval: 50 * time.Millisecond, PollInterval: 50 * time.Millisecond,
OutputRetryInterval: 10 * time.Millisecond,
TxMgrConfig: newTxMgrConfig(sys.EthInstances[RoleL1].WSEndpoint(), cfg.Secrets.Proposer), TxMgrConfig: newTxMgrConfig(sys.EthInstances[RoleL1].WSEndpoint(), cfg.Secrets.Proposer),
AllowNonFinalized: cfg.NonFinalizedProposals, AllowNonFinalized: cfg.NonFinalizedProposals,
LogConfig: oplog.CLIConfig{ LogConfig: oplog.CLIConfig{
...@@ -875,7 +874,6 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste ...@@ -875,7 +874,6 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste
RollupRpc: sys.RollupNodes[RoleSeq].HTTPEndpoint(), RollupRpc: sys.RollupNodes[RoleSeq].HTTPEndpoint(),
L2OOAddress: config.L1Deployments.L2OutputOracleProxy.Hex(), L2OOAddress: config.L1Deployments.L2OutputOracleProxy.Hex(),
PollInterval: 50 * time.Millisecond, PollInterval: 50 * time.Millisecond,
OutputRetryInterval: 10 * time.Millisecond,
TxMgrConfig: newTxMgrConfig(sys.EthInstances[RoleL1].WSEndpoint(), cfg.Secrets.Proposer), TxMgrConfig: newTxMgrConfig(sys.EthInstances[RoleL1].WSEndpoint(), cfg.Secrets.Proposer),
AllowNonFinalized: cfg.NonFinalizedProposals, AllowNonFinalized: cfg.NonFinalizedProposals,
LogConfig: oplog.CLIConfig{ LogConfig: oplog.CLIConfig{
......
// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package bindings
import (
"errors"
"math/big"
"strings"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
)
// Reference imports to suppress errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt
_ = strings.NewReader
_ = ethereum.NotFound
_ = bind.Bind
_ = common.Big1
_ = types.BloomLookup
_ = event.NewSubscription
)
// IDisputeGameFactoryGameSearchResult is an auto generated low-level Go binding around an user-defined struct.
type IDisputeGameFactoryGameSearchResult struct {
Index *big.Int
Metadata [32]byte
Timestamp uint64
RootClaim [32]byte
ExtraData []byte
}
// DisputeGameFactoryMetaData contains all meta data concerning the DisputeGameFactory contract.
var DisputeGameFactoryMetaData = &bind.MetaData{
ABI: "[{\"type\":\"constructor\",\"inputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"create\",\"inputs\":[{\"name\":\"_gameType\",\"type\":\"uint32\",\"internalType\":\"GameType\"},{\"name\":\"_rootClaim\",\"type\":\"bytes32\",\"internalType\":\"Claim\"},{\"name\":\"_extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"proxy_\",\"type\":\"address\",\"internalType\":\"contractIDisputeGame\"}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"findLatestGames\",\"inputs\":[{\"name\":\"_gameType\",\"type\":\"uint32\",\"internalType\":\"GameType\"},{\"name\":\"_start\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_n\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"games_\",\"type\":\"tuple[]\",\"internalType\":\"structIDisputeGameFactory.GameSearchResult[]\",\"components\":[{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"metadata\",\"type\":\"bytes32\",\"internalType\":\"GameId\"},{\"name\":\"timestamp\",\"type\":\"uint64\",\"internalType\":\"Timestamp\"},{\"name\":\"rootClaim\",\"type\":\"bytes32\",\"internalType\":\"Claim\"},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gameAtIndex\",\"inputs\":[{\"name\":\"_index\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"gameType_\",\"type\":\"uint32\",\"internalType\":\"GameType\"},{\"name\":\"timestamp_\",\"type\":\"uint64\",\"internalType\":\"Timestamp\"},{\"name\":\"proxy_\",\"type\":\"address\",\"internalType\":\"contractIDisputeGame\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gameCount\",\"inputs\":[],\"outputs\":[{\"name\":\"gameCount_\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"gameImpls\",\"inputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"GameType\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractIDisputeGame\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"games\",\"inputs\":[{\"name\":\"_gameType\",\"type\":\"uint32\",\"internalType\":\"GameType\"},{\"name\":\"_rootClaim\",\"type\":\"bytes32\",\"internalType\":\"Claim\"},{\"name\":\"_extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"proxy_\",\"type\":\"address\",\"internalType\":\"contractIDisputeGame\"},{\"name\":\"timestamp_\",\"type\":\"uint64\",\"internalType\":\"Timestamp\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getGameUUID\",\"inputs\":[{\"name\":\"_gameType\",\"type\":\"uint32\",\"internalType\":\"GameType\"},{\"name\":\"_rootClaim\",\"type\":\"bytes32\",\"internalType\":\"Claim\"},{\"name\":\"_extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"uuid_\",\"type\":\"bytes32\",\"internalType\":\"Hash\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"initBonds\",\"inputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"GameType\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setImplementation\",\"inputs\":[{\"name\":\"_gameType\",\"type\":\"uint32\",\"internalType\":\"GameType\"},{\"name\":\"_impl\",\"type\":\"address\",\"internalType\":\"contractIDisputeGame\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setInitBond\",\"inputs\":[{\"name\":\"_gameType\",\"type\":\"uint32\",\"internalType\":\"GameType\"},{\"name\":\"_initBond\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"version\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"DisputeGameCreated\",\"inputs\":[{\"name\":\"disputeProxy\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"gameType\",\"type\":\"uint32\",\"indexed\":true,\"internalType\":\"GameType\"},{\"name\":\"rootClaim\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"Claim\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ImplementationSet\",\"inputs\":[{\"name\":\"impl\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"gameType\",\"type\":\"uint32\",\"indexed\":true,\"internalType\":\"GameType\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"InitBondUpdated\",\"inputs\":[{\"name\":\"gameType\",\"type\":\"uint32\",\"indexed\":true,\"internalType\":\"GameType\"},{\"name\":\"newBond\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"GameAlreadyExists\",\"inputs\":[{\"name\":\"uuid\",\"type\":\"bytes32\",\"internalType\":\"Hash\"}]},{\"type\":\"error\",\"name\":\"IncorrectBondAmount\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NoImplementation\",\"inputs\":[{\"name\":\"gameType\",\"type\":\"uint32\",\"internalType\":\"GameType\"}]}]",
Bin: "0x60806040523480156200001157600080fd5b506200001e600062000024565b62000292565b600054610100900460ff1615808015620000455750600054600160ff909116105b8062000075575062000062306200016260201b62000cdd1760201c565b15801562000075575060005460ff166001145b620000de5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562000102576000805461ff0019166101001790555b6200010c62000171565b6200011782620001d9565b80156200015e576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6001600160a01b03163b151590565b600054610100900460ff16620001cd5760405162461bcd60e51b815260206004820152602b60248201526000805160206200185283398151915260448201526a6e697469616c697a696e6760a81b6064820152608401620000d5565b620001d76200022b565b565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16620002875760405162461bcd60e51b815260206004820152602b60248201526000805160206200185283398151915260448201526a6e697469616c697a696e6760a81b6064820152608401620000d5565b620001d733620001d9565b6115b080620002a26000396000f3fe6080604052600436106100e85760003560e01c80636593dc6e1161008a57806396cd97201161005957806396cd972014610313578063bb8aa1fc14610333578063c4d66de814610394578063f2fde38b146103b457600080fd5b80636593dc6e14610293578063715018a6146102c057806382ecf2f6146102d55780638da5cb5b146102e857600080fd5b8063254bd683116100c6578063254bd6831461019c5780634d1975b4146101c957806354fd4d50146101e85780635f0150cb1461023e57600080fd5b806314f6b1a3146100ed5780631b685b9e1461010f5780631e3342401461017c575b600080fd5b3480156100f957600080fd5b5061010d6101083660046110bf565b6103d4565b005b34801561011b57600080fd5b5061015261012a3660046110f6565b60656020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561018857600080fd5b5061010d610197366004611111565b61045e565b3480156101a857600080fd5b506101bc6101b736600461113b565b6104aa565b60405161017391906111e8565b3480156101d557600080fd5b506068545b604051908152602001610173565b3480156101f457600080fd5b506102316040518060400160405280600581526020017f302e362e3000000000000000000000000000000000000000000000000000000081525081565b60405161017391906112a5565b34801561024a57600080fd5b5061025e6102593660046112b8565b6106ee565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835267ffffffffffffffff909116602083015201610173565b34801561029f57600080fd5b506101da6102ae3660046110f6565b60666020526000908152604090205481565b3480156102cc57600080fd5b5061010d610741565b6101526102e33660046112b8565b610755565b3480156102f457600080fd5b5060335473ffffffffffffffffffffffffffffffffffffffff16610152565b34801561031f57600080fd5b506101da61032e3660046112b8565b6109ef565b34801561033f57600080fd5b5061035361034e36600461133f565b610a28565b6040805163ffffffff909416845267ffffffffffffffff909216602084015273ffffffffffffffffffffffffffffffffffffffff1690820152606001610173565b3480156103a057600080fd5b5061010d6103af366004611358565b610a8a565b3480156103c057600080fd5b5061010d6103cf366004611358565b610c26565b6103dc610cf9565b63ffffffff821660008181526065602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8616908117909155905190917fff513d80e2c7fa487608f70a618dfbc0cf415699dc69588c747e8c71566c88de91a35050565b610466610cf9565b63ffffffff8216600081815260666020526040808220849055518392917f74d6665c4b26d5596a5aa13d3014e0c06af4d322075a797f87b03cd4c5bc91ca91a35050565b606854606090831015806104bc575081155b6106e7575060408051600583901b8101602001909152825b8381116106e5576000606882815481106104f0576104f0611375565b600091825260209091200154905060e081901c60a082901c67ffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff831663ffffffff891683036106b6576001865101865260008173ffffffffffffffffffffffffffffffffffffffff1663609d33346040518163ffffffff1660e01b8152600401600060405180830381865afa15801561058a573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526105d091908101906113d3565b905060008273ffffffffffffffffffffffffffffffffffffffff1663bcef3b556040518163ffffffff1660e01b8152600401602060405180830381865afa15801561061f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610643919061149e565b90506040518060a001604052808881526020018781526020018567ffffffffffffffff168152602001828152602001838152508860018a5161068591906114b7565b8151811061069557610695611375565b6020026020010181905250888851106106b3575050505050506106e5565b50505b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191506104d49050565b505b9392505050565b60008060006106ff878787876109ef565b60009081526067602052604090205473ffffffffffffffffffffffffffffffffffffffff81169860a09190911c67ffffffffffffffff16975095505050505050565b610749610cf9565b6107536000610d7a565b565b63ffffffff841660009081526065602052604081205473ffffffffffffffffffffffffffffffffffffffff16806107c5576040517f031c6de400000000000000000000000000000000000000000000000000000000815263ffffffff871660048201526024015b60405180910390fd5b63ffffffff86166000908152606660205260409020543414610813576040517f8620aa1900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006108206001436114b7565b40905061088a338783888860405160200161083f9594939291906114f5565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905273ffffffffffffffffffffffffffffffffffffffff841690610df1565b92508273ffffffffffffffffffffffffffffffffffffffff16638129fc1c346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156108d457600080fd5b505af11580156108e8573d6000803e3d6000fd5b505050505060006108fb888888886109ef565b60008181526067602052604090205490915015610947576040517f014f6fe5000000000000000000000000000000000000000000000000000000008152600481018290526024016107bc565b60004260a01b60e08a901b178517600083815260676020526040808220839055606880546001810182559083527fa2153420d844928b4421650203c77babc8b33d7f2e7b450e2966db0c220977530183905551919250899163ffffffff8c169173ffffffffffffffffffffffffffffffffffffffff8916917f5b565efe82411da98814f356d0e7bcb8f0219b8d970307c5afb4a6903a8b2e359190a450505050949350505050565b600084848484604051602001610a089493929190611542565b604051602081830303815290604052805190602001209050949350505050565b6000806000610a7d60688581548110610a4357610a43611375565b906000526020600020015460e081901c9160a082901c67ffffffffffffffff169173ffffffffffffffffffffffffffffffffffffffff1690565b9196909550909350915050565b600054610100900460ff1615808015610aaa5750600054600160ff909116105b80610ac45750303b158015610ac4575060005460ff166001145b610b50576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016107bc565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015610bae57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b610bb6610dff565b610bbf82610d7a565b8015610c2257600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b610c2e610cf9565b73ffffffffffffffffffffffffffffffffffffffff8116610cd1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016107bc565b610cda81610d7a565b50565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b60335473ffffffffffffffffffffffffffffffffffffffff163314610753576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016107bc565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60006106e760008484610e9e565b600054610100900460ff16610e96576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016107bc565b610753610fe4565b600060608203516040830351602084035184518060208701018051600283016c5af43d3d93803e606057fd5bf3895289600d8a035278593da1005b363d3d373d3d3d3d610000806062363936013d738160481b1760218a03527f9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff603a8a035272fd6100003d81600a3d39f336602c57343d527f6062820160781b1761ff9e82106059018a03528060f01b8352606c8101604c8a038cf097505086610f6a5763301164256000526004601cfd5b905285527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08501527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa09092019190915292915050565b600054610100900460ff1661107b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016107bc565b61075333610d7a565b803563ffffffff8116811461109857600080fd5b919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610cda57600080fd5b600080604083850312156110d257600080fd5b6110db83611084565b915060208301356110eb8161109d565b809150509250929050565b60006020828403121561110857600080fd5b6106e782611084565b6000806040838503121561112457600080fd5b61112d83611084565b946020939093013593505050565b60008060006060848603121561115057600080fd5b61115984611084565b95602085013595506040909401359392505050565b60005b83811015611189578181015183820152602001611171565b83811115611198576000848401525b50505050565b600081518084526111b681602086016020860161116e565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015611297578883037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc001855281518051845287810151888501528681015167ffffffffffffffff16878501526060808201519085015260809081015160a0918501829052906112838186018361119e565b96890196945050509086019060010161120f565b509098975050505050505050565b6020815260006106e7602083018461119e565b600080600080606085870312156112ce57600080fd5b6112d785611084565b935060208501359250604085013567ffffffffffffffff808211156112fb57600080fd5b818701915087601f83011261130f57600080fd5b81358181111561131e57600080fd5b88602082850101111561133057600080fd5b95989497505060200194505050565b60006020828403121561135157600080fd5b5035919050565b60006020828403121561136a57600080fd5b81356106e78161109d565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000602082840312156113e557600080fd5b815167ffffffffffffffff808211156113fd57600080fd5b818401915084601f83011261141157600080fd5b815181811115611423576114236113a4565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715611469576114696113a4565b8160405282815287602084870101111561148257600080fd5b61149383602083016020880161116e565b979650505050505050565b6000602082840312156114b057600080fd5b5051919050565b6000828210156114f0577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b500390565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008660601b1681528460148201528360348201528183605483013760009101605401908152949350505050565b63ffffffff8516815283602082015260606040820152816060820152818360808301376000818301608090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101939250505056fea164736f6c634300080f000a496e697469616c697a61626c653a20636f6e7472616374206973206e6f742069",
}
// DisputeGameFactoryABI is the input ABI used to generate the binding from.
// Deprecated: Use DisputeGameFactoryMetaData.ABI instead.
var DisputeGameFactoryABI = DisputeGameFactoryMetaData.ABI
// DisputeGameFactoryBin is the compiled bytecode used for deploying new contracts.
// Deprecated: Use DisputeGameFactoryMetaData.Bin instead.
var DisputeGameFactoryBin = DisputeGameFactoryMetaData.Bin
// DeployDisputeGameFactory deploys a new Ethereum contract, binding an instance of DisputeGameFactory to it.
func DeployDisputeGameFactory(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *DisputeGameFactory, error) {
parsed, err := DisputeGameFactoryMetaData.GetAbi()
if err != nil {
return common.Address{}, nil, nil, err
}
if parsed == nil {
return common.Address{}, nil, nil, errors.New("GetABI returned nil")
}
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(DisputeGameFactoryBin), backend)
if err != nil {
return common.Address{}, nil, nil, err
}
return address, tx, &DisputeGameFactory{DisputeGameFactoryCaller: DisputeGameFactoryCaller{contract: contract}, DisputeGameFactoryTransactor: DisputeGameFactoryTransactor{contract: contract}, DisputeGameFactoryFilterer: DisputeGameFactoryFilterer{contract: contract}}, nil
}
// DisputeGameFactory is an auto generated Go binding around an Ethereum contract.
type DisputeGameFactory struct {
DisputeGameFactoryCaller // Read-only binding to the contract
DisputeGameFactoryTransactor // Write-only binding to the contract
DisputeGameFactoryFilterer // Log filterer for contract events
}
// DisputeGameFactoryCaller is an auto generated read-only Go binding around an Ethereum contract.
type DisputeGameFactoryCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// DisputeGameFactoryTransactor is an auto generated write-only Go binding around an Ethereum contract.
type DisputeGameFactoryTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// DisputeGameFactoryFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type DisputeGameFactoryFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// DisputeGameFactorySession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
type DisputeGameFactorySession struct {
Contract *DisputeGameFactory // Generic contract binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// DisputeGameFactoryCallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
type DisputeGameFactoryCallerSession struct {
Contract *DisputeGameFactoryCaller // Generic contract caller binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
}
// DisputeGameFactoryTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
type DisputeGameFactoryTransactorSession struct {
Contract *DisputeGameFactoryTransactor // Generic contract transactor binding to set the session for
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// DisputeGameFactoryRaw is an auto generated low-level Go binding around an Ethereum contract.
type DisputeGameFactoryRaw struct {
Contract *DisputeGameFactory // Generic contract binding to access the raw methods on
}
// DisputeGameFactoryCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
type DisputeGameFactoryCallerRaw struct {
Contract *DisputeGameFactoryCaller // Generic read-only contract binding to access the raw methods on
}
// DisputeGameFactoryTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type DisputeGameFactoryTransactorRaw struct {
Contract *DisputeGameFactoryTransactor // Generic write-only contract binding to access the raw methods on
}
// NewDisputeGameFactory creates a new instance of DisputeGameFactory, bound to a specific deployed contract.
func NewDisputeGameFactory(address common.Address, backend bind.ContractBackend) (*DisputeGameFactory, error) {
contract, err := bindDisputeGameFactory(address, backend, backend, backend)
if err != nil {
return nil, err
}
return &DisputeGameFactory{DisputeGameFactoryCaller: DisputeGameFactoryCaller{contract: contract}, DisputeGameFactoryTransactor: DisputeGameFactoryTransactor{contract: contract}, DisputeGameFactoryFilterer: DisputeGameFactoryFilterer{contract: contract}}, nil
}
// NewDisputeGameFactoryCaller creates a new read-only instance of DisputeGameFactory, bound to a specific deployed contract.
func NewDisputeGameFactoryCaller(address common.Address, caller bind.ContractCaller) (*DisputeGameFactoryCaller, error) {
contract, err := bindDisputeGameFactory(address, caller, nil, nil)
if err != nil {
return nil, err
}
return &DisputeGameFactoryCaller{contract: contract}, nil
}
// NewDisputeGameFactoryTransactor creates a new write-only instance of DisputeGameFactory, bound to a specific deployed contract.
func NewDisputeGameFactoryTransactor(address common.Address, transactor bind.ContractTransactor) (*DisputeGameFactoryTransactor, error) {
contract, err := bindDisputeGameFactory(address, nil, transactor, nil)
if err != nil {
return nil, err
}
return &DisputeGameFactoryTransactor{contract: contract}, nil
}
// NewDisputeGameFactoryFilterer creates a new log filterer instance of DisputeGameFactory, bound to a specific deployed contract.
func NewDisputeGameFactoryFilterer(address common.Address, filterer bind.ContractFilterer) (*DisputeGameFactoryFilterer, error) {
contract, err := bindDisputeGameFactory(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &DisputeGameFactoryFilterer{contract: contract}, nil
}
// bindDisputeGameFactory binds a generic wrapper to an already deployed contract.
func bindDisputeGameFactory(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(DisputeGameFactoryABI))
if err != nil {
return nil, err
}
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_DisputeGameFactory *DisputeGameFactoryRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _DisputeGameFactory.Contract.DisputeGameFactoryCaller.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_DisputeGameFactory *DisputeGameFactoryRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.DisputeGameFactoryTransactor.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_DisputeGameFactory *DisputeGameFactoryRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.DisputeGameFactoryTransactor.contract.Transact(opts, method, params...)
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_DisputeGameFactory *DisputeGameFactoryCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _DisputeGameFactory.Contract.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_DisputeGameFactory *DisputeGameFactoryTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_DisputeGameFactory *DisputeGameFactoryTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.contract.Transact(opts, method, params...)
}
// FindLatestGames is a free data retrieval call binding the contract method 0x254bd683.
//
// Solidity: function findLatestGames(uint32 _gameType, uint256 _start, uint256 _n) view returns((uint256,bytes32,uint64,bytes32,bytes)[] games_)
func (_DisputeGameFactory *DisputeGameFactoryCaller) FindLatestGames(opts *bind.CallOpts, _gameType uint32, _start *big.Int, _n *big.Int) ([]IDisputeGameFactoryGameSearchResult, error) {
var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "findLatestGames", _gameType, _start, _n)
if err != nil {
return *new([]IDisputeGameFactoryGameSearchResult), err
}
out0 := *abi.ConvertType(out[0], new([]IDisputeGameFactoryGameSearchResult)).(*[]IDisputeGameFactoryGameSearchResult)
return out0, err
}
// FindLatestGames is a free data retrieval call binding the contract method 0x254bd683.
//
// Solidity: function findLatestGames(uint32 _gameType, uint256 _start, uint256 _n) view returns((uint256,bytes32,uint64,bytes32,bytes)[] games_)
func (_DisputeGameFactory *DisputeGameFactorySession) FindLatestGames(_gameType uint32, _start *big.Int, _n *big.Int) ([]IDisputeGameFactoryGameSearchResult, error) {
return _DisputeGameFactory.Contract.FindLatestGames(&_DisputeGameFactory.CallOpts, _gameType, _start, _n)
}
// FindLatestGames is a free data retrieval call binding the contract method 0x254bd683.
//
// Solidity: function findLatestGames(uint32 _gameType, uint256 _start, uint256 _n) view returns((uint256,bytes32,uint64,bytes32,bytes)[] games_)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) FindLatestGames(_gameType uint32, _start *big.Int, _n *big.Int) ([]IDisputeGameFactoryGameSearchResult, error) {
return _DisputeGameFactory.Contract.FindLatestGames(&_DisputeGameFactory.CallOpts, _gameType, _start, _n)
}
// GameAtIndex is a free data retrieval call binding the contract method 0xbb8aa1fc.
//
// Solidity: function gameAtIndex(uint256 _index) view returns(uint32 gameType_, uint64 timestamp_, address proxy_)
func (_DisputeGameFactory *DisputeGameFactoryCaller) GameAtIndex(opts *bind.CallOpts, _index *big.Int) (struct {
GameType uint32
Timestamp uint64
Proxy common.Address
}, error) {
var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "gameAtIndex", _index)
outstruct := new(struct {
GameType uint32
Timestamp uint64
Proxy common.Address
})
if err != nil {
return *outstruct, err
}
outstruct.GameType = *abi.ConvertType(out[0], new(uint32)).(*uint32)
outstruct.Timestamp = *abi.ConvertType(out[1], new(uint64)).(*uint64)
outstruct.Proxy = *abi.ConvertType(out[2], new(common.Address)).(*common.Address)
return *outstruct, err
}
// GameAtIndex is a free data retrieval call binding the contract method 0xbb8aa1fc.
//
// Solidity: function gameAtIndex(uint256 _index) view returns(uint32 gameType_, uint64 timestamp_, address proxy_)
func (_DisputeGameFactory *DisputeGameFactorySession) GameAtIndex(_index *big.Int) (struct {
GameType uint32
Timestamp uint64
Proxy common.Address
}, error) {
return _DisputeGameFactory.Contract.GameAtIndex(&_DisputeGameFactory.CallOpts, _index)
}
// GameAtIndex is a free data retrieval call binding the contract method 0xbb8aa1fc.
//
// Solidity: function gameAtIndex(uint256 _index) view returns(uint32 gameType_, uint64 timestamp_, address proxy_)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) GameAtIndex(_index *big.Int) (struct {
GameType uint32
Timestamp uint64
Proxy common.Address
}, error) {
return _DisputeGameFactory.Contract.GameAtIndex(&_DisputeGameFactory.CallOpts, _index)
}
// GameCount is a free data retrieval call binding the contract method 0x4d1975b4.
//
// Solidity: function gameCount() view returns(uint256 gameCount_)
func (_DisputeGameFactory *DisputeGameFactoryCaller) GameCount(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "gameCount")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// GameCount is a free data retrieval call binding the contract method 0x4d1975b4.
//
// Solidity: function gameCount() view returns(uint256 gameCount_)
func (_DisputeGameFactory *DisputeGameFactorySession) GameCount() (*big.Int, error) {
return _DisputeGameFactory.Contract.GameCount(&_DisputeGameFactory.CallOpts)
}
// GameCount is a free data retrieval call binding the contract method 0x4d1975b4.
//
// Solidity: function gameCount() view returns(uint256 gameCount_)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) GameCount() (*big.Int, error) {
return _DisputeGameFactory.Contract.GameCount(&_DisputeGameFactory.CallOpts)
}
// GameImpls is a free data retrieval call binding the contract method 0x1b685b9e.
//
// Solidity: function gameImpls(uint32 ) view returns(address)
func (_DisputeGameFactory *DisputeGameFactoryCaller) GameImpls(opts *bind.CallOpts, arg0 uint32) (common.Address, error) {
var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "gameImpls", arg0)
if err != nil {
return *new(common.Address), err
}
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
return out0, err
}
// GameImpls is a free data retrieval call binding the contract method 0x1b685b9e.
//
// Solidity: function gameImpls(uint32 ) view returns(address)
func (_DisputeGameFactory *DisputeGameFactorySession) GameImpls(arg0 uint32) (common.Address, error) {
return _DisputeGameFactory.Contract.GameImpls(&_DisputeGameFactory.CallOpts, arg0)
}
// GameImpls is a free data retrieval call binding the contract method 0x1b685b9e.
//
// Solidity: function gameImpls(uint32 ) view returns(address)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) GameImpls(arg0 uint32) (common.Address, error) {
return _DisputeGameFactory.Contract.GameImpls(&_DisputeGameFactory.CallOpts, arg0)
}
// Games is a free data retrieval call binding the contract method 0x5f0150cb.
//
// Solidity: function games(uint32 _gameType, bytes32 _rootClaim, bytes _extraData) view returns(address proxy_, uint64 timestamp_)
func (_DisputeGameFactory *DisputeGameFactoryCaller) Games(opts *bind.CallOpts, _gameType uint32, _rootClaim [32]byte, _extraData []byte) (struct {
Proxy common.Address
Timestamp uint64
}, error) {
var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "games", _gameType, _rootClaim, _extraData)
outstruct := new(struct {
Proxy common.Address
Timestamp uint64
})
if err != nil {
return *outstruct, err
}
outstruct.Proxy = *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
outstruct.Timestamp = *abi.ConvertType(out[1], new(uint64)).(*uint64)
return *outstruct, err
}
// Games is a free data retrieval call binding the contract method 0x5f0150cb.
//
// Solidity: function games(uint32 _gameType, bytes32 _rootClaim, bytes _extraData) view returns(address proxy_, uint64 timestamp_)
func (_DisputeGameFactory *DisputeGameFactorySession) Games(_gameType uint32, _rootClaim [32]byte, _extraData []byte) (struct {
Proxy common.Address
Timestamp uint64
}, error) {
return _DisputeGameFactory.Contract.Games(&_DisputeGameFactory.CallOpts, _gameType, _rootClaim, _extraData)
}
// Games is a free data retrieval call binding the contract method 0x5f0150cb.
//
// Solidity: function games(uint32 _gameType, bytes32 _rootClaim, bytes _extraData) view returns(address proxy_, uint64 timestamp_)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) Games(_gameType uint32, _rootClaim [32]byte, _extraData []byte) (struct {
Proxy common.Address
Timestamp uint64
}, error) {
return _DisputeGameFactory.Contract.Games(&_DisputeGameFactory.CallOpts, _gameType, _rootClaim, _extraData)
}
// GetGameUUID is a free data retrieval call binding the contract method 0x96cd9720.
//
// Solidity: function getGameUUID(uint32 _gameType, bytes32 _rootClaim, bytes _extraData) pure returns(bytes32 uuid_)
func (_DisputeGameFactory *DisputeGameFactoryCaller) GetGameUUID(opts *bind.CallOpts, _gameType uint32, _rootClaim [32]byte, _extraData []byte) ([32]byte, error) {
var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "getGameUUID", _gameType, _rootClaim, _extraData)
if err != nil {
return *new([32]byte), err
}
out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte)
return out0, err
}
// GetGameUUID is a free data retrieval call binding the contract method 0x96cd9720.
//
// Solidity: function getGameUUID(uint32 _gameType, bytes32 _rootClaim, bytes _extraData) pure returns(bytes32 uuid_)
func (_DisputeGameFactory *DisputeGameFactorySession) GetGameUUID(_gameType uint32, _rootClaim [32]byte, _extraData []byte) ([32]byte, error) {
return _DisputeGameFactory.Contract.GetGameUUID(&_DisputeGameFactory.CallOpts, _gameType, _rootClaim, _extraData)
}
// GetGameUUID is a free data retrieval call binding the contract method 0x96cd9720.
//
// Solidity: function getGameUUID(uint32 _gameType, bytes32 _rootClaim, bytes _extraData) pure returns(bytes32 uuid_)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) GetGameUUID(_gameType uint32, _rootClaim [32]byte, _extraData []byte) ([32]byte, error) {
return _DisputeGameFactory.Contract.GetGameUUID(&_DisputeGameFactory.CallOpts, _gameType, _rootClaim, _extraData)
}
// InitBonds is a free data retrieval call binding the contract method 0x6593dc6e.
//
// Solidity: function initBonds(uint32 ) view returns(uint256)
func (_DisputeGameFactory *DisputeGameFactoryCaller) InitBonds(opts *bind.CallOpts, arg0 uint32) (*big.Int, error) {
var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "initBonds", arg0)
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// InitBonds is a free data retrieval call binding the contract method 0x6593dc6e.
//
// Solidity: function initBonds(uint32 ) view returns(uint256)
func (_DisputeGameFactory *DisputeGameFactorySession) InitBonds(arg0 uint32) (*big.Int, error) {
return _DisputeGameFactory.Contract.InitBonds(&_DisputeGameFactory.CallOpts, arg0)
}
// InitBonds is a free data retrieval call binding the contract method 0x6593dc6e.
//
// Solidity: function initBonds(uint32 ) view returns(uint256)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) InitBonds(arg0 uint32) (*big.Int, error) {
return _DisputeGameFactory.Contract.InitBonds(&_DisputeGameFactory.CallOpts, arg0)
}
// Owner is a free data retrieval call binding the contract method 0x8da5cb5b.
//
// Solidity: function owner() view returns(address)
func (_DisputeGameFactory *DisputeGameFactoryCaller) Owner(opts *bind.CallOpts) (common.Address, error) {
var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "owner")
if err != nil {
return *new(common.Address), err
}
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
return out0, err
}
// Owner is a free data retrieval call binding the contract method 0x8da5cb5b.
//
// Solidity: function owner() view returns(address)
func (_DisputeGameFactory *DisputeGameFactorySession) Owner() (common.Address, error) {
return _DisputeGameFactory.Contract.Owner(&_DisputeGameFactory.CallOpts)
}
// Owner is a free data retrieval call binding the contract method 0x8da5cb5b.
//
// Solidity: function owner() view returns(address)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) Owner() (common.Address, error) {
return _DisputeGameFactory.Contract.Owner(&_DisputeGameFactory.CallOpts)
}
// Version is a free data retrieval call binding the contract method 0x54fd4d50.
//
// Solidity: function version() view returns(string)
func (_DisputeGameFactory *DisputeGameFactoryCaller) Version(opts *bind.CallOpts) (string, error) {
var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "version")
if err != nil {
return *new(string), err
}
out0 := *abi.ConvertType(out[0], new(string)).(*string)
return out0, err
}
// Version is a free data retrieval call binding the contract method 0x54fd4d50.
//
// Solidity: function version() view returns(string)
func (_DisputeGameFactory *DisputeGameFactorySession) Version() (string, error) {
return _DisputeGameFactory.Contract.Version(&_DisputeGameFactory.CallOpts)
}
// Version is a free data retrieval call binding the contract method 0x54fd4d50.
//
// Solidity: function version() view returns(string)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) Version() (string, error) {
return _DisputeGameFactory.Contract.Version(&_DisputeGameFactory.CallOpts)
}
// Create is a paid mutator transaction binding the contract method 0x82ecf2f6.
//
// Solidity: function create(uint32 _gameType, bytes32 _rootClaim, bytes _extraData) payable returns(address proxy_)
func (_DisputeGameFactory *DisputeGameFactoryTransactor) Create(opts *bind.TransactOpts, _gameType uint32, _rootClaim [32]byte, _extraData []byte) (*types.Transaction, error) {
return _DisputeGameFactory.contract.Transact(opts, "create", _gameType, _rootClaim, _extraData)
}
// Create is a paid mutator transaction binding the contract method 0x82ecf2f6.
//
// Solidity: function create(uint32 _gameType, bytes32 _rootClaim, bytes _extraData) payable returns(address proxy_)
func (_DisputeGameFactory *DisputeGameFactorySession) Create(_gameType uint32, _rootClaim [32]byte, _extraData []byte) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.Create(&_DisputeGameFactory.TransactOpts, _gameType, _rootClaim, _extraData)
}
// Create is a paid mutator transaction binding the contract method 0x82ecf2f6.
//
// Solidity: function create(uint32 _gameType, bytes32 _rootClaim, bytes _extraData) payable returns(address proxy_)
func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) Create(_gameType uint32, _rootClaim [32]byte, _extraData []byte) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.Create(&_DisputeGameFactory.TransactOpts, _gameType, _rootClaim, _extraData)
}
// Initialize is a paid mutator transaction binding the contract method 0xc4d66de8.
//
// Solidity: function initialize(address _owner) returns()
func (_DisputeGameFactory *DisputeGameFactoryTransactor) Initialize(opts *bind.TransactOpts, _owner common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.contract.Transact(opts, "initialize", _owner)
}
// Initialize is a paid mutator transaction binding the contract method 0xc4d66de8.
//
// Solidity: function initialize(address _owner) returns()
func (_DisputeGameFactory *DisputeGameFactorySession) Initialize(_owner common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.Initialize(&_DisputeGameFactory.TransactOpts, _owner)
}
// Initialize is a paid mutator transaction binding the contract method 0xc4d66de8.
//
// Solidity: function initialize(address _owner) returns()
func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) Initialize(_owner common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.Initialize(&_DisputeGameFactory.TransactOpts, _owner)
}
// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6.
//
// Solidity: function renounceOwnership() returns()
func (_DisputeGameFactory *DisputeGameFactoryTransactor) RenounceOwnership(opts *bind.TransactOpts) (*types.Transaction, error) {
return _DisputeGameFactory.contract.Transact(opts, "renounceOwnership")
}
// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6.
//
// Solidity: function renounceOwnership() returns()
func (_DisputeGameFactory *DisputeGameFactorySession) RenounceOwnership() (*types.Transaction, error) {
return _DisputeGameFactory.Contract.RenounceOwnership(&_DisputeGameFactory.TransactOpts)
}
// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6.
//
// Solidity: function renounceOwnership() returns()
func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) RenounceOwnership() (*types.Transaction, error) {
return _DisputeGameFactory.Contract.RenounceOwnership(&_DisputeGameFactory.TransactOpts)
}
// SetImplementation is a paid mutator transaction binding the contract method 0x14f6b1a3.
//
// Solidity: function setImplementation(uint32 _gameType, address _impl) returns()
func (_DisputeGameFactory *DisputeGameFactoryTransactor) SetImplementation(opts *bind.TransactOpts, _gameType uint32, _impl common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.contract.Transact(opts, "setImplementation", _gameType, _impl)
}
// SetImplementation is a paid mutator transaction binding the contract method 0x14f6b1a3.
//
// Solidity: function setImplementation(uint32 _gameType, address _impl) returns()
func (_DisputeGameFactory *DisputeGameFactorySession) SetImplementation(_gameType uint32, _impl common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.SetImplementation(&_DisputeGameFactory.TransactOpts, _gameType, _impl)
}
// SetImplementation is a paid mutator transaction binding the contract method 0x14f6b1a3.
//
// Solidity: function setImplementation(uint32 _gameType, address _impl) returns()
func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) SetImplementation(_gameType uint32, _impl common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.SetImplementation(&_DisputeGameFactory.TransactOpts, _gameType, _impl)
}
// SetInitBond is a paid mutator transaction binding the contract method 0x1e334240.
//
// Solidity: function setInitBond(uint32 _gameType, uint256 _initBond) returns()
func (_DisputeGameFactory *DisputeGameFactoryTransactor) SetInitBond(opts *bind.TransactOpts, _gameType uint32, _initBond *big.Int) (*types.Transaction, error) {
return _DisputeGameFactory.contract.Transact(opts, "setInitBond", _gameType, _initBond)
}
// SetInitBond is a paid mutator transaction binding the contract method 0x1e334240.
//
// Solidity: function setInitBond(uint32 _gameType, uint256 _initBond) returns()
func (_DisputeGameFactory *DisputeGameFactorySession) SetInitBond(_gameType uint32, _initBond *big.Int) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.SetInitBond(&_DisputeGameFactory.TransactOpts, _gameType, _initBond)
}
// SetInitBond is a paid mutator transaction binding the contract method 0x1e334240.
//
// Solidity: function setInitBond(uint32 _gameType, uint256 _initBond) returns()
func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) SetInitBond(_gameType uint32, _initBond *big.Int) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.SetInitBond(&_DisputeGameFactory.TransactOpts, _gameType, _initBond)
}
// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b.
//
// Solidity: function transferOwnership(address newOwner) returns()
func (_DisputeGameFactory *DisputeGameFactoryTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.contract.Transact(opts, "transferOwnership", newOwner)
}
// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b.
//
// Solidity: function transferOwnership(address newOwner) returns()
func (_DisputeGameFactory *DisputeGameFactorySession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.TransferOwnership(&_DisputeGameFactory.TransactOpts, newOwner)
}
// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b.
//
// Solidity: function transferOwnership(address newOwner) returns()
func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.TransferOwnership(&_DisputeGameFactory.TransactOpts, newOwner)
}
// DisputeGameFactoryDisputeGameCreatedIterator is returned from FilterDisputeGameCreated and is used to iterate over the raw logs and unpacked data for DisputeGameCreated events raised by the DisputeGameFactory contract.
type DisputeGameFactoryDisputeGameCreatedIterator struct {
Event *DisputeGameFactoryDisputeGameCreated // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *DisputeGameFactoryDisputeGameCreatedIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(DisputeGameFactoryDisputeGameCreated)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(DisputeGameFactoryDisputeGameCreated)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *DisputeGameFactoryDisputeGameCreatedIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *DisputeGameFactoryDisputeGameCreatedIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// DisputeGameFactoryDisputeGameCreated represents a DisputeGameCreated event raised by the DisputeGameFactory contract.
type DisputeGameFactoryDisputeGameCreated struct {
DisputeProxy common.Address
GameType uint32
RootClaim [32]byte
Raw types.Log // Blockchain specific contextual infos
}
// FilterDisputeGameCreated is a free log retrieval operation binding the contract event 0x5b565efe82411da98814f356d0e7bcb8f0219b8d970307c5afb4a6903a8b2e35.
//
// Solidity: event DisputeGameCreated(address indexed disputeProxy, uint32 indexed gameType, bytes32 indexed rootClaim)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) FilterDisputeGameCreated(opts *bind.FilterOpts, disputeProxy []common.Address, gameType []uint32, rootClaim [][32]byte) (*DisputeGameFactoryDisputeGameCreatedIterator, error) {
var disputeProxyRule []interface{}
for _, disputeProxyItem := range disputeProxy {
disputeProxyRule = append(disputeProxyRule, disputeProxyItem)
}
var gameTypeRule []interface{}
for _, gameTypeItem := range gameType {
gameTypeRule = append(gameTypeRule, gameTypeItem)
}
var rootClaimRule []interface{}
for _, rootClaimItem := range rootClaim {
rootClaimRule = append(rootClaimRule, rootClaimItem)
}
logs, sub, err := _DisputeGameFactory.contract.FilterLogs(opts, "DisputeGameCreated", disputeProxyRule, gameTypeRule, rootClaimRule)
if err != nil {
return nil, err
}
return &DisputeGameFactoryDisputeGameCreatedIterator{contract: _DisputeGameFactory.contract, event: "DisputeGameCreated", logs: logs, sub: sub}, nil
}
// WatchDisputeGameCreated is a free log subscription operation binding the contract event 0x5b565efe82411da98814f356d0e7bcb8f0219b8d970307c5afb4a6903a8b2e35.
//
// Solidity: event DisputeGameCreated(address indexed disputeProxy, uint32 indexed gameType, bytes32 indexed rootClaim)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) WatchDisputeGameCreated(opts *bind.WatchOpts, sink chan<- *DisputeGameFactoryDisputeGameCreated, disputeProxy []common.Address, gameType []uint32, rootClaim [][32]byte) (event.Subscription, error) {
var disputeProxyRule []interface{}
for _, disputeProxyItem := range disputeProxy {
disputeProxyRule = append(disputeProxyRule, disputeProxyItem)
}
var gameTypeRule []interface{}
for _, gameTypeItem := range gameType {
gameTypeRule = append(gameTypeRule, gameTypeItem)
}
var rootClaimRule []interface{}
for _, rootClaimItem := range rootClaim {
rootClaimRule = append(rootClaimRule, rootClaimItem)
}
logs, sub, err := _DisputeGameFactory.contract.WatchLogs(opts, "DisputeGameCreated", disputeProxyRule, gameTypeRule, rootClaimRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(DisputeGameFactoryDisputeGameCreated)
if err := _DisputeGameFactory.contract.UnpackLog(event, "DisputeGameCreated", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseDisputeGameCreated is a log parse operation binding the contract event 0x5b565efe82411da98814f356d0e7bcb8f0219b8d970307c5afb4a6903a8b2e35.
//
// Solidity: event DisputeGameCreated(address indexed disputeProxy, uint32 indexed gameType, bytes32 indexed rootClaim)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) ParseDisputeGameCreated(log types.Log) (*DisputeGameFactoryDisputeGameCreated, error) {
event := new(DisputeGameFactoryDisputeGameCreated)
if err := _DisputeGameFactory.contract.UnpackLog(event, "DisputeGameCreated", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// DisputeGameFactoryImplementationSetIterator is returned from FilterImplementationSet and is used to iterate over the raw logs and unpacked data for ImplementationSet events raised by the DisputeGameFactory contract.
type DisputeGameFactoryImplementationSetIterator struct {
Event *DisputeGameFactoryImplementationSet // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *DisputeGameFactoryImplementationSetIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(DisputeGameFactoryImplementationSet)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(DisputeGameFactoryImplementationSet)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *DisputeGameFactoryImplementationSetIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *DisputeGameFactoryImplementationSetIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// DisputeGameFactoryImplementationSet represents a ImplementationSet event raised by the DisputeGameFactory contract.
type DisputeGameFactoryImplementationSet struct {
Impl common.Address
GameType uint32
Raw types.Log // Blockchain specific contextual infos
}
// FilterImplementationSet is a free log retrieval operation binding the contract event 0xff513d80e2c7fa487608f70a618dfbc0cf415699dc69588c747e8c71566c88de.
//
// Solidity: event ImplementationSet(address indexed impl, uint32 indexed gameType)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) FilterImplementationSet(opts *bind.FilterOpts, impl []common.Address, gameType []uint32) (*DisputeGameFactoryImplementationSetIterator, error) {
var implRule []interface{}
for _, implItem := range impl {
implRule = append(implRule, implItem)
}
var gameTypeRule []interface{}
for _, gameTypeItem := range gameType {
gameTypeRule = append(gameTypeRule, gameTypeItem)
}
logs, sub, err := _DisputeGameFactory.contract.FilterLogs(opts, "ImplementationSet", implRule, gameTypeRule)
if err != nil {
return nil, err
}
return &DisputeGameFactoryImplementationSetIterator{contract: _DisputeGameFactory.contract, event: "ImplementationSet", logs: logs, sub: sub}, nil
}
// WatchImplementationSet is a free log subscription operation binding the contract event 0xff513d80e2c7fa487608f70a618dfbc0cf415699dc69588c747e8c71566c88de.
//
// Solidity: event ImplementationSet(address indexed impl, uint32 indexed gameType)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) WatchImplementationSet(opts *bind.WatchOpts, sink chan<- *DisputeGameFactoryImplementationSet, impl []common.Address, gameType []uint32) (event.Subscription, error) {
var implRule []interface{}
for _, implItem := range impl {
implRule = append(implRule, implItem)
}
var gameTypeRule []interface{}
for _, gameTypeItem := range gameType {
gameTypeRule = append(gameTypeRule, gameTypeItem)
}
logs, sub, err := _DisputeGameFactory.contract.WatchLogs(opts, "ImplementationSet", implRule, gameTypeRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(DisputeGameFactoryImplementationSet)
if err := _DisputeGameFactory.contract.UnpackLog(event, "ImplementationSet", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseImplementationSet is a log parse operation binding the contract event 0xff513d80e2c7fa487608f70a618dfbc0cf415699dc69588c747e8c71566c88de.
//
// Solidity: event ImplementationSet(address indexed impl, uint32 indexed gameType)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) ParseImplementationSet(log types.Log) (*DisputeGameFactoryImplementationSet, error) {
event := new(DisputeGameFactoryImplementationSet)
if err := _DisputeGameFactory.contract.UnpackLog(event, "ImplementationSet", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// DisputeGameFactoryInitBondUpdatedIterator is returned from FilterInitBondUpdated and is used to iterate over the raw logs and unpacked data for InitBondUpdated events raised by the DisputeGameFactory contract.
type DisputeGameFactoryInitBondUpdatedIterator struct {
Event *DisputeGameFactoryInitBondUpdated // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *DisputeGameFactoryInitBondUpdatedIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(DisputeGameFactoryInitBondUpdated)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(DisputeGameFactoryInitBondUpdated)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *DisputeGameFactoryInitBondUpdatedIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *DisputeGameFactoryInitBondUpdatedIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// DisputeGameFactoryInitBondUpdated represents a InitBondUpdated event raised by the DisputeGameFactory contract.
type DisputeGameFactoryInitBondUpdated struct {
GameType uint32
NewBond *big.Int
Raw types.Log // Blockchain specific contextual infos
}
// FilterInitBondUpdated is a free log retrieval operation binding the contract event 0x74d6665c4b26d5596a5aa13d3014e0c06af4d322075a797f87b03cd4c5bc91ca.
//
// Solidity: event InitBondUpdated(uint32 indexed gameType, uint256 indexed newBond)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) FilterInitBondUpdated(opts *bind.FilterOpts, gameType []uint32, newBond []*big.Int) (*DisputeGameFactoryInitBondUpdatedIterator, error) {
var gameTypeRule []interface{}
for _, gameTypeItem := range gameType {
gameTypeRule = append(gameTypeRule, gameTypeItem)
}
var newBondRule []interface{}
for _, newBondItem := range newBond {
newBondRule = append(newBondRule, newBondItem)
}
logs, sub, err := _DisputeGameFactory.contract.FilterLogs(opts, "InitBondUpdated", gameTypeRule, newBondRule)
if err != nil {
return nil, err
}
return &DisputeGameFactoryInitBondUpdatedIterator{contract: _DisputeGameFactory.contract, event: "InitBondUpdated", logs: logs, sub: sub}, nil
}
// WatchInitBondUpdated is a free log subscription operation binding the contract event 0x74d6665c4b26d5596a5aa13d3014e0c06af4d322075a797f87b03cd4c5bc91ca.
//
// Solidity: event InitBondUpdated(uint32 indexed gameType, uint256 indexed newBond)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) WatchInitBondUpdated(opts *bind.WatchOpts, sink chan<- *DisputeGameFactoryInitBondUpdated, gameType []uint32, newBond []*big.Int) (event.Subscription, error) {
var gameTypeRule []interface{}
for _, gameTypeItem := range gameType {
gameTypeRule = append(gameTypeRule, gameTypeItem)
}
var newBondRule []interface{}
for _, newBondItem := range newBond {
newBondRule = append(newBondRule, newBondItem)
}
logs, sub, err := _DisputeGameFactory.contract.WatchLogs(opts, "InitBondUpdated", gameTypeRule, newBondRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(DisputeGameFactoryInitBondUpdated)
if err := _DisputeGameFactory.contract.UnpackLog(event, "InitBondUpdated", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseInitBondUpdated is a log parse operation binding the contract event 0x74d6665c4b26d5596a5aa13d3014e0c06af4d322075a797f87b03cd4c5bc91ca.
//
// Solidity: event InitBondUpdated(uint32 indexed gameType, uint256 indexed newBond)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) ParseInitBondUpdated(log types.Log) (*DisputeGameFactoryInitBondUpdated, error) {
event := new(DisputeGameFactoryInitBondUpdated)
if err := _DisputeGameFactory.contract.UnpackLog(event, "InitBondUpdated", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// DisputeGameFactoryInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the DisputeGameFactory contract.
type DisputeGameFactoryInitializedIterator struct {
Event *DisputeGameFactoryInitialized // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *DisputeGameFactoryInitializedIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(DisputeGameFactoryInitialized)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(DisputeGameFactoryInitialized)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *DisputeGameFactoryInitializedIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *DisputeGameFactoryInitializedIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// DisputeGameFactoryInitialized represents a Initialized event raised by the DisputeGameFactory contract.
type DisputeGameFactoryInitialized struct {
Version uint8
Raw types.Log // Blockchain specific contextual infos
}
// FilterInitialized is a free log retrieval operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498.
//
// Solidity: event Initialized(uint8 version)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) FilterInitialized(opts *bind.FilterOpts) (*DisputeGameFactoryInitializedIterator, error) {
logs, sub, err := _DisputeGameFactory.contract.FilterLogs(opts, "Initialized")
if err != nil {
return nil, err
}
return &DisputeGameFactoryInitializedIterator{contract: _DisputeGameFactory.contract, event: "Initialized", logs: logs, sub: sub}, nil
}
// WatchInitialized is a free log subscription operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498.
//
// Solidity: event Initialized(uint8 version)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *DisputeGameFactoryInitialized) (event.Subscription, error) {
logs, sub, err := _DisputeGameFactory.contract.WatchLogs(opts, "Initialized")
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(DisputeGameFactoryInitialized)
if err := _DisputeGameFactory.contract.UnpackLog(event, "Initialized", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseInitialized is a log parse operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498.
//
// Solidity: event Initialized(uint8 version)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) ParseInitialized(log types.Log) (*DisputeGameFactoryInitialized, error) {
event := new(DisputeGameFactoryInitialized)
if err := _DisputeGameFactory.contract.UnpackLog(event, "Initialized", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// DisputeGameFactoryOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the DisputeGameFactory contract.
type DisputeGameFactoryOwnershipTransferredIterator struct {
Event *DisputeGameFactoryOwnershipTransferred // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *DisputeGameFactoryOwnershipTransferredIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(DisputeGameFactoryOwnershipTransferred)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(DisputeGameFactoryOwnershipTransferred)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *DisputeGameFactoryOwnershipTransferredIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *DisputeGameFactoryOwnershipTransferredIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// DisputeGameFactoryOwnershipTransferred represents a OwnershipTransferred event raised by the DisputeGameFactory contract.
type DisputeGameFactoryOwnershipTransferred struct {
PreviousOwner common.Address
NewOwner common.Address
Raw types.Log // Blockchain specific contextual infos
}
// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0.
//
// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*DisputeGameFactoryOwnershipTransferredIterator, error) {
var previousOwnerRule []interface{}
for _, previousOwnerItem := range previousOwner {
previousOwnerRule = append(previousOwnerRule, previousOwnerItem)
}
var newOwnerRule []interface{}
for _, newOwnerItem := range newOwner {
newOwnerRule = append(newOwnerRule, newOwnerItem)
}
logs, sub, err := _DisputeGameFactory.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule)
if err != nil {
return nil, err
}
return &DisputeGameFactoryOwnershipTransferredIterator{contract: _DisputeGameFactory.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil
}
// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0.
//
// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *DisputeGameFactoryOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) {
var previousOwnerRule []interface{}
for _, previousOwnerItem := range previousOwner {
previousOwnerRule = append(previousOwnerRule, previousOwnerItem)
}
var newOwnerRule []interface{}
for _, newOwnerItem := range newOwner {
newOwnerRule = append(newOwnerRule, newOwnerItem)
}
logs, sub, err := _DisputeGameFactory.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(DisputeGameFactoryOwnershipTransferred)
if err := _DisputeGameFactory.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0.
//
// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) ParseOwnershipTransferred(log types.Log) (*DisputeGameFactoryOwnershipTransferred, error) {
event := new(DisputeGameFactoryOwnershipTransferred)
if err := _DisputeGameFactory.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
package contracts
import (
"context"
"fmt"
"math/big"
"time"
"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/txmgr"
"github.com/ethereum-optimism/optimism/packages/contracts-bedrock/snapshots"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
)
const (
methodGameCount = "gameCount"
methodGameAtIndex = "gameAtIndex"
methodInitBonds = "initBonds"
methodCreateGame = "create"
methodVersion = "version"
methodClaim = "claimData"
)
type gameMetadata struct {
GameType uint32
Timestamp time.Time
Address common.Address
Proposer common.Address
}
type DisputeGameFactory struct {
caller *batching.MultiCaller
contract *batching.BoundContract
gameABI *abi.ABI
networkTimeout time.Duration
}
func NewDisputeGameFactory(addr common.Address, caller *batching.MultiCaller, networkTimeout time.Duration) *DisputeGameFactory {
factoryABI := snapshots.LoadDisputeGameFactoryABI()
gameABI := snapshots.LoadFaultDisputeGameABI()
return &DisputeGameFactory{
caller: caller,
contract: batching.NewBoundContract(factoryABI, addr),
gameABI: gameABI,
networkTimeout: networkTimeout,
}
}
func (f *DisputeGameFactory) Version(ctx context.Context) (string, error) {
cCtx, cancel := context.WithTimeout(ctx, f.networkTimeout)
defer cancel()
result, err := f.caller.SingleCall(cCtx, rpcblock.Latest, f.contract.Call(methodVersion))
if err != nil {
return "", fmt.Errorf("failed to get version: %w", err)
}
return result.GetString(0), nil
}
// HasProposedSince attempts to find a game with the specified game type created by the specified proposer after the
// given cut off time. If one is found, returns true and the time the game was created at.
// If no matching proposal is found, returns false, time.Time{}, nil
func (f *DisputeGameFactory) HasProposedSince(ctx context.Context, proposer common.Address, cutoff time.Time, gameType uint32) (bool, time.Time, error) {
gameCount, err := f.gameCount(ctx)
if err != nil {
return false, time.Time{}, fmt.Errorf("failed to get dispute game count: %w", err)
}
if gameCount == 0 {
return false, time.Time{}, nil
}
for idx := gameCount - 1; ; idx-- {
game, err := f.gameAtIndex(ctx, idx)
if err != nil {
return false, time.Time{}, fmt.Errorf("failed to get dispute game %d: %w", idx, err)
}
if game.Timestamp.Before(cutoff) {
// Reached a game that is before the expected cutoff, so we haven't found a suitable proposal
return false, time.Time{}, nil
}
if game.GameType == gameType && game.Proposer == proposer {
// Found a matching proposal
return true, game.Timestamp, nil
}
if idx == 0 { // Need to check here rather than in the for condition to avoid underflow
// Checked every game and didn't find a match
return false, time.Time{}, nil
}
}
}
func (f *DisputeGameFactory) ProposalTx(ctx context.Context, gameType uint32, outputRoot common.Hash, l2BlockNum uint64) (txmgr.TxCandidate, error) {
cCtx, cancel := context.WithTimeout(ctx, f.networkTimeout)
defer cancel()
result, err := f.caller.SingleCall(cCtx, rpcblock.Latest, f.contract.Call(methodInitBonds, gameType))
if err != nil {
return txmgr.TxCandidate{}, fmt.Errorf("failed to fetch init bond: %w", err)
}
initBond := result.GetBigInt(0)
call := f.contract.Call(methodCreateGame, gameType, outputRoot, common.BigToHash(big.NewInt(int64(l2BlockNum))).Bytes())
candidate, err := call.ToTxCandidate()
if err != nil {
return txmgr.TxCandidate{}, err
}
candidate.Value = initBond
return candidate, err
}
func (f *DisputeGameFactory) gameCount(ctx context.Context) (uint64, error) {
cCtx, cancel := context.WithTimeout(ctx, f.networkTimeout)
defer cancel()
result, err := f.caller.SingleCall(cCtx, rpcblock.Latest, f.contract.Call(methodGameCount))
if err != nil {
return 0, fmt.Errorf("failed to load game count: %w", err)
}
return result.GetBigInt(0).Uint64(), nil
}
func (f *DisputeGameFactory) gameAtIndex(ctx context.Context, idx uint64) (gameMetadata, error) {
cCtx, cancel := context.WithTimeout(ctx, f.networkTimeout)
defer cancel()
result, err := f.caller.SingleCall(cCtx, rpcblock.Latest, f.contract.Call(methodGameAtIndex, new(big.Int).SetUint64(idx)))
if err != nil {
return gameMetadata{}, fmt.Errorf("failed to load game %v: %w", idx, err)
}
gameType := result.GetUint32(0)
timestamp := result.GetUint64(1)
address := result.GetAddress(2)
gameContract := batching.NewBoundContract(f.gameABI, address)
cCtx, cancel = context.WithTimeout(ctx, f.networkTimeout)
defer cancel()
result, err = f.caller.SingleCall(cCtx, rpcblock.Latest, gameContract.Call(methodClaim, big.NewInt(0)))
if err != nil {
return gameMetadata{}, fmt.Errorf("failed to load root claim of game %v: %w", idx, err)
}
// We don't need most of the claim data, only the claimant which is the game proposer
claimant := result.GetAddress(2)
return gameMetadata{
GameType: gameType,
Timestamp: time.Unix(int64(timestamp), 0),
Address: address,
Proposer: claimant,
}, nil
}
package contracts
import (
"context"
"math"
"math/big"
"testing"
"time"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
batchingTest "github.com/ethereum-optimism/optimism/op-service/sources/batching/test"
"github.com/ethereum-optimism/optimism/packages/contracts-bedrock/snapshots"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
var factoryAddr = common.Address{0xff, 0xff}
var proposerAddr = common.Address{0xaa, 0xbb}
func TestHasProposedSince(t *testing.T) {
cutOffTime := time.Unix(1000, 0)
t.Run("NoProposals", func(t *testing.T) {
stubRpc, factory := setupDisputeGameFactoryTest(t)
withClaims(stubRpc)
proposed, proposalTime, err := factory.HasProposedSince(context.Background(), proposerAddr, cutOffTime, 0)
require.NoError(t, err)
require.False(t, proposed)
require.Equal(t, time.Time{}, proposalTime)
})
t.Run("NoMatchingProposal", func(t *testing.T) {
stubRpc, factory := setupDisputeGameFactoryTest(t)
withClaims(
stubRpc,
gameMetadata{
GameType: 0,
Timestamp: time.Unix(1600, 0),
Address: common.Address{0x22},
Proposer: common.Address{0xee}, // Wrong proposer
},
gameMetadata{
GameType: 1, // Wrong game type
Timestamp: time.Unix(1700, 0),
Address: common.Address{0x33},
Proposer: proposerAddr,
},
)
proposed, proposalTime, err := factory.HasProposedSince(context.Background(), proposerAddr, cutOffTime, 0)
require.NoError(t, err)
require.False(t, proposed)
require.Equal(t, time.Time{}, proposalTime)
})
t.Run("MatchingProposalBeforeCutOff", func(t *testing.T) {
stubRpc, factory := setupDisputeGameFactoryTest(t)
withClaims(
stubRpc,
gameMetadata{
GameType: 0,
Timestamp: time.Unix(999, 0),
Address: common.Address{0x11},
Proposer: proposerAddr,
},
gameMetadata{
GameType: 0,
Timestamp: time.Unix(1600, 0),
Address: common.Address{0x22},
Proposer: common.Address{0xee}, // Wrong proposer
},
gameMetadata{
GameType: 1, // Wrong game type
Timestamp: time.Unix(1700, 0),
Address: common.Address{0x33},
Proposer: proposerAddr,
},
)
proposed, proposalTime, err := factory.HasProposedSince(context.Background(), proposerAddr, cutOffTime, 0)
require.NoError(t, err)
require.False(t, proposed)
require.Equal(t, time.Time{}, proposalTime)
})
t.Run("MatchingProposalAtCutOff", func(t *testing.T) {
stubRpc, factory := setupDisputeGameFactoryTest(t)
withClaims(
stubRpc,
gameMetadata{
GameType: 0,
Timestamp: cutOffTime,
Address: common.Address{0x11},
Proposer: proposerAddr,
},
gameMetadata{
GameType: 0,
Timestamp: time.Unix(1600, 0),
Address: common.Address{0x22},
Proposer: common.Address{0xee}, // Wrong proposer
},
gameMetadata{
GameType: 1, // Wrong game type
Timestamp: time.Unix(1700, 0),
Address: common.Address{0x33},
Proposer: proposerAddr,
},
)
proposed, proposalTime, err := factory.HasProposedSince(context.Background(), proposerAddr, cutOffTime, 0)
require.NoError(t, err)
require.True(t, proposed)
require.Equal(t, cutOffTime, proposalTime)
})
t.Run("MatchingProposalAfterCutOff", func(t *testing.T) {
stubRpc, factory := setupDisputeGameFactoryTest(t)
expectedProposalTime := time.Unix(1100, 0)
withClaims(
stubRpc,
gameMetadata{
GameType: 0,
Timestamp: expectedProposalTime,
Address: common.Address{0x11},
Proposer: proposerAddr,
},
gameMetadata{
GameType: 0,
Timestamp: time.Unix(1600, 0),
Address: common.Address{0x22},
Proposer: common.Address{0xee}, // Wrong proposer
},
gameMetadata{
GameType: 1, // Wrong game type
Timestamp: time.Unix(1700, 0),
Address: common.Address{0x33},
Proposer: proposerAddr,
},
)
proposed, proposalTime, err := factory.HasProposedSince(context.Background(), proposerAddr, cutOffTime, 0)
require.NoError(t, err)
require.True(t, proposed)
require.Equal(t, expectedProposalTime, proposalTime)
})
t.Run("MultipleMatchingProposalAfterCutOff", func(t *testing.T) {
stubRpc, factory := setupDisputeGameFactoryTest(t)
expectedProposalTime := time.Unix(1600, 0)
withClaims(
stubRpc,
gameMetadata{
GameType: 0,
Timestamp: time.Unix(1400, 0),
Address: common.Address{0x11},
Proposer: proposerAddr,
},
gameMetadata{
GameType: 0,
Timestamp: time.Unix(1500, 0),
Address: common.Address{0x22},
Proposer: proposerAddr,
},
gameMetadata{
GameType: 0,
Timestamp: expectedProposalTime,
Address: common.Address{0x33},
Proposer: proposerAddr,
},
)
proposed, proposalTime, err := factory.HasProposedSince(context.Background(), proposerAddr, cutOffTime, 0)
require.NoError(t, err)
require.True(t, proposed)
// Should find the most recent proposal
require.Equal(t, expectedProposalTime, proposalTime)
})
}
func TestProposalTx(t *testing.T) {
stubRpc, factory := setupDisputeGameFactoryTest(t)
traceType := uint32(123)
outputRoot := common.Hash{0x01}
l2BlockNum := common.BigToHash(big.NewInt(456)).Bytes()
bond := big.NewInt(49284294829)
stubRpc.SetResponse(factoryAddr, methodInitBonds, rpcblock.Latest, []interface{}{traceType}, []interface{}{bond})
stubRpc.SetResponse(factoryAddr, methodCreateGame, rpcblock.Latest, []interface{}{traceType, outputRoot, l2BlockNum}, nil)
tx, err := factory.ProposalTx(context.Background(), traceType, outputRoot, uint64(456))
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
require.NotNil(t, tx.Value)
require.Truef(t, bond.Cmp(tx.Value) == 0, "Expected bond %v but was %v", bond, tx.Value)
}
func withClaims(stubRpc *batchingTest.AbiBasedRpc, games ...gameMetadata) {
gameAbi := snapshots.LoadFaultDisputeGameABI()
stubRpc.SetResponse(factoryAddr, methodGameCount, rpcblock.Latest, nil, []interface{}{big.NewInt(int64(len(games)))})
for i, game := range games {
stubRpc.SetResponse(factoryAddr, methodGameAtIndex, rpcblock.Latest, []interface{}{big.NewInt(int64(i))}, []interface{}{
game.GameType,
uint64(game.Timestamp.Unix()),
game.Address,
})
stubRpc.AddContract(game.Address, gameAbi)
stubRpc.SetResponse(game.Address, methodClaim, rpcblock.Latest, []interface{}{big.NewInt(0)}, []interface{}{
uint32(math.MaxUint32), // Parent address (none for root claim)
common.Address{}, // Countered by
game.Proposer, // Claimant
big.NewInt(1000), // Bond
common.Hash{0xdd}, // Claim
big.NewInt(1), // Position (gindex 1 for root position)
big.NewInt(100), // Clock
})
}
}
func setupDisputeGameFactoryTest(t *testing.T) (*batchingTest.AbiBasedRpc, *DisputeGameFactory) {
fdgAbi := snapshots.LoadDisputeGameFactoryABI()
stubRpc := batchingTest.NewAbiBasedRpc(t, factoryAddr, fdgAbi)
caller := batching.NewMultiCaller(stubRpc, batching.DefaultBatchSize)
factory := NewDisputeGameFactory(factoryAddr, caller, time.Minute)
return stubRpc, factory
}
...@@ -60,12 +60,6 @@ var ( ...@@ -60,12 +60,6 @@ var (
Usage: "Interval between submitting L2 output proposals when the dispute game factory address is set", Usage: "Interval between submitting L2 output proposals when the dispute game factory address is set",
EnvVars: prefixEnvVars("PROPOSAL_INTERVAL"), EnvVars: prefixEnvVars("PROPOSAL_INTERVAL"),
} }
OutputRetryIntervalFlag = &cli.DurationFlag{
Name: "output-retry-interval",
Usage: "Interval between retrying output fetching (DGF)",
Value: 12 * time.Second,
EnvVars: prefixEnvVars("OUTPUT_RETRY_INTERVAL"),
}
DisputeGameTypeFlag = &cli.UintFlag{ DisputeGameTypeFlag = &cli.UintFlag{
Name: "game-type", Name: "game-type",
Usage: "Dispute game type to create via the configured DisputeGameFactory", Usage: "Dispute game type to create via the configured DisputeGameFactory",
...@@ -101,7 +95,6 @@ var optionalFlags = []cli.Flag{ ...@@ -101,7 +95,6 @@ var optionalFlags = []cli.Flag{
L2OutputHDPathFlag, L2OutputHDPathFlag,
DisputeGameFactoryAddressFlag, DisputeGameFactoryAddressFlag,
ProposalIntervalFlag, ProposalIntervalFlag,
OutputRetryIntervalFlag,
DisputeGameTypeFlag, DisputeGameTypeFlag,
ActiveSequencerCheckDurationFlag, ActiveSequencerCheckDurationFlag,
WaitNodeSyncFlag, WaitNodeSyncFlag,
......
...@@ -11,7 +11,6 @@ import ( ...@@ -11,7 +11,6 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
...@@ -46,23 +45,6 @@ func setupL2OutputOracle() (common.Address, *bind.TransactOpts, *backends.Simula ...@@ -46,23 +45,6 @@ func setupL2OutputOracle() (common.Address, *bind.TransactOpts, *backends.Simula
return from, opts, backend, contract, nil return from, opts, backend, contract, nil
} }
// setupDisputeGameFactory deploys the DisputeGameFactory contract to a simulated backend
func setupDisputeGameFactory() (common.Address, *bind.TransactOpts, *backends.SimulatedBackend, *bindings.DisputeGameFactory, error) {
_, from, opts, backend, err := simulatedBackend()
if err != nil {
return common.Address{}, nil, nil, nil, err
}
_, _, contract, err := bindings.DeployDisputeGameFactory(
opts,
backend,
)
if err != nil {
return common.Address{}, nil, nil, nil, err
}
return from, opts, backend, contract, nil
}
// TestManualABIPacking ensure that the manual ABI packing is the same as going through the bound contract. // TestManualABIPacking ensure that the manual ABI packing is the same as going through the bound contract.
// We don't use the contract to transact because it does not fit our transaction management scheme, but // We don't use the contract to transact because it does not fit our transaction management scheme, but
// we want to make sure that we don't incorrectly create the transaction data. // we want to make sure that we don't incorrectly create the transaction data.
...@@ -92,28 +74,4 @@ func TestManualABIPacking(t *testing.T) { ...@@ -92,28 +74,4 @@ func TestManualABIPacking(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, txData, tx.Data()) require.Equal(t, txData, tx.Data())
// DGF
_, opts, _, dgf, err := setupDisputeGameFactory()
require.NoError(t, err)
rng = rand.New(rand.NewSource(1234))
dgfAbi, err := bindings.DisputeGameFactoryMetaData.GetAbi()
require.NoError(t, err)
output = testutils.RandomOutputResponse(rng)
txData, err = proposeL2OutputDGFTxData(dgfAbi, uint32(0), output)
require.NoError(t, err)
opts.GasLimit = 100_000
dgfTx, err := dgf.Create(
opts,
uint32(0),
output.OutputRoot,
math.U256Bytes(new(big.Int).SetUint64(output.BlockRef.Number)),
)
require.NoError(t, err)
require.Equal(t, txData, dgfTx.Data())
} }
...@@ -53,9 +53,6 @@ type CLIConfig struct { ...@@ -53,9 +53,6 @@ type CLIConfig struct {
// ProposalInterval is the delay between submitting L2 output proposals when the DGFAddress is set. // ProposalInterval is the delay between submitting L2 output proposals when the DGFAddress is set.
ProposalInterval time.Duration ProposalInterval time.Duration
// OutputRetryInterval is the delay between retrying output fetch if one fails.
OutputRetryInterval time.Duration
// DisputeGameType is the type of dispute game to create when submitting an output proposal. // DisputeGameType is the type of dispute game to create when submitting an output proposal.
DisputeGameType uint32 DisputeGameType uint32
...@@ -113,7 +110,6 @@ func NewConfig(ctx *cli.Context) *CLIConfig { ...@@ -113,7 +110,6 @@ func NewConfig(ctx *cli.Context) *CLIConfig {
PprofConfig: oppprof.ReadCLIConfig(ctx), PprofConfig: oppprof.ReadCLIConfig(ctx),
DGFAddress: ctx.String(flags.DisputeGameFactoryAddressFlag.Name), DGFAddress: ctx.String(flags.DisputeGameFactoryAddressFlag.Name),
ProposalInterval: ctx.Duration(flags.ProposalIntervalFlag.Name), ProposalInterval: ctx.Duration(flags.ProposalIntervalFlag.Name),
OutputRetryInterval: ctx.Duration(flags.OutputRetryIntervalFlag.Name),
DisputeGameType: uint32(ctx.Uint(flags.DisputeGameTypeFlag.Name)), DisputeGameType: uint32(ctx.Uint(flags.DisputeGameTypeFlag.Name)),
ActiveSequencerCheckDuration: ctx.Duration(flags.ActiveSequencerCheckDurationFlag.Name), ActiveSequencerCheckDuration: ctx.Duration(flags.ActiveSequencerCheckDurationFlag.Name),
WaitNodeSync: ctx.Bool(flags.WaitNodeSyncFlag.Name), WaitNodeSync: ctx.Bool(flags.WaitNodeSyncFlag.Name),
......
...@@ -8,19 +8,19 @@ import ( ...@@ -8,19 +8,19 @@ import (
"sync" "sync"
"time" "time"
"github.com/ethereum-optimism/optimism/op-proposer/bindings"
"github.com/ethereum-optimism/optimism/op-proposer/contracts"
"github.com/ethereum-optimism/optimism/op-proposer/metrics"
"github.com/ethereum-optimism/optimism/op-service/dial"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"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/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-proposer/bindings"
"github.com/ethereum-optimism/optimism/op-proposer/metrics"
"github.com/ethereum-optimism/optimism/op-service/dial"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
) )
var ( var (
...@@ -44,6 +44,12 @@ type L2OOContract interface { ...@@ -44,6 +44,12 @@ type L2OOContract interface {
NextBlockNumber(*bind.CallOpts) (*big.Int, error) NextBlockNumber(*bind.CallOpts) (*big.Int, error)
} }
type DGFContract interface {
Version(ctx context.Context) (string, error)
HasProposedSince(ctx context.Context, proposer common.Address, cutoff time.Time, gameType uint32) (bool, time.Time, error)
ProposalTx(ctx context.Context, gameType uint32, outputRoot common.Hash, l2BlockNum uint64) (txmgr.TxCandidate, error)
}
type RollupClient interface { type RollupClient interface {
SyncStatus(ctx context.Context) (*eth.SyncStatus, error) SyncStatus(ctx context.Context) (*eth.SyncStatus, error)
OutputAtBlock(ctx context.Context, blockNum uint64) (*eth.OutputResponse, error) OutputAtBlock(ctx context.Context, blockNum uint64) (*eth.OutputResponse, error)
...@@ -55,6 +61,7 @@ type DriverSetup struct { ...@@ -55,6 +61,7 @@ type DriverSetup struct {
Cfg ProposerConfig Cfg ProposerConfig
Txmgr txmgr.TxManager Txmgr txmgr.TxManager
L1Client L1Client L1Client L1Client
Multicaller *batching.MultiCaller
// RollupProvider's RollupClient() is used to retrieve output roots from // RollupProvider's RollupClient() is used to retrieve output roots from
RollupProvider dial.RollupProvider RollupProvider dial.RollupProvider
...@@ -76,8 +83,7 @@ type L2OutputSubmitter struct { ...@@ -76,8 +83,7 @@ type L2OutputSubmitter struct {
l2ooContract L2OOContract l2ooContract L2OOContract
l2ooABI *abi.ABI l2ooABI *abi.ABI
dgfContract *bindings.DisputeGameFactoryCaller dgfContract DGFContract
dgfABI *abi.ABI
} }
// NewL2OutputSubmitter creates a new L2 Output Submitter // NewL2OutputSubmitter creates a new L2 Output Submitter
...@@ -135,27 +141,15 @@ func newL2OOSubmitter(ctx context.Context, cancel context.CancelFunc, setup Driv ...@@ -135,27 +141,15 @@ func newL2OOSubmitter(ctx context.Context, cancel context.CancelFunc, setup Driv
} }
func newDGFSubmitter(ctx context.Context, cancel context.CancelFunc, setup DriverSetup) (*L2OutputSubmitter, error) { func newDGFSubmitter(ctx context.Context, cancel context.CancelFunc, setup DriverSetup) (*L2OutputSubmitter, error) {
dgfCaller, err := bindings.NewDisputeGameFactoryCaller(*setup.Cfg.DisputeGameFactoryAddr, setup.L1Client) dgfCaller := contracts.NewDisputeGameFactory(*setup.Cfg.DisputeGameFactoryAddr, setup.Multicaller, setup.Cfg.NetworkTimeout)
if err != nil {
cancel()
return nil, fmt.Errorf("failed to create DGF at address %s: %w", setup.Cfg.DisputeGameFactoryAddr, err)
}
cCtx, cCancel := context.WithTimeout(ctx, setup.Cfg.NetworkTimeout) version, err := dgfCaller.Version(ctx)
defer cCancel()
version, err := dgfCaller.Version(&bind.CallOpts{Context: cCtx})
if err != nil { if err != nil {
cancel() cancel()
return nil, err return nil, err
} }
log.Info("Connected to DisputeGameFactory", "address", setup.Cfg.DisputeGameFactoryAddr, "version", version) log.Info("Connected to DisputeGameFactory", "address", setup.Cfg.DisputeGameFactoryAddr, "version", version)
parsed, err := bindings.DisputeGameFactoryMetaData.GetAbi()
if err != nil {
cancel()
return nil, err
}
return &L2OutputSubmitter{ return &L2OutputSubmitter{
DriverSetup: setup, DriverSetup: setup,
done: make(chan struct{}), done: make(chan struct{}),
...@@ -163,7 +157,6 @@ func newDGFSubmitter(ctx context.Context, cancel context.CancelFunc, setup Drive ...@@ -163,7 +157,6 @@ func newDGFSubmitter(ctx context.Context, cancel context.CancelFunc, setup Drive
cancel: cancel, cancel: cancel,
dgfContract: dgfCaller, dgfContract: dgfCaller,
dgfABI: parsed,
}, nil }, nil
} }
...@@ -269,18 +262,41 @@ func (l *L2OutputSubmitter) FetchL2OOOutput(ctx context.Context) (*eth.OutputRes ...@@ -269,18 +262,41 @@ func (l *L2OutputSubmitter) FetchL2OOOutput(ctx context.Context) (*eth.OutputRes
return output, true, nil return output, true, nil
} }
// FetchDGFOutput gets the next output proposal for the DGF. // FetchDGFOutput queries the DGF for the latest game and infers whether it is time to make another proposal
// If necessary, it gets the next output proposal for the DGF, and returns it along with
// a boolean for whether the proposal should be submitted at all.
// The passed context is expected to be a lifecycle context. A network timeout // The passed context is expected to be a lifecycle context. A network timeout
// context will be derived from it. // context will be derived from it.
func (l *L2OutputSubmitter) FetchDGFOutput(ctx context.Context) (*eth.OutputResponse, error) { func (l *L2OutputSubmitter) FetchDGFOutput(ctx context.Context) (*eth.OutputResponse, bool, error) {
ctx, cancel := context.WithTimeout(ctx, l.Cfg.NetworkTimeout) cutoff := time.Now().Add(-l.Cfg.ProposalInterval)
defer cancel() proposedRecently, proposalTime, err := l.dgfContract.HasProposedSince(ctx, l.Txmgr.From(), cutoff, l.Cfg.DisputeGameType)
if err != nil {
return nil, false, fmt.Errorf("could not check for recent proposal: %w", err)
}
blockNum, err := l.FetchCurrentBlockNumber(ctx) if proposedRecently {
l.Log.Debug("Duration since last game not past proposal interval", "duration", time.Since(proposalTime))
return nil, false, nil
}
l.Log.Info("No proposals found for at least proposal interval, submitting proposal now", "proposalInterval", l.Cfg.ProposalInterval)
// Fetch the current L2 heads
currentBlockNumber, err := l.FetchCurrentBlockNumber(ctx)
if err != nil { if err != nil {
return nil, err return nil, false, fmt.Errorf("could not fetch current block number: %w", err)
} }
return l.FetchOutput(ctx, blockNum)
if currentBlockNumber == 0 {
l.Log.Info("Skipping proposal for genesis block")
return nil, false, nil
}
output, err := l.FetchOutput(ctx, currentBlockNumber)
if err != nil {
return nil, false, fmt.Errorf("could not fetch output at current block number %d: %w", currentBlockNumber, err)
}
return output, true, nil
} }
// FetchCurrentBlockNumber gets the current block number from the [L2OutputSubmitter]'s [RollupClient]. If the `AllowNonFinalized` configuration // FetchCurrentBlockNumber gets the current block number from the [L2OutputSubmitter]'s [RollupClient]. If the `AllowNonFinalized` configuration
...@@ -337,21 +353,10 @@ func proposeL2OutputTxData(abi *abi.ABI, output *eth.OutputResponse) ([]byte, er ...@@ -337,21 +353,10 @@ func proposeL2OutputTxData(abi *abi.ABI, output *eth.OutputResponse) ([]byte, er
new(big.Int).SetUint64(output.Status.CurrentL1.Number)) new(big.Int).SetUint64(output.Status.CurrentL1.Number))
} }
func (l *L2OutputSubmitter) ProposeL2OutputDGFTxData(output *eth.OutputResponse) ([]byte, *big.Int, error) { func (l *L2OutputSubmitter) ProposeL2OutputDGFTxCandidate(ctx context.Context, output *eth.OutputResponse) (txmgr.TxCandidate, error) {
bond, err := l.dgfContract.InitBonds(&bind.CallOpts{}, l.Cfg.DisputeGameType) cCtx, cancel := context.WithTimeout(ctx, l.Cfg.NetworkTimeout)
if err != nil { defer cancel()
return nil, nil, err return l.dgfContract.ProposalTx(cCtx, l.Cfg.DisputeGameType, common.Hash(output.OutputRoot), output.BlockRef.Number)
}
data, err := proposeL2OutputDGFTxData(l.dgfABI, l.Cfg.DisputeGameType, output)
if err != nil {
return nil, nil, err
}
return data, bond, err
}
// proposeL2OutputDGFTxData creates the transaction data for the DisputeGameFactory's `create` function
func proposeL2OutputDGFTxData(abi *abi.ABI, gameType uint32, output *eth.OutputResponse) ([]byte, error) {
return abi.Pack("create", gameType, output.OutputRoot, math.U256Bytes(new(big.Int).SetUint64(output.BlockRef.Number)))
} }
// We wait until l1head advances beyond blocknum. This is used to make sure proposal tx won't // We wait until l1head advances beyond blocknum. This is used to make sure proposal tx won't
...@@ -392,16 +397,11 @@ func (l *L2OutputSubmitter) sendTransaction(ctx context.Context, output *eth.Out ...@@ -392,16 +397,11 @@ func (l *L2OutputSubmitter) sendTransaction(ctx context.Context, output *eth.Out
l.Log.Info("Proposing output root", "output", output.OutputRoot, "block", output.BlockRef) l.Log.Info("Proposing output root", "output", output.OutputRoot, "block", output.BlockRef)
var receipt *types.Receipt var receipt *types.Receipt
if l.Cfg.DisputeGameFactoryAddr != nil { if l.Cfg.DisputeGameFactoryAddr != nil {
data, bond, err := l.ProposeL2OutputDGFTxData(output) candidate, err := l.ProposeL2OutputDGFTxCandidate(ctx, output)
if err != nil { if err != nil {
return err return err
} }
receipt, err = l.Txmgr.Send(ctx, txmgr.TxCandidate{ receipt, err = l.Txmgr.Send(ctx, candidate)
TxData: data,
To: l.Cfg.DisputeGameFactoryAddr,
GasLimit: 0,
Value: bond,
})
if err != nil { if err != nil {
return err return err
} }
...@@ -432,39 +432,11 @@ func (l *L2OutputSubmitter) sendTransaction(ctx context.Context, output *eth.Out ...@@ -432,39 +432,11 @@ func (l *L2OutputSubmitter) sendTransaction(ctx context.Context, output *eth.Out
} }
// loop is responsible for creating & submitting the next outputs // loop is responsible for creating & submitting the next outputs
// The loop regularly polls the L2 chain to infer whether to make the next proposal.
func (l *L2OutputSubmitter) loop() { func (l *L2OutputSubmitter) loop() {
defer l.wg.Done() defer l.wg.Done()
defer l.Log.Info("loop returning")
ctx := l.ctx ctx := l.ctx
if l.dgfContract == nil {
l.loopL2OO(ctx)
} else {
l.loopDGF(ctx)
}
}
func (l *L2OutputSubmitter) waitNodeSync() error {
cCtx, cancel := context.WithTimeout(l.ctx, l.Cfg.NetworkTimeout)
defer cancel()
l1head, err := l.Txmgr.BlockNumber(cCtx)
if err != nil {
return fmt.Errorf("failed to retrieve current L1 block number: %w", err)
}
rollupClient, err := l.RollupProvider.RollupClient(l.ctx)
if err != nil {
return fmt.Errorf("failed to get rollup client: %w", err)
}
return dial.WaitRollupSync(l.ctx, l.Log, rollupClient, l1head, time.Second*12)
}
// The loopL2OO regularly polls the L2OO for the next block to propose,
// and if the current finalized (or safe) block is past that next block, it
// proposes it.
func (l *L2OutputSubmitter) loopL2OO(ctx context.Context) {
defer l.Log.Info("loopL2OO returning")
ticker := time.NewTicker(l.Cfg.PollInterval) ticker := time.NewTicker(l.Cfg.PollInterval)
defer ticker.Stop() defer ticker.Stop()
for { for {
...@@ -480,12 +452,19 @@ func (l *L2OutputSubmitter) loopL2OO(ctx context.Context) { ...@@ -480,12 +452,19 @@ func (l *L2OutputSubmitter) loopL2OO(ctx context.Context) {
// A note on retrying: the outer ticker already runs on a short // A note on retrying: the outer ticker already runs on a short
// poll interval, which has a default value of 6 seconds. So no // poll interval, which has a default value of 6 seconds. So no
// retry logic is needed around output fetching here. // retry logic is needed around output fetching here.
output, shouldPropose, err := l.FetchL2OOOutput(ctx) var output *eth.OutputResponse
var shouldPropose bool
var err error
if l.dgfContract == nil {
output, shouldPropose, err = l.FetchL2OOOutput(ctx)
} else {
output, shouldPropose, err = l.FetchDGFOutput(ctx)
}
if err != nil { if err != nil {
l.Log.Warn("Error getting L2OO output", "err", err) l.Log.Warn("Error getting output", "err", err)
continue continue
} else if !shouldPropose { } else if !shouldPropose {
// debug logging already in FetchL2OOOutput // debug logging already in Fetch(DGF|L2OO)Output
continue continue
} }
...@@ -494,45 +473,24 @@ func (l *L2OutputSubmitter) loopL2OO(ctx context.Context) { ...@@ -494,45 +473,24 @@ func (l *L2OutputSubmitter) loopL2OO(ctx context.Context) {
return return
} }
} }
} }
// The loopDGF proposes a new output every proposal interval. It does _not_ query func (l *L2OutputSubmitter) waitNodeSync() error {
// the DGF for when to next propose, as the DGF doesn't have the concept of a cCtx, cancel := context.WithTimeout(l.ctx, l.Cfg.NetworkTimeout)
// proposal interval, like in the L2OO case. For this reason, it has to keep track defer cancel()
// of the interval itself, for which it uses an internal ticker.
func (l *L2OutputSubmitter) loopDGF(ctx context.Context) {
defer l.Log.Info("loopDGF returning")
ticker := time.NewTicker(l.Cfg.ProposalInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
var (
output *eth.OutputResponse
err error
)
// A note on retrying: because the proposal interval is usually much
// larger than the interval at which to retry proposing on a failed attempt,
// we want to keep retrying getting the output proposal until we succeed.
for output == nil || err != nil {
select {
case <-l.done:
return
default:
}
output, err = l.FetchDGFOutput(ctx) l1head, err := l.Txmgr.BlockNumber(cCtx)
if err != nil { if err != nil {
l.Log.Warn("Error getting DGF output, retrying...", "err", err) return fmt.Errorf("failed to retrieve current L1 block number: %w", err)
time.Sleep(l.Cfg.OutputRetryInterval)
}
} }
l.proposeOutput(ctx, output) rollupClient, err := l.RollupProvider.RollupClient(l.ctx)
case <-l.done: if err != nil {
return return fmt.Errorf("failed to get rollup client: %w", err)
}
} }
return dial.WaitRollupSync(l.ctx, l.Log, rollupClient, l1head, time.Second*12)
} }
func (l *L2OutputSubmitter) proposeOutput(ctx context.Context, output *eth.OutputResponse) { func (l *L2OutputSubmitter) proposeOutput(ctx context.Context, output *eth.OutputResponse) {
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum-optimism/optimism/op-service/testutils" "github.com/ethereum-optimism/optimism/op-service/testutils"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
txmgrmocks "github.com/ethereum-optimism/optimism/op-service/txmgr/mocks" txmgrmocks "github.com/ethereum-optimism/optimism/op-service/txmgr/mocks"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
...@@ -37,6 +38,23 @@ func (m *MockL2OOContract) NextBlockNumber(opts *bind.CallOpts) (*big.Int, error ...@@ -37,6 +38,23 @@ func (m *MockL2OOContract) NextBlockNumber(opts *bind.CallOpts) (*big.Int, error
return args.Get(0).(*big.Int), args.Error(1) return args.Get(0).(*big.Int), args.Error(1)
} }
type StubDGFContract struct {
hasProposedCount int
}
func (m *StubDGFContract) HasProposedSince(_ context.Context, _ common.Address, _ time.Time, _ uint32) (bool, time.Time, error) {
m.hasProposedCount++
return false, time.Unix(1000, 0), nil
}
func (m *StubDGFContract) ProposalTx(_ context.Context, _ uint32, _ common.Hash, _ uint64) (txmgr.TxCandidate, error) {
panic("not implemented")
}
func (m *StubDGFContract) Version(_ context.Context) (string, error) {
panic("not implemented")
}
type mockRollupEndpointProvider struct { type mockRollupEndpointProvider struct {
rollupClient *testutils.MockRollupClient rollupClient *testutils.MockRollupClient
rollupClientErr error rollupClientErr error
...@@ -54,7 +72,7 @@ func (p *mockRollupEndpointProvider) RollupClient(context.Context) (dial.RollupC ...@@ -54,7 +72,7 @@ func (p *mockRollupEndpointProvider) RollupClient(context.Context) (dial.RollupC
func (p *mockRollupEndpointProvider) Close() {} func (p *mockRollupEndpointProvider) Close() {}
func setup(t *testing.T) (*L2OutputSubmitter, *mockRollupEndpointProvider, *MockL2OOContract, *txmgrmocks.TxManager, *testlog.CapturingHandler) { func setup(t *testing.T, testName string) (*L2OutputSubmitter, *mockRollupEndpointProvider, *MockL2OOContract, *StubDGFContract, *txmgrmocks.TxManager, *testlog.CapturingHandler) {
ep := newEndpointProvider() ep := newEndpointProvider()
l2OutputOracleAddr := common.HexToAddress("0x3F8A862E63E759a77DA22d384027D21BF096bA9E") l2OutputOracleAddr := common.HexToAddress("0x3F8A862E63E759a77DA22d384027D21BF096bA9E")
...@@ -62,7 +80,6 @@ func setup(t *testing.T) (*L2OutputSubmitter, *mockRollupEndpointProvider, *Mock ...@@ -62,7 +80,6 @@ func setup(t *testing.T) (*L2OutputSubmitter, *mockRollupEndpointProvider, *Mock
proposerConfig := ProposerConfig{ proposerConfig := ProposerConfig{
PollInterval: time.Microsecond, PollInterval: time.Microsecond,
ProposalInterval: time.Microsecond, ProposalInterval: time.Microsecond,
OutputRetryInterval: time.Microsecond,
L2OutputOracleAddr: &l2OutputOracleAddr, L2OutputOracleAddr: &l2OutputOracleAddr,
} }
...@@ -81,15 +98,23 @@ func setup(t *testing.T) (*L2OutputSubmitter, *mockRollupEndpointProvider, *Mock ...@@ -81,15 +98,23 @@ func setup(t *testing.T) (*L2OutputSubmitter, *mockRollupEndpointProvider, *Mock
require.NoError(t, err) require.NoError(t, err)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
l2ooContract := new(MockL2OOContract)
l2OutputSubmitter := L2OutputSubmitter{ l2OutputSubmitter := L2OutputSubmitter{
DriverSetup: setup, DriverSetup: setup,
done: make(chan struct{}), done: make(chan struct{}),
l2ooContract: l2ooContract,
l2ooABI: parsed, l2ooABI: parsed,
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
} }
var mockDGFContract *StubDGFContract
var mockL2OOContract *MockL2OOContract
if testName == "DGF" {
mockDGFContract = new(StubDGFContract)
l2OutputSubmitter.dgfContract = mockDGFContract
} else {
mockL2OOContract = new(MockL2OOContract)
l2OutputSubmitter.l2ooContract = mockL2OOContract
}
txmgr.On("BlockNumber", mock.Anything).Return(uint64(100), nil).Once() txmgr.On("BlockNumber", mock.Anything).Return(uint64(100), nil).Once()
txmgr.On("Send", mock.Anything, mock.Anything). txmgr.On("Send", mock.Anything, mock.Anything).
...@@ -101,7 +126,7 @@ func setup(t *testing.T) (*L2OutputSubmitter, *mockRollupEndpointProvider, *Mock ...@@ -101,7 +126,7 @@ func setup(t *testing.T) (*L2OutputSubmitter, *mockRollupEndpointProvider, *Mock
close(l2OutputSubmitter.done) close(l2OutputSubmitter.done)
}) })
return &l2OutputSubmitter, ep, l2ooContract, txmgr, logs return &l2OutputSubmitter, ep, mockL2OOContract, mockDGFContract, txmgr, logs
} }
func TestL2OutputSubmitter_OutputRetry(t *testing.T) { func TestL2OutputSubmitter_OutputRetry(t *testing.T) {
...@@ -112,10 +137,11 @@ func TestL2OutputSubmitter_OutputRetry(t *testing.T) { ...@@ -112,10 +137,11 @@ func TestL2OutputSubmitter_OutputRetry(t *testing.T) {
{name: "DGF"}, {name: "DGF"},
} }
proposerAddr := common.Address{0xab}
const numFails = 3 const numFails = 3
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
ps, ep, l2ooContract, txmgr, logs := setup(t) ps, ep, l2ooContract, dgfContract, txmgr, logs := setup(t, tt.name)
ep.rollupClient.On("SyncStatus").Return(&eth.SyncStatus{FinalizedL2: eth.L2BlockRef{Number: 42}}, nil).Times(numFails + 1) ep.rollupClient.On("SyncStatus").Return(&eth.SyncStatus{FinalizedL2: eth.L2BlockRef{Number: 42}}, nil).Times(numFails + 1)
ep.rollupClient.ExpectOutputAtBlock(42, nil, fmt.Errorf("TEST: failed to fetch output")).Times(numFails) ep.rollupClient.ExpectOutputAtBlock(42, nil, fmt.Errorf("TEST: failed to fetch output")).Times(numFails)
...@@ -132,19 +158,24 @@ func TestL2OutputSubmitter_OutputRetry(t *testing.T) { ...@@ -132,19 +158,24 @@ func TestL2OutputSubmitter_OutputRetry(t *testing.T) {
nil, nil,
) )
if tt.name == "DGF" { txmgr.On("From").Return(proposerAddr).Times(numFails + 1)
ps.loopDGF(ps.ctx)
} else { if tt.name == "L2OO" {
txmgr.On("From").Return(common.Address{}).Times(numFails + 1)
l2ooContract.On("NextBlockNumber", mock.AnythingOfType("*bind.CallOpts")).Return(big.NewInt(42), nil).Times(numFails + 1) l2ooContract.On("NextBlockNumber", mock.AnythingOfType("*bind.CallOpts")).Return(big.NewInt(42), nil).Times(numFails + 1)
ps.loopL2OO(ps.ctx)
} }
ps.wg.Add(1)
ps.loop()
ep.rollupClient.AssertExpectations(t) ep.rollupClient.AssertExpectations(t)
if tt.name == "L2OO" {
l2ooContract.AssertExpectations(t) l2ooContract.AssertExpectations(t)
require.Len(t, logs.FindLogs(testlog.NewMessageContainsFilter("Error getting "+tt.name)), numFails) } else {
require.Equal(t, numFails+1, dgfContract.hasProposedCount)
}
require.Len(t, logs.FindLogs(testlog.NewMessageContainsFilter("Error getting output")), numFails)
require.NotNil(t, logs.FindLog(testlog.NewMessageFilter("Proposer tx successfully published"))) require.NotNil(t, logs.FindLog(testlog.NewMessageFilter("Proposer tx successfully published")))
require.NotNil(t, logs.FindLog(testlog.NewMessageFilter("loop"+tt.name+" returning"))) require.NotNil(t, logs.FindLog(testlog.NewMessageFilter("loop returning")))
}) })
} }
} }
...@@ -18,6 +18,7 @@ import ( ...@@ -18,6 +18,7 @@ import (
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/ethereum-optimism/optimism/op-service/oppprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -32,9 +33,6 @@ type ProposerConfig struct { ...@@ -32,9 +33,6 @@ type ProposerConfig struct {
PollInterval time.Duration PollInterval time.Duration
NetworkTimeout time.Duration NetworkTimeout time.Duration
// How frequently to retry fetching an output if one fails
OutputRetryInterval time.Duration
// How frequently to post L2 outputs when the DisputeGameFactory is configured // How frequently to post L2 outputs when the DisputeGameFactory is configured
ProposalInterval time.Duration ProposalInterval time.Duration
...@@ -92,7 +90,6 @@ func (ps *ProposerService) initFromCLIConfig(ctx context.Context, version string ...@@ -92,7 +90,6 @@ func (ps *ProposerService) initFromCLIConfig(ctx context.Context, version string
ps.initMetrics(cfg) ps.initMetrics(cfg)
ps.PollInterval = cfg.PollInterval ps.PollInterval = cfg.PollInterval
ps.OutputRetryInterval = cfg.OutputRetryInterval
ps.NetworkTimeout = cfg.TxMgrConfig.NetworkTimeout ps.NetworkTimeout = cfg.TxMgrConfig.NetworkTimeout
ps.AllowNonFinalized = cfg.AllowNonFinalized ps.AllowNonFinalized = cfg.AllowNonFinalized
ps.WaitNodeSync = cfg.WaitNodeSync ps.WaitNodeSync = cfg.WaitNodeSync
...@@ -234,6 +231,7 @@ func (ps *ProposerService) initDriver() error { ...@@ -234,6 +231,7 @@ func (ps *ProposerService) initDriver() error {
Cfg: ps.ProposerConfig, Cfg: ps.ProposerConfig,
Txmgr: ps.TxManager, Txmgr: ps.TxManager,
L1Client: ps.L1Client, L1Client: ps.L1Client,
Multicaller: batching.NewMultiCaller(ps.L1Client.Client(), batching.DefaultBatchSize),
RollupProvider: ps.RollupProvider, RollupProvider: ps.RollupProvider,
}) })
if err != nil { if err != 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