Commit 4d16e57e authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into dependabot/npm_and_yarn/patch-package-8.0.0

parents f4ad5126 ec984f5e
---
'@eth-optimism/sdk': patch
---
Updated npm dependencies to latest
---
'@eth-optimism/common-ts': patch
---
Updated npm dependencies of common-ts
---
'@eth-optimism/web3.js-plugin': patch
---
Update code exmaples in README
...@@ -415,6 +415,10 @@ jobs: ...@@ -415,6 +415,10 @@ jobs:
pnpm autogen:invariant-docs pnpm autogen:invariant-docs
git diff --exit-code ./invariant-docs/*.md || echo "export INVARIANT_DOCS_STATUS=1" >> "$BASH_ENV" git diff --exit-code ./invariant-docs/*.md || echo "export INVARIANT_DOCS_STATUS=1" >> "$BASH_ENV"
working_directory: packages/contracts-bedrock working_directory: packages/contracts-bedrock
- run:
name: check deploy configs || echo "export DEPLOY_CONFIGS_STATUS=1" >> "$BASH_ENV"
command: pnpm validate-deploy-configs
working_directory: packages/contracts-bedrock
- run: - run:
name: check statuses name: check statuses
command: | command: |
...@@ -430,12 +434,16 @@ jobs: ...@@ -430,12 +434,16 @@ jobs:
echo "Storage snapshot failed, see job output for details." echo "Storage snapshot failed, see job output for details."
FAILED=1 FAILED=1
fi fi
if [[ "$SEMVER_LOCK_STATUS" -ne 0 ]]; then
echo "Semver lock failed, see job output for details."
FAILED=1
fi
if [[ "$INVARIANT_DOCS_STATUS" -ne 0 ]]; then if [[ "$INVARIANT_DOCS_STATUS" -ne 0 ]]; then
echo "Invariant docs failed, see job output for details." echo "Invariant docs failed, see job output for details."
FAILED=1 FAILED=1
fi fi
if [[ "$SEMVER_LOCK_STATUS" -ne 0 ]]; then if [[ "$DEPLOY_CONFIGS_STATUS" -ne 0 ]]; then
echo "Semver lock failed, see job output for details." echo "Deploy config check failed, see job output for details."
FAILED=1 FAILED=1
fi fi
if [[ "$FAILED" -ne 0 ]]; then if [[ "$FAILED" -ne 0 ]]; then
......
...@@ -300,7 +300,7 @@ pull_request_rules: ...@@ -300,7 +300,7 @@ pull_request_rules:
- A-proxyd - A-proxyd
- name: Add M-docs label - name: Add M-docs label
conditions: conditions:
- 'files~=^(technical-documents|specs)\/' - 'files~=^(docs|specs)\/'
- '#label<5' - '#label<5'
actions: actions:
label: label:
...@@ -321,11 +321,3 @@ pull_request_rules: ...@@ -321,11 +321,3 @@ pull_request_rules:
label: label:
add: add:
- M-ci - M-ci
- name: Add M-bot label for bots
conditions:
- 'author=(github-actions|dependabot|)'
- '#label<5'
actions:
label:
add:
- M-bot
<div align="center"> <div align="center">
<br /> <br />
<br /> <br />
...@@ -48,13 +47,7 @@ Refer to the Directory Structure section below to understand which packages are ...@@ -48,13 +47,7 @@ Refer to the Directory Structure section below to understand which packages are
## Directory Structure ## Directory Structure
<pre> <pre>
~~ Production ~~ ├── <a href="./docs">docs</a>: A collection of documents including audits and post-mortems
├── <a href="./packages">packages</a>
│ ├── <a href="./packages/common-ts">common-ts</a>: Common tools for building apps in TypeScript
│ ├── <a href="./packages/contracts-bedrock">contracts-bedrock</a>: Bedrock smart contracts.
│ ├── <a href="./packages/core-utils">core-utils</a>: Low-level utilities that make building Optimism easier
│ ├── <a href="./packages/chain-mon">chain-mon</a>: Chain monitoring services
│ └── <a href="./packages/sdk">sdk</a>: provides a set of tools for interacting with Optimism
├── <a href="./op-bindings">op-bindings</a>: Go bindings for Bedrock smart contracts. ├── <a href="./op-bindings">op-bindings</a>: Go bindings for Bedrock smart contracts.
├── <a href="./op-batcher">op-batcher</a>: L2-Batch Submitter, submits bundles of batches to L1 ├── <a href="./op-batcher">op-batcher</a>: L2-Batch Submitter, submits bundles of batches to L1
├── <a href="./op-bootnode">op-bootnode</a>: Standalone op-node discovery bootnode ├── <a href="./op-bootnode">op-bootnode</a>: Standalone op-node discovery bootnode
...@@ -70,19 +63,15 @@ Refer to the Directory Structure section below to understand which packages are ...@@ -70,19 +63,15 @@ Refer to the Directory Structure section below to understand which packages are
├── <a href="./op-signer">op-signer</a>: Client signer ├── <a href="./op-signer">op-signer</a>: Client signer
├── <a href="./op-wheel">op-wheel</a>: Database utilities ├── <a href="./op-wheel">op-wheel</a>: Database utilities
├── <a href="./ops-bedrock">ops-bedrock</a>: Bedrock devnet work ├── <a href="./ops-bedrock">ops-bedrock</a>: Bedrock devnet work
├── <a href="./proxyd">proxyd</a>: Configurable RPC request router and proxy
└── <a href="./specs">specs</a>: Specs of the rollup starting at the Bedrock upgrade
~~ Pre-BEDROCK ~~
├── <a href="./packages">packages</a> ├── <a href="./packages">packages</a>
│ ├── <a href="./packages/chain-mon">chain-mon</a>: Chain monitoring services
│ ├── <a href="./packages/common-ts">common-ts</a>: Common tools for building apps in TypeScript │ ├── <a href="./packages/common-ts">common-ts</a>: Common tools for building apps in TypeScript
│ ├── <a href="./packages/contracts-ts">contracts-ts</a>: ABI and Address constants
│ ├── <a href="./packages/contracts-bedrock">contracts-bedrock</a>: Bedrock smart contracts
│ ├── <a href="./packages/core-utils">core-utils</a>: Low-level utilities that make building Optimism easier │ ├── <a href="./packages/core-utils">core-utils</a>: Low-level utilities that make building Optimism easier
│ ├── <a href="./packages/chain-mon">chain-mon</a>: Chain monitoring services
│ └── <a href="./packages/sdk">sdk</a>: provides a set of tools for interacting with Optimism │ └── <a href="./packages/sdk">sdk</a>: provides a set of tools for interacting with Optimism
├── <a href="./indexer">indexer</a>: indexes and syncs transactions
├── <a href="./op-exporter">op-exporter</a>: A prometheus exporter to collect/serve metrics from an Optimism node
├── <a href="./proxyd">proxyd</a>: Configurable RPC request router and proxy ├── <a href="./proxyd">proxyd</a>: Configurable RPC request router and proxy
└── <a href="./technical-documents">technical-documents</a>: audits and post-mortem documents └── <a href="./specs">specs</a>: Specs of the rollup starting at the Bedrock upgrade
</pre> </pre>
## Branching Model ## Branching Model
......
...@@ -56,7 +56,7 @@ func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalWithFilter(filter database ...@@ -56,7 +56,7 @@ func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalWithFilter(filter database
func (mbv *MockBridgeTransfersView) L1BridgeDepositsByAddress(address common.Address, cursor string, limit int) (*database.L1BridgeDepositsResponse, error) { func (mbv *MockBridgeTransfersView) L1BridgeDepositsByAddress(address common.Address, cursor string, limit int) (*database.L1BridgeDepositsResponse, error) {
return &database.L1BridgeDepositsResponse{ return &database.L1BridgeDepositsResponse{
Deposits: []*database.L1BridgeDepositWithTransactionHashes{ Deposits: []database.L1BridgeDepositWithTransactionHashes{
{ {
L1BridgeDeposit: deposit, L1BridgeDeposit: deposit,
L1TransactionHash: common.HexToHash("0x123"), L1TransactionHash: common.HexToHash("0x123"),
...@@ -67,7 +67,7 @@ func (mbv *MockBridgeTransfersView) L1BridgeDepositsByAddress(address common.Add ...@@ -67,7 +67,7 @@ func (mbv *MockBridgeTransfersView) L1BridgeDepositsByAddress(address common.Add
func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalsByAddress(address common.Address, cursor string, limit int) (*database.L2BridgeWithdrawalsResponse, error) { func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalsByAddress(address common.Address, cursor string, limit int) (*database.L2BridgeWithdrawalsResponse, error) {
return &database.L2BridgeWithdrawalsResponse{ return &database.L2BridgeWithdrawalsResponse{
Withdrawals: []*database.L2BridgeWithdrawalWithTransactionHashes{ Withdrawals: []database.L2BridgeWithdrawalWithTransactionHashes{
{ {
L2BridgeWithdrawal: withdrawal, L2BridgeWithdrawal: withdrawal,
L2TransactionHash: common.HexToHash("0x789"), L2TransactionHash: common.HexToHash("0x789"),
......
...@@ -40,7 +40,7 @@ func runIndexer(ctx *cli.Context) error { ...@@ -40,7 +40,7 @@ func runIndexer(ctx *cli.Context) error {
return err return err
} }
indexer, err := indexer.NewIndexer(cfg.Chain, cfg.RPCs, db, logger) indexer, err := indexer.NewIndexer(logger, cfg.Chain, cfg.RPCs, db)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -3,7 +3,6 @@ package config ...@@ -3,7 +3,6 @@ package config
import ( import (
"fmt" "fmt"
"os" "os"
"reflect"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -23,31 +22,18 @@ type Config struct { ...@@ -23,31 +22,18 @@ type Config struct {
// fetch this via onchain config from RPCsConfig and remove from config in future // fetch this via onchain config from RPCsConfig and remove from config in future
type L1Contracts struct { type L1Contracts struct {
OptimismPortal common.Address `toml:"optimism-portal"` OptimismPortalProxy common.Address `toml:"optimism-portal"`
L2OutputOracle common.Address `toml:"l2-output-oracle"` L2OutputOracleProxy common.Address `toml:"l2-output-oracle"`
L1CrossDomainMessenger common.Address `toml:"l1-cross-domain-messenger"` L1CrossDomainMessengerProxy common.Address `toml:"l1-cross-domain-messenger"`
L1StandardBridge common.Address `toml:"l1-standard-bridge"` L1StandardBridgeProxy common.Address `toml:"l1-standard-bridge"`
L1ERC721Bridge common.Address `toml:"l1-erc721-bridge"`
// Some more contracts -- ProxyAdmin, SystemConfig, etcc // Some more contracts -- L1ERC721Bridge, ProxyAdmin, SystemConfig, etc
// Ignore the auxiliary contracts? // Ignore the auxiliary contracts?
// Legacy contracts? We'll add this in to index the legacy chain. // Legacy contracts? We'll add this in to index the legacy chain.
// Remove afterwards? // Remove afterwards?
} }
func (c L1Contracts) ToSlice() []common.Address {
fields := reflect.VisibleFields(reflect.TypeOf(c))
v := reflect.ValueOf(c)
contracts := make([]common.Address, len(fields))
for i, field := range fields {
contracts[i] = (v.FieldByName(field.Name).Interface()).(common.Address)
}
return contracts
}
// ChainConfig configures of the chain being indexed // ChainConfig configures of the chain being indexed
type ChainConfig struct { type ChainConfig struct {
// Configure known chains with the l2 chain id // Configure known chains with the l2 chain id
...@@ -88,6 +74,7 @@ type MetricsConfig struct { ...@@ -88,6 +74,7 @@ type MetricsConfig struct {
// LoadConfig loads the `indexer.toml` config file from a given path // LoadConfig loads the `indexer.toml` config file from a given path
func LoadConfig(logger geth_log.Logger, path string) (Config, error) { func LoadConfig(logger geth_log.Logger, path string) (Config, error) {
logger.Info("Loading config file", "path", path)
var conf Config var conf Config
data, err := os.ReadFile(path) data, err := os.ReadFile(path)
...@@ -97,6 +84,8 @@ func LoadConfig(logger geth_log.Logger, path string) (Config, error) { ...@@ -97,6 +84,8 @@ func LoadConfig(logger geth_log.Logger, path string) (Config, error) {
data = []byte(os.ExpandEnv(string(data))) data = []byte(os.ExpandEnv(string(data)))
logger.Debug("Decoding config file", "data", string(data))
if _, err := toml.Decode(string(data), &conf); err != nil { if _, err := toml.Decode(string(data), &conf); err != nil {
logger.Info("Failed to decode config file", "message", err) logger.Info("Failed to decode config file", "message", err)
return conf, err return conf, err
......
...@@ -54,11 +54,10 @@ func TestLoadConfig(t *testing.T) { ...@@ -54,11 +54,10 @@ func TestLoadConfig(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, conf.Chain.Preset, 420) require.Equal(t, conf.Chain.Preset, 420)
require.Equal(t, conf.Chain.L1Contracts.OptimismPortal.String(), presetL1Contracts[420].OptimismPortal.String()) require.Equal(t, conf.Chain.L1Contracts.OptimismPortalProxy.String(), presetL1Contracts[420].OptimismPortalProxy.String())
require.Equal(t, conf.Chain.L1Contracts.L1CrossDomainMessenger.String(), presetL1Contracts[420].L1CrossDomainMessenger.String()) require.Equal(t, conf.Chain.L1Contracts.L1CrossDomainMessengerProxy.String(), presetL1Contracts[420].L1CrossDomainMessengerProxy.String())
require.Equal(t, conf.Chain.L1Contracts.L1ERC721Bridge.String(), presetL1Contracts[420].L1ERC721Bridge.String()) require.Equal(t, conf.Chain.L1Contracts.L1StandardBridgeProxy.String(), presetL1Contracts[420].L1StandardBridgeProxy.String())
require.Equal(t, conf.Chain.L1Contracts.L1StandardBridge.String(), presetL1Contracts[420].L1StandardBridge.String()) require.Equal(t, conf.Chain.L1Contracts.L2OutputOracleProxy.String(), presetL1Contracts[420].L2OutputOracleProxy.String())
require.Equal(t, conf.Chain.L1Contracts.L2OutputOracle.String(), presetL1Contracts[420].L2OutputOracle.String())
require.Equal(t, conf.RPCs.L1RPC, "https://l1.example.com") require.Equal(t, conf.RPCs.L1RPC, "https://l1.example.com")
require.Equal(t, conf.RPCs.L2RPC, "https://l2.example.com") require.Equal(t, conf.RPCs.L2RPC, "https://l2.example.com")
require.Equal(t, conf.DB.Host, "127.0.0.1") require.Equal(t, conf.DB.Host, "127.0.0.1")
...@@ -85,7 +84,6 @@ func TestLoadConfig_WithoutPreset(t *testing.T) { ...@@ -85,7 +84,6 @@ func TestLoadConfig_WithoutPreset(t *testing.T) {
l2-output-oracle = "0x42097868233d1aa22e815a266982f2cf17685a27" l2-output-oracle = "0x42097868233d1aa22e815a266982f2cf17685a27"
l1-cross-domain-messenger = "0x420ce71c97B33Cc4729CF772ae268934F7ab5fA1" l1-cross-domain-messenger = "0x420ce71c97B33Cc4729CF772ae268934F7ab5fA1"
l1-standard-bridge = "0x4209fc46f92E8a1c0deC1b1747d010903E884bE1" l1-standard-bridge = "0x4209fc46f92E8a1c0deC1b1747d010903E884bE1"
l1-erc721-bridge = "0x420749f83b81B301cAb5f48EB8516B986DAef23D"
[rpcs] [rpcs]
l1-rpc = "https://l1.example.com" l1-rpc = "https://l1.example.com"
...@@ -104,11 +102,10 @@ func TestLoadConfig_WithoutPreset(t *testing.T) { ...@@ -104,11 +102,10 @@ func TestLoadConfig_WithoutPreset(t *testing.T) {
conf, err := LoadConfig(logger, tmpfile.Name()) conf, err := LoadConfig(logger, tmpfile.Name())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, conf.Chain.L1Contracts.OptimismPortal.String(), common.HexToAddress("0x4205Fc579115071764c7423A4f12eDde41f106Ed").String()) require.Equal(t, conf.Chain.L1Contracts.OptimismPortalProxy.String(), common.HexToAddress("0x4205Fc579115071764c7423A4f12eDde41f106Ed").String())
require.Equal(t, conf.Chain.L1Contracts.L2OutputOracle.String(), common.HexToAddress("0x42097868233d1aa22e815a266982f2cf17685a27").String()) require.Equal(t, conf.Chain.L1Contracts.L2OutputOracleProxy.String(), common.HexToAddress("0x42097868233d1aa22e815a266982f2cf17685a27").String())
require.Equal(t, conf.Chain.L1Contracts.L1CrossDomainMessenger.String(), common.HexToAddress("0x420ce71c97B33Cc4729CF772ae268934F7ab5fA1").String()) require.Equal(t, conf.Chain.L1Contracts.L1CrossDomainMessengerProxy.String(), common.HexToAddress("0x420ce71c97B33Cc4729CF772ae268934F7ab5fA1").String())
require.Equal(t, conf.Chain.L1Contracts.L1StandardBridge.String(), common.HexToAddress("0x4209fc46f92E8a1c0deC1b1747d010903E884bE1").String()) require.Equal(t, conf.Chain.L1Contracts.L1StandardBridgeProxy.String(), common.HexToAddress("0x4209fc46f92E8a1c0deC1b1747d010903E884bE1").String())
require.Equal(t, conf.Chain.L1Contracts.L1ERC721Bridge.String(), common.HexToAddress("0x420749f83b81B301cAb5f48EB8516B986DAef23D").String())
require.Equal(t, conf.Chain.Preset, 0) require.Equal(t, conf.Chain.Preset, 0)
} }
......
...@@ -10,54 +10,44 @@ import ( ...@@ -10,54 +10,44 @@ import (
var presetL1Contracts = map[int]L1Contracts{ var presetL1Contracts = map[int]L1Contracts{
// OP Mainnet // OP Mainnet
10: { 10: {
OptimismPortal: common.HexToAddress("0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"), OptimismPortalProxy: common.HexToAddress("0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"),
L2OutputOracle: common.HexToAddress("0xdfe97868233d1aa22e815a266982f2cf17685a27"), L2OutputOracleProxy: common.HexToAddress("0xdfe97868233d1aa22e815a266982f2cf17685a27"),
L1CrossDomainMessenger: common.HexToAddress("0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1"), L1CrossDomainMessengerProxy: common.HexToAddress("0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1"),
L1StandardBridge: common.HexToAddress("0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1"), L1StandardBridgeProxy: common.HexToAddress("0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1"),
L1ERC721Bridge: common.HexToAddress("0x5a7749f83b81B301cAb5f48EB8516B986DAef23D"),
}, },
// OP Goerli // OP Goerli
420: { 420: {
OptimismPortal: common.HexToAddress("0x5b47E1A08Ea6d985D6649300584e6722Ec4B1383"), OptimismPortalProxy: common.HexToAddress("0x5b47E1A08Ea6d985D6649300584e6722Ec4B1383"),
L2OutputOracle: common.HexToAddress("0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0"), L2OutputOracleProxy: common.HexToAddress("0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0"),
L1CrossDomainMessenger: common.HexToAddress("0x5086d1eEF304eb5284A0f6720f79403b4e9bE294"), L1CrossDomainMessengerProxy: common.HexToAddress("0x5086d1eEF304eb5284A0f6720f79403b4e9bE294"),
L1StandardBridge: common.HexToAddress("0x636Af16bf2f682dD3109e60102b8E1A089FedAa8"), L1StandardBridgeProxy: common.HexToAddress("0x636Af16bf2f682dD3109e60102b8E1A089FedAa8"),
L1ERC721Bridge: common.HexToAddress("0x8DD330DdE8D9898d43b4dc840Da27A07dF91b3c9"),
}, },
// Base Mainnet // Base Mainnet
8453: { 8453: {
OptimismPortal: common.HexToAddress("0x49048044D57e1C92A77f79988d21Fa8fAF74E97e"), OptimismPortalProxy: common.HexToAddress("0x49048044D57e1C92A77f79988d21Fa8fAF74E97e"),
L2OutputOracle: common.HexToAddress("0x56315b90c40730925ec5485cf004d835058518A0"), L2OutputOracleProxy: common.HexToAddress("0x56315b90c40730925ec5485cf004d835058518A0"),
L1CrossDomainMessenger: common.HexToAddress("0x866E82a600A1414e583f7F13623F1aC5d58b0Afa"), L1CrossDomainMessengerProxy: common.HexToAddress("0x866E82a600A1414e583f7F13623F1aC5d58b0Afa"),
L1StandardBridge: common.HexToAddress("0x3154Cf16ccdb4C6d922629664174b904d80F2C35"), L1StandardBridgeProxy: common.HexToAddress("0x3154Cf16ccdb4C6d922629664174b904d80F2C35"),
// FIXME update this to the correct address
L1ERC721Bridge: common.HexToAddress("0x0000000000000000000000000000000000000000"),
}, },
// Base Goerli // Base Goerli
84531: { 84531: {
OptimismPortal: common.HexToAddress("0xe93c8cD0D409341205A592f8c4Ac1A5fe5585cfA"), OptimismPortalProxy: common.HexToAddress("0xe93c8cD0D409341205A592f8c4Ac1A5fe5585cfA"),
L2OutputOracle: common.HexToAddress("0x2A35891ff30313CcFa6CE88dcf3858bb075A2298"), L2OutputOracleProxy: common.HexToAddress("0x2A35891ff30313CcFa6CE88dcf3858bb075A2298"),
L1CrossDomainMessenger: common.HexToAddress("0x8e5693140eA606bcEB98761d9beB1BC87383706D"), L1CrossDomainMessengerProxy: common.HexToAddress("0x8e5693140eA606bcEB98761d9beB1BC87383706D"),
L1StandardBridge: common.HexToAddress("0xfA6D8Ee5BE770F84FC001D098C4bD604Fe01284a"), L1StandardBridgeProxy: common.HexToAddress("0xfA6D8Ee5BE770F84FC001D098C4bD604Fe01284a"),
// FIXME update this to the correct address
L1ERC721Bridge: common.HexToAddress("0x0000000000000000000000000000000000000000"),
}, },
// Zora mainnet // Zora mainnet
7777777: { 7777777: {
OptimismPortal: common.HexToAddress("0x1a0ad011913A150f69f6A19DF447A0CfD9551054"), OptimismPortalProxy: common.HexToAddress("0x1a0ad011913A150f69f6A19DF447A0CfD9551054"),
L2OutputOracle: common.HexToAddress("0x9E6204F750cD866b299594e2aC9eA824E2e5f95c"), L2OutputOracleProxy: common.HexToAddress("0x9E6204F750cD866b299594e2aC9eA824E2e5f95c"),
L1CrossDomainMessenger: common.HexToAddress("0xdC40a14d9abd6F410226f1E6de71aE03441ca506"), L1CrossDomainMessengerProxy: common.HexToAddress("0xdC40a14d9abd6F410226f1E6de71aE03441ca506"),
L1StandardBridge: common.HexToAddress("0x3e2Ea9B92B7E48A52296fD261dc26fd995284631"), L1StandardBridgeProxy: common.HexToAddress("0x3e2Ea9B92B7E48A52296fD261dc26fd995284631"),
// FIXME update this to the correct address
L1ERC721Bridge: common.HexToAddress("0x0000000000000000000000000000000000000000"),
}, },
// Zora goerli // Zora goerli
999: { 999: {
OptimismPortal: common.HexToAddress("0xDb9F51790365e7dc196e7D072728df39Be958ACe"), OptimismPortalProxy: common.HexToAddress("0xDb9F51790365e7dc196e7D072728df39Be958ACe"),
L2OutputOracle: common.HexToAddress("0xdD292C9eEd00f6A32Ff5245d0BCd7f2a15f24e00"), L2OutputOracleProxy: common.HexToAddress("0xdD292C9eEd00f6A32Ff5245d0BCd7f2a15f24e00"),
L1CrossDomainMessenger: common.HexToAddress("0xD87342e16352D33170557A7dA1e5fB966a60FafC"), L1CrossDomainMessengerProxy: common.HexToAddress("0xD87342e16352D33170557A7dA1e5fB966a60FafC"),
L1StandardBridge: common.HexToAddress("0x7CC09AC2452D6555d5e0C213Ab9E2d44eFbFc956"), L1StandardBridgeProxy: common.HexToAddress("0x7CC09AC2452D6555d5e0C213Ab9E2d44eFbFc956"),
// FIXME update this to the correct address
L1ERC721Bridge: common.HexToAddress("0x0000000000000000000000000000000000000000"),
}, },
} }
...@@ -38,11 +38,11 @@ func BlockHeaderFromHeader(header *types.Header) BlockHeader { ...@@ -38,11 +38,11 @@ func BlockHeaderFromHeader(header *types.Header) BlockHeader {
} }
type L1BlockHeader struct { type L1BlockHeader struct {
BlockHeader BlockHeader `gorm:"embedded"`
} }
type L2BlockHeader struct { type L2BlockHeader struct {
BlockHeader BlockHeader `gorm:"embedded"`
} }
type LegacyStateBatch struct { type LegacyStateBatch struct {
...@@ -65,24 +65,28 @@ type OutputProposal struct { ...@@ -65,24 +65,28 @@ type OutputProposal struct {
} }
type BlocksView interface { type BlocksView interface {
L1BlockHeader(*big.Int) (*L1BlockHeader, error) L1BlockHeader(common.Hash) (*L1BlockHeader, error)
LatestL1BlockHeader() (*L1BlockHeader, error) L1BlockHeaderWithFilter(BlockHeader) (*L1BlockHeader, error)
L1LatestBlockHeader() (*L1BlockHeader, error)
L2BlockHeader(common.Hash) (*L2BlockHeader, error)
L2BlockHeaderWithFilter(BlockHeader) (*L2BlockHeader, error)
L2LatestBlockHeader() (*L2BlockHeader, error)
LatestCheckpointedOutput() (*OutputProposal, error) LatestCheckpointedOutput() (*OutputProposal, error)
OutputProposal(index *big.Int) (*OutputProposal, error) OutputProposal(index *big.Int) (*OutputProposal, error)
L2BlockHeader(*big.Int) (*L2BlockHeader, error) LatestEpoch() (*Epoch, error)
LatestL2BlockHeader() (*L2BlockHeader, error)
} }
type BlocksDB interface { type BlocksDB interface {
BlocksView BlocksView
StoreL1BlockHeaders([]*L1BlockHeader) error StoreL1BlockHeaders([]L1BlockHeader) error
StoreL2BlockHeaders([]*L2BlockHeader) error StoreL2BlockHeaders([]L2BlockHeader) error
StoreLegacyStateBatches([]*LegacyStateBatch) error StoreLegacyStateBatches([]LegacyStateBatch) error
StoreOutputProposals([]*OutputProposal) error StoreOutputProposals([]OutputProposal) error
} }
/** /**
...@@ -99,36 +103,39 @@ func newBlocksDB(db *gorm.DB) BlocksDB { ...@@ -99,36 +103,39 @@ func newBlocksDB(db *gorm.DB) BlocksDB {
// L1 // L1
func (db *blocksDB) StoreL1BlockHeaders(headers []*L1BlockHeader) error { func (db *blocksDB) StoreL1BlockHeaders(headers []L1BlockHeader) error {
result := db.gorm.Create(&headers) result := db.gorm.Create(&headers)
return result.Error return result.Error
} }
func (db *blocksDB) StoreLegacyStateBatches(stateBatches []*LegacyStateBatch) error { func (db *blocksDB) StoreLegacyStateBatches(stateBatches []LegacyStateBatch) error {
result := db.gorm.Create(stateBatches) result := db.gorm.Create(stateBatches)
return result.Error return result.Error
} }
func (db *blocksDB) StoreOutputProposals(outputs []*OutputProposal) error { func (db *blocksDB) StoreOutputProposals(outputs []OutputProposal) error {
result := db.gorm.Create(outputs) result := db.gorm.Create(outputs)
return result.Error return result.Error
} }
func (db *blocksDB) L1BlockHeader(height *big.Int) (*L1BlockHeader, error) { func (db *blocksDB) L1BlockHeader(hash common.Hash) (*L1BlockHeader, error) {
return db.L1BlockHeaderWithFilter(BlockHeader{Hash: hash})
}
func (db *blocksDB) L1BlockHeaderWithFilter(filter BlockHeader) (*L1BlockHeader, error) {
var l1Header L1BlockHeader var l1Header L1BlockHeader
result := db.gorm.Where(&BlockHeader{Number: U256{Int: height}}).Take(&l1Header) result := db.gorm.Where(&filter).Take(&l1Header)
if result.Error != nil { if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) { if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil return nil, nil
} }
return nil, result.Error return nil, result.Error
} }
return &l1Header, nil return &l1Header, nil
} }
func (db *blocksDB) LatestL1BlockHeader() (*L1BlockHeader, error) { func (db *blocksDB) L1LatestBlockHeader() (*L1BlockHeader, error) {
var l1Header L1BlockHeader var l1Header L1BlockHeader
result := db.gorm.Order("number DESC").Take(&l1Header) result := db.gorm.Order("number DESC").Take(&l1Header)
if result.Error != nil { if result.Error != nil {
...@@ -172,36 +179,71 @@ func (db *blocksDB) OutputProposal(index *big.Int) (*OutputProposal, error) { ...@@ -172,36 +179,71 @@ func (db *blocksDB) OutputProposal(index *big.Int) (*OutputProposal, error) {
// L2 // L2
func (db *blocksDB) StoreL2BlockHeaders(headers []*L2BlockHeader) error { func (db *blocksDB) StoreL2BlockHeaders(headers []L2BlockHeader) error {
result := db.gorm.Create(&headers) result := db.gorm.Create(&headers)
return result.Error return result.Error
} }
func (db *blocksDB) L2BlockHeader(height *big.Int) (*L2BlockHeader, error) { func (db *blocksDB) L2BlockHeader(hash common.Hash) (*L2BlockHeader, error) {
return db.L2BlockHeaderWithFilter(BlockHeader{Hash: hash})
}
func (db *blocksDB) L2BlockHeaderWithFilter(filter BlockHeader) (*L2BlockHeader, error) {
var l2Header L2BlockHeader var l2Header L2BlockHeader
result := db.gorm.Where(&BlockHeader{Number: U256{Int: height}}).Take(&l2Header) result := db.gorm.Where(&filter).Take(&l2Header)
if result.Error != nil { if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) { if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil return nil, nil
} }
return nil, result.Error return nil, result.Error
} }
return &l2Header, nil return &l2Header, nil
} }
func (db *blocksDB) LatestL2BlockHeader() (*L2BlockHeader, error) { func (db *blocksDB) L2LatestBlockHeader() (*L2BlockHeader, error) {
var l2Header L2BlockHeader var l2Header L2BlockHeader
result := db.gorm.Order("number DESC").Take(&l2Header) result := db.gorm.Order("number DESC").Take(&l2Header)
if result.Error != nil { if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) { if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil return nil, nil
} }
return nil, result.Error return nil, result.Error
} }
result.Logger.Info(context.Background(), "number ", l2Header.Number) result.Logger.Info(context.Background(), "number ", l2Header.Number)
return &l2Header, nil return &l2Header, nil
} }
// Auxiliary Methods on both L1 & L2
type Epoch struct {
L1BlockHeader L1BlockHeader `gorm:"embedded"`
L2BlockHeader L2BlockHeader `gorm:"embedded"`
}
// LatestEpoch return the latest epoch, seen on L1 & L2. In other words
// this returns the latest indexed L1 block that has a corresponding
// indexed L2 block with a matching L1Origin (equal timestamps).
//
// For more, see the protocol spec:
// - https://github.com/ethereum-optimism/optimism/blob/develop/specs/derivation.md
func (db *blocksDB) LatestEpoch() (*Epoch, error) {
// Since L1 blocks occur less frequently than L2, we do a INNER JOIN from L1 on
// L2 for a faster query. Per the protocol, the L2 block that starts a new epoch
// will have a matching timestamp with the L1 origin.
query := db.gorm.Table("l1_block_headers").Order("l1_block_headers.timestamp DESC")
query = query.Joins("INNER JOIN l2_block_headers ON l1_block_headers.timestamp = l2_block_headers.timestamp")
query = query.Select("*")
var epoch Epoch
result := query.Take(&epoch)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &epoch, nil
}
...@@ -47,10 +47,10 @@ type BridgeMessagesView interface { ...@@ -47,10 +47,10 @@ type BridgeMessagesView interface {
type BridgeMessagesDB interface { type BridgeMessagesDB interface {
BridgeMessagesView BridgeMessagesView
StoreL1BridgeMessages([]*L1BridgeMessage) error StoreL1BridgeMessages([]L1BridgeMessage) error
MarkRelayedL1BridgeMessage(common.Hash, uuid.UUID) error MarkRelayedL1BridgeMessage(common.Hash, uuid.UUID) error
StoreL2BridgeMessages([]*L2BridgeMessage) error StoreL2BridgeMessages([]L2BridgeMessage) error
MarkRelayedL2BridgeMessage(common.Hash, uuid.UUID) error MarkRelayedL2BridgeMessage(common.Hash, uuid.UUID) error
} }
...@@ -70,7 +70,7 @@ func newBridgeMessagesDB(db *gorm.DB) BridgeMessagesDB { ...@@ -70,7 +70,7 @@ func newBridgeMessagesDB(db *gorm.DB) BridgeMessagesDB {
* Arbitrary Messages Sent from L1 * Arbitrary Messages Sent from L1
*/ */
func (db bridgeMessagesDB) StoreL1BridgeMessages(messages []*L1BridgeMessage) error { func (db bridgeMessagesDB) StoreL1BridgeMessages(messages []L1BridgeMessage) error {
result := db.gorm.Create(&messages) result := db.gorm.Create(&messages)
return result.Error return result.Error
} }
...@@ -109,7 +109,7 @@ func (db bridgeMessagesDB) MarkRelayedL1BridgeMessage(messageHash common.Hash, r ...@@ -109,7 +109,7 @@ func (db bridgeMessagesDB) MarkRelayedL1BridgeMessage(messageHash common.Hash, r
* Arbitrary Messages Sent from L2 * Arbitrary Messages Sent from L2
*/ */
func (db bridgeMessagesDB) StoreL2BridgeMessages(messages []*L2BridgeMessage) error { func (db bridgeMessagesDB) StoreL2BridgeMessages(messages []L2BridgeMessage) error {
result := db.gorm.Create(&messages) result := db.gorm.Create(&messages)
return result.Error return result.Error
} }
......
...@@ -53,9 +53,9 @@ type BridgeTransactionsView interface { ...@@ -53,9 +53,9 @@ type BridgeTransactionsView interface {
type BridgeTransactionsDB interface { type BridgeTransactionsDB interface {
BridgeTransactionsView BridgeTransactionsView
StoreL1TransactionDeposits([]*L1TransactionDeposit) error StoreL1TransactionDeposits([]L1TransactionDeposit) error
StoreL2TransactionWithdrawals([]*L2TransactionWithdrawal) error StoreL2TransactionWithdrawals([]L2TransactionWithdrawal) error
MarkL2TransactionWithdrawalProvenEvent(common.Hash, uuid.UUID) error MarkL2TransactionWithdrawalProvenEvent(common.Hash, uuid.UUID) error
MarkL2TransactionWithdrawalFinalizedEvent(common.Hash, uuid.UUID, bool) error MarkL2TransactionWithdrawalFinalizedEvent(common.Hash, uuid.UUID, bool) error
} }
...@@ -76,7 +76,7 @@ func newBridgeTransactionsDB(db *gorm.DB) BridgeTransactionsDB { ...@@ -76,7 +76,7 @@ func newBridgeTransactionsDB(db *gorm.DB) BridgeTransactionsDB {
* Transactions deposited from L1 * Transactions deposited from L1
*/ */
func (db *bridgeTransactionsDB) StoreL1TransactionDeposits(deposits []*L1TransactionDeposit) error { func (db *bridgeTransactionsDB) StoreL1TransactionDeposits(deposits []L1TransactionDeposit) error {
result := db.gorm.Create(&deposits) result := db.gorm.Create(&deposits)
return result.Error return result.Error
} }
...@@ -98,7 +98,7 @@ func (db *bridgeTransactionsDB) L1TransactionDeposit(sourceHash common.Hash) (*L ...@@ -98,7 +98,7 @@ func (db *bridgeTransactionsDB) L1TransactionDeposit(sourceHash common.Hash) (*L
* Transactions withdrawn from L2 * Transactions withdrawn from L2
*/ */
func (db *bridgeTransactionsDB) StoreL2TransactionWithdrawals(withdrawals []*L2TransactionWithdrawal) error { func (db *bridgeTransactionsDB) StoreL2TransactionWithdrawals(withdrawals []L2TransactionWithdrawal) error {
result := db.gorm.Create(&withdrawals) result := db.gorm.Create(&withdrawals)
return result.Error return result.Error
} }
......
...@@ -32,7 +32,6 @@ type BridgeTransfer struct { ...@@ -32,7 +32,6 @@ type BridgeTransfer struct {
type L1BridgeDeposit struct { type L1BridgeDeposit struct {
BridgeTransfer `gorm:"embedded"` BridgeTransfer `gorm:"embedded"`
TransactionSourceHash common.Hash `gorm:"primaryKey;serializer:json"` TransactionSourceHash common.Hash `gorm:"primaryKey;serializer:json"`
} }
...@@ -45,7 +44,6 @@ type L1BridgeDepositWithTransactionHashes struct { ...@@ -45,7 +44,6 @@ type L1BridgeDepositWithTransactionHashes struct {
type L2BridgeWithdrawal struct { type L2BridgeWithdrawal struct {
BridgeTransfer `gorm:"embedded"` BridgeTransfer `gorm:"embedded"`
TransactionWithdrawalHash common.Hash `gorm:"primaryKey;serializer:json"` TransactionWithdrawalHash common.Hash `gorm:"primaryKey;serializer:json"`
} }
...@@ -70,8 +68,8 @@ type BridgeTransfersView interface { ...@@ -70,8 +68,8 @@ type BridgeTransfersView interface {
type BridgeTransfersDB interface { type BridgeTransfersDB interface {
BridgeTransfersView BridgeTransfersView
StoreL1BridgeDeposits([]*L1BridgeDeposit) error StoreL1BridgeDeposits([]L1BridgeDeposit) error
StoreL2BridgeWithdrawals([]*L2BridgeWithdrawal) error StoreL2BridgeWithdrawals([]L2BridgeWithdrawal) error
} }
/** /**
...@@ -90,7 +88,7 @@ func newBridgeTransfersDB(db *gorm.DB) BridgeTransfersDB { ...@@ -90,7 +88,7 @@ func newBridgeTransfersDB(db *gorm.DB) BridgeTransfersDB {
* Tokens Bridged (Deposited) from L1 * Tokens Bridged (Deposited) from L1
*/ */
func (db *bridgeTransfersDB) StoreL1BridgeDeposits(deposits []*L1BridgeDeposit) error { func (db *bridgeTransfersDB) StoreL1BridgeDeposits(deposits []L1BridgeDeposit) error {
result := db.gorm.Create(&deposits) result := db.gorm.Create(&deposits)
return result.Error return result.Error
} }
...@@ -124,7 +122,7 @@ func (db *bridgeTransfersDB) L1BridgeDepositWithFilter(filter BridgeTransfer) (* ...@@ -124,7 +122,7 @@ func (db *bridgeTransfersDB) L1BridgeDepositWithFilter(filter BridgeTransfer) (*
} }
type L1BridgeDepositsResponse struct { type L1BridgeDepositsResponse struct {
Deposits []*L1BridgeDepositWithTransactionHashes Deposits []L1BridgeDepositWithTransactionHashes
Cursor string Cursor string
HasNextPage bool HasNextPage bool
} }
...@@ -152,7 +150,7 @@ l1_transaction_deposits.l2_transaction_hash`) ...@@ -152,7 +150,7 @@ l1_transaction_deposits.l2_transaction_hash`)
filteredQuery := depositsQuery.Where(&Transaction{FromAddress: address}).Order("l1_bridge_deposits.transaction_source_hash DESC").Limit(limit + 1) filteredQuery := depositsQuery.Where(&Transaction{FromAddress: address}).Order("l1_bridge_deposits.transaction_source_hash DESC").Limit(limit + 1)
deposits := []*L1BridgeDepositWithTransactionHashes{} deposits := []L1BridgeDepositWithTransactionHashes{}
result := filteredQuery.Scan(&deposits) result := filteredQuery.Scan(&deposits)
if result.Error != nil { if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) { if errors.Is(result.Error, gorm.ErrRecordNotFound) {
...@@ -185,7 +183,7 @@ l1_transaction_deposits.l2_transaction_hash`) ...@@ -185,7 +183,7 @@ l1_transaction_deposits.l2_transaction_hash`)
* Tokens Bridged (Withdrawn) from L2 * Tokens Bridged (Withdrawn) from L2
*/ */
func (db *bridgeTransfersDB) StoreL2BridgeWithdrawals(withdrawals []*L2BridgeWithdrawal) error { func (db *bridgeTransfersDB) StoreL2BridgeWithdrawals(withdrawals []L2BridgeWithdrawal) error {
result := db.gorm.Create(&withdrawals) result := db.gorm.Create(&withdrawals)
return result.Error return result.Error
} }
...@@ -219,7 +217,7 @@ func (db *bridgeTransfersDB) L2BridgeWithdrawalWithFilter(filter BridgeTransfer) ...@@ -219,7 +217,7 @@ func (db *bridgeTransfersDB) L2BridgeWithdrawalWithFilter(filter BridgeTransfer)
} }
type L2BridgeWithdrawalsResponse struct { type L2BridgeWithdrawalsResponse struct {
Withdrawals []*L2BridgeWithdrawalWithTransactionHashes Withdrawals []L2BridgeWithdrawalWithTransactionHashes
Cursor string Cursor string
HasNextPage bool HasNextPage bool
} }
...@@ -249,7 +247,7 @@ finalized_l1_contract_events.transaction_hash AS finalized_l1_transaction_hash`) ...@@ -249,7 +247,7 @@ finalized_l1_contract_events.transaction_hash AS finalized_l1_transaction_hash`)
filteredQuery := withdrawalsQuery.Where(&Transaction{FromAddress: address}).Order("l2_bridge_withdrawals.timestamp DESC").Limit(limit + 1) filteredQuery := withdrawalsQuery.Where(&Transaction{FromAddress: address}).Order("l2_bridge_withdrawals.timestamp DESC").Limit(limit + 1)
withdrawals := []*L2BridgeWithdrawalWithTransactionHashes{} withdrawals := []L2BridgeWithdrawalWithTransactionHashes{}
result := filteredQuery.Scan(&withdrawals) result := filteredQuery.Scan(&withdrawals)
if result.Error != nil { if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) { if errors.Is(result.Error, gorm.ErrRecordNotFound) {
......
...@@ -2,6 +2,7 @@ package database ...@@ -2,6 +2,7 @@ package database
import ( import (
"errors" "errors"
"fmt"
"math/big" "math/big"
"gorm.io/gorm" "gorm.io/gorm"
...@@ -74,19 +75,23 @@ type L2ContractEvent struct { ...@@ -74,19 +75,23 @@ type L2ContractEvent struct {
type ContractEventsView interface { type ContractEventsView interface {
L1ContractEvent(uuid.UUID) (*L1ContractEvent, error) L1ContractEvent(uuid.UUID) (*L1ContractEvent, error)
L1ContractEventByTxLogIndex(common.Hash, uint64) (*L1ContractEvent, error) L1ContractEventWithFilter(ContractEvent) (*L1ContractEvent, error)
L1ContractEventsWithFilter(ContractEvent, *big.Int, *big.Int) ([]L1ContractEvent, error) L1ContractEventsWithFilter(ContractEvent, *big.Int, *big.Int) ([]L1ContractEvent, error)
L1LatestContractEventWithFilter(ContractEvent) (*L1ContractEvent, error)
L2ContractEvent(uuid.UUID) (*L2ContractEvent, error) L2ContractEvent(uuid.UUID) (*L2ContractEvent, error)
L2ContractEventByTxLogIndex(common.Hash, uint64) (*L2ContractEvent, error) L2ContractEventWithFilter(ContractEvent) (*L2ContractEvent, error)
L2ContractEventsWithFilter(ContractEvent, *big.Int, *big.Int) ([]L2ContractEvent, error) L2ContractEventsWithFilter(ContractEvent, *big.Int, *big.Int) ([]L2ContractEvent, error)
L2LatestContractEventWithFilter(ContractEvent) (*L2ContractEvent, error)
ContractEventsWithFilter(ContractEvent, string, *big.Int, *big.Int) ([]ContractEvent, error)
} }
type ContractEventsDB interface { type ContractEventsDB interface {
ContractEventsView ContractEventsView
StoreL1ContractEvents([]*L1ContractEvent) error StoreL1ContractEvents([]L1ContractEvent) error
StoreL2ContractEvents([]*L2ContractEvent) error StoreL2ContractEvents([]L2ContractEvent) error
} }
/** /**
...@@ -103,33 +108,22 @@ func newContractEventsDB(db *gorm.DB) ContractEventsDB { ...@@ -103,33 +108,22 @@ func newContractEventsDB(db *gorm.DB) ContractEventsDB {
// L1 // L1
func (db *contractEventsDB) StoreL1ContractEvents(events []*L1ContractEvent) error { func (db *contractEventsDB) StoreL1ContractEvents(events []L1ContractEvent) error {
result := db.gorm.Create(&events) result := db.gorm.Create(&events)
return result.Error return result.Error
} }
func (db *contractEventsDB) L1ContractEvent(uuid uuid.UUID) (*L1ContractEvent, error) { func (db *contractEventsDB) L1ContractEvent(uuid uuid.UUID) (*L1ContractEvent, error) {
var l1ContractEvent L1ContractEvent return db.L1ContractEventWithFilter(ContractEvent{GUID: uuid})
result := db.gorm.Where(&ContractEvent{GUID: uuid}).Take(&l1ContractEvent)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &l1ContractEvent, nil
} }
func (db *contractEventsDB) L1ContractEventByTxLogIndex(txHash common.Hash, logIndex uint64) (*L1ContractEvent, error) { func (db *contractEventsDB) L1ContractEventWithFilter(filter ContractEvent) (*L1ContractEvent, error) {
var l1ContractEvent L1ContractEvent var l1ContractEvent L1ContractEvent
result := db.gorm.Where(&ContractEvent{TransactionHash: txHash, LogIndex: logIndex}).Take(&l1ContractEvent) result := db.gorm.Where(&filter).Take(&l1ContractEvent)
if result.Error != nil { if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) { if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil return nil, nil
} }
return nil, result.Error return nil, result.Error
} }
...@@ -140,6 +134,12 @@ func (db *contractEventsDB) L1ContractEventsWithFilter(filter ContractEvent, fro ...@@ -140,6 +134,12 @@ func (db *contractEventsDB) L1ContractEventsWithFilter(filter ContractEvent, fro
if fromHeight == nil { if fromHeight == nil {
fromHeight = big.NewInt(0) fromHeight = big.NewInt(0)
} }
if toHeight == nil {
return nil, errors.New("end height unspecified")
}
if fromHeight.Cmp(toHeight) > 0 {
return nil, fmt.Errorf("fromHeight %d is greater than toHeight %d", fromHeight, toHeight)
}
query := db.gorm.Table("l1_contract_events").Where(&filter) query := db.gorm.Table("l1_contract_events").Where(&filter)
query = query.Joins("INNER JOIN l1_block_headers ON l1_contract_events.block_hash = l1_block_headers.hash") query = query.Joins("INNER JOIN l1_block_headers ON l1_contract_events.block_hash = l1_block_headers.hash")
...@@ -160,35 +160,37 @@ func (db *contractEventsDB) L1ContractEventsWithFilter(filter ContractEvent, fro ...@@ -160,35 +160,37 @@ func (db *contractEventsDB) L1ContractEventsWithFilter(filter ContractEvent, fro
return events, nil return events, nil
} }
// L2 func (db *contractEventsDB) L1LatestContractEventWithFilter(filter ContractEvent) (*L1ContractEvent, error) {
var l1ContractEvent L1ContractEvent
func (db *contractEventsDB) StoreL2ContractEvents(events []*L2ContractEvent) error { result := db.gorm.Where(&filter).Order("timestamp DESC").Take(&l1ContractEvent)
result := db.gorm.Create(&events)
return result.Error
}
func (db *contractEventsDB) L2ContractEvent(uuid uuid.UUID) (*L2ContractEvent, error) {
var l2ContractEvent L2ContractEvent
result := db.gorm.Where(&ContractEvent{GUID: uuid}).Take(&l2ContractEvent)
if result.Error != nil { if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) { if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil return nil, nil
} }
return nil, result.Error return nil, result.Error
} }
return &l2ContractEvent, nil return &l1ContractEvent, nil
} }
func (db *contractEventsDB) L2ContractEventByTxLogIndex(txHash common.Hash, logIndex uint64) (*L2ContractEvent, error) { // L2
func (db *contractEventsDB) StoreL2ContractEvents(events []L2ContractEvent) error {
result := db.gorm.Create(&events)
return result.Error
}
func (db *contractEventsDB) L2ContractEvent(uuid uuid.UUID) (*L2ContractEvent, error) {
return db.L2ContractEventWithFilter(ContractEvent{GUID: uuid})
}
func (db *contractEventsDB) L2ContractEventWithFilter(filter ContractEvent) (*L2ContractEvent, error) {
var l2ContractEvent L2ContractEvent var l2ContractEvent L2ContractEvent
result := db.gorm.Where(&ContractEvent{TransactionHash: txHash, LogIndex: logIndex}).Take(&l2ContractEvent) result := db.gorm.Where(&filter).Take(&l2ContractEvent)
if result.Error != nil { if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) { if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil return nil, nil
} }
return nil, result.Error return nil, result.Error
} }
...@@ -199,6 +201,12 @@ func (db *contractEventsDB) L2ContractEventsWithFilter(filter ContractEvent, fro ...@@ -199,6 +201,12 @@ func (db *contractEventsDB) L2ContractEventsWithFilter(filter ContractEvent, fro
if fromHeight == nil { if fromHeight == nil {
fromHeight = big.NewInt(0) fromHeight = big.NewInt(0)
} }
if toHeight == nil {
return nil, errors.New("end height unspecified")
}
if fromHeight.Cmp(toHeight) > 0 {
return nil, fmt.Errorf("fromHeight %d is greater than toHeight %d", fromHeight, toHeight)
}
query := db.gorm.Table("l2_contract_events").Where(&filter) query := db.gorm.Table("l2_contract_events").Where(&filter)
query = query.Joins("INNER JOIN l2_block_headers ON l2_contract_events.block_hash = l2_block_headers.hash") query = query.Joins("INNER JOIN l2_block_headers ON l2_contract_events.block_hash = l2_block_headers.hash")
...@@ -218,3 +226,46 @@ func (db *contractEventsDB) L2ContractEventsWithFilter(filter ContractEvent, fro ...@@ -218,3 +226,46 @@ func (db *contractEventsDB) L2ContractEventsWithFilter(filter ContractEvent, fro
return events, nil return events, nil
} }
func (db *contractEventsDB) L2LatestContractEventWithFilter(filter ContractEvent) (*L2ContractEvent, error) {
var l2ContractEvent L2ContractEvent
result := db.gorm.Where(&filter).Order("timestamp DESC").Take(&l2ContractEvent)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
return &l2ContractEvent, nil
}
// Auxiliary methods for both L1 and L2
// ContractEventsWithFilter will retrieve contract events within the specified range according to the `chainSelector`.
func (db *contractEventsDB) ContractEventsWithFilter(filter ContractEvent, chainSelector string, fromHeight, toHeight *big.Int) ([]ContractEvent, error) {
switch chainSelector {
case "l1":
l1Events, err := db.L1ContractEventsWithFilter(filter, fromHeight, toHeight)
if err != nil {
return nil, err
}
events := make([]ContractEvent, len(l1Events))
for i := range l1Events {
events[i] = l1Events[i].ContractEvent
}
return events, nil
case "l2":
l2Events, err := db.L2ContractEventsWithFilter(filter, fromHeight, toHeight)
if err != nil {
return nil, err
}
events := make([]ContractEvent, len(l2Events))
for i := range l2Events {
events[i] = l2Events[i].ContractEvent
}
return events, nil
default:
return nil, errors.New("expected 'l1' or 'l2' for chain selection")
}
}
...@@ -32,9 +32,6 @@ func TestE2EBridgeL1CrossDomainMessenger(t *testing.T) { ...@@ -32,9 +32,6 @@ func TestE2EBridgeL1CrossDomainMessenger(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
l1Opts.Value = big.NewInt(params.Ether) l1Opts.Value = big.NewInt(params.Ether)
// Pause the processor to track relayed event
testSuite.Indexer.L2Processor.PauseForTest()
// (1) Send the Message // (1) Send the Message
sentMsgTx, err := l1CrossDomainMessenger.SendMessage(l1Opts, aliceAddr, calldata, 100_000) sentMsgTx, err := l1CrossDomainMessenger.SendMessage(l1Opts, aliceAddr, calldata, 100_000)
require.NoError(t, err) require.NoError(t, err)
...@@ -46,7 +43,7 @@ func TestE2EBridgeL1CrossDomainMessenger(t *testing.T) { ...@@ -46,7 +43,7 @@ func TestE2EBridgeL1CrossDomainMessenger(t *testing.T) {
// wait for processor catchup // wait for processor catchup
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader() l1Header := testSuite.Indexer.BridgeProcessor.LatestL1Header
return l1Header != nil && l1Header.Number.Uint64() >= sentMsgReceipt.BlockNumber.Uint64(), nil return l1Header != nil && l1Header.Number.Uint64() >= sentMsgReceipt.BlockNumber.Uint64(), nil
})) }))
...@@ -70,17 +67,18 @@ func TestE2EBridgeL1CrossDomainMessenger(t *testing.T) { ...@@ -70,17 +67,18 @@ func TestE2EBridgeL1CrossDomainMessenger(t *testing.T) {
require.ElementsMatch(t, calldata, sentMessage.Tx.Data) require.ElementsMatch(t, calldata, sentMessage.Tx.Data)
// (2) Process RelayedMesssage on inclusion // (2) Process RelayedMesssage on inclusion
require.Nil(t, sentMessage.RelayedMessageEventGUID) // - We dont assert that `RelayedMessageEventGUID` is nil prior to inclusion since there isn't a
testSuite.Indexer.L2Processor.ResumeForTest() // a straightforward way of pausing/resuming the processors at the right time. The codepath is the
// same for L2->L1 messages which does check for this so we are still covered
transaction, err := testSuite.DB.BridgeTransactions.L1TransactionDeposit(sentMessage.TransactionSourceHash) transaction, err := testSuite.DB.BridgeTransactions.L1TransactionDeposit(sentMessage.TransactionSourceHash)
require.NoError(t, err) require.NoError(t, err)
// wait for processor catchup // wait for processor catchup
depositReceipt, err := wait.ForReceiptOK(context.Background(), testSuite.L2Client, transaction.L2TransactionHash) l2DepositReceipt, err := wait.ForReceiptOK(context.Background(), testSuite.L2Client, transaction.L2TransactionHash)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader() l2Header := testSuite.Indexer.BridgeProcessor.LatestL2Header
return l2Header != nil && l2Header.Number.Uint64() >= depositReceipt.BlockNumber.Uint64(), nil return l2Header != nil && l2Header.Number.Uint64() >= l2DepositReceipt.BlockNumber.Uint64(), nil
})) }))
sentMessage, err = testSuite.DB.BridgeMessages.L1BridgeMessage(parsedMessage.MessageHash) sentMessage, err = testSuite.DB.BridgeMessages.L1BridgeMessage(parsedMessage.MessageHash)
...@@ -132,7 +130,7 @@ func TestE2EBridgeL2CrossDomainMessenger(t *testing.T) { ...@@ -132,7 +130,7 @@ func TestE2EBridgeL2CrossDomainMessenger(t *testing.T) {
// wait for processor catchup // wait for processor catchup
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader() l2Header := testSuite.Indexer.BridgeProcessor.LatestL2Header
return l2Header != nil && l2Header.Number.Uint64() >= sentMsgReceipt.BlockNumber.Uint64(), nil return l2Header != nil && l2Header.Number.Uint64() >= sentMsgReceipt.BlockNumber.Uint64(), nil
})) }))
...@@ -161,7 +159,7 @@ func TestE2EBridgeL2CrossDomainMessenger(t *testing.T) { ...@@ -161,7 +159,7 @@ func TestE2EBridgeL2CrossDomainMessenger(t *testing.T) {
// wait for processor catchup // wait for processor catchup
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader() l1Header := testSuite.Indexer.BridgeProcessor.LatestL1Header
return l1Header != nil && l1Header.Number.Uint64() >= finalizedReceipt.BlockNumber.Uint64(), nil return l1Header != nil && l1Header.Number.Uint64() >= finalizedReceipt.BlockNumber.Uint64(), nil
})) }))
......
...@@ -46,12 +46,13 @@ func TestE2EBridgeTransactionsOptimismPortalDeposits(t *testing.T) { ...@@ -46,12 +46,13 @@ func TestE2EBridgeTransactionsOptimismPortalDeposits(t *testing.T) {
// wait for processor catchup // wait for processor catchup
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader() l1Header := testSuite.Indexer.BridgeProcessor.LatestL1Header
return l1Header != nil && l1Header.Number.Uint64() >= depositReceipt.BlockNumber.Uint64(), nil return l1Header != nil && l1Header.Number.Uint64() >= depositReceipt.BlockNumber.Uint64(), nil
})) }))
deposit, err := testSuite.DB.BridgeTransactions.L1TransactionDeposit(depositInfo.DepositTx.SourceHash) deposit, err := testSuite.DB.BridgeTransactions.L1TransactionDeposit(depositInfo.DepositTx.SourceHash)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, deposit)
require.Equal(t, depositL2TxHash, deposit.L2TransactionHash) require.Equal(t, depositL2TxHash, deposit.L2TransactionHash)
require.Equal(t, big.NewInt(100_000), deposit.GasLimit.Int) require.Equal(t, big.NewInt(100_000), deposit.GasLimit.Int)
require.Equal(t, big.NewInt(params.Ether), deposit.Tx.Amount.Int) require.Equal(t, big.NewInt(params.Ether), deposit.Tx.Amount.Int)
...@@ -100,7 +101,7 @@ func TestE2EBridgeTransactionsL2ToL1MessagePasserWithdrawal(t *testing.T) { ...@@ -100,7 +101,7 @@ func TestE2EBridgeTransactionsL2ToL1MessagePasserWithdrawal(t *testing.T) {
// wait for processor catchup // wait for processor catchup
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader() l2Header := testSuite.Indexer.BridgeProcessor.LatestL2Header
return l2Header != nil && l2Header.Number.Uint64() >= withdrawReceipt.BlockNumber.Uint64(), nil return l2Header != nil && l2Header.Number.Uint64() >= withdrawReceipt.BlockNumber.Uint64(), nil
})) }))
...@@ -111,6 +112,7 @@ func TestE2EBridgeTransactionsL2ToL1MessagePasserWithdrawal(t *testing.T) { ...@@ -111,6 +112,7 @@ func TestE2EBridgeTransactionsL2ToL1MessagePasserWithdrawal(t *testing.T) {
withdraw, err := testSuite.DB.BridgeTransactions.L2TransactionWithdrawal(withdrawalHash) withdraw, err := testSuite.DB.BridgeTransactions.L2TransactionWithdrawal(withdrawalHash)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, withdraw)
require.Equal(t, msgPassed.Nonce.Uint64(), withdraw.Nonce.Int.Uint64()) require.Equal(t, msgPassed.Nonce.Uint64(), withdraw.Nonce.Int.Uint64())
require.Equal(t, big.NewInt(100_000), withdraw.GasLimit.Int) require.Equal(t, big.NewInt(100_000), withdraw.GasLimit.Int)
require.Equal(t, big.NewInt(params.Ether), withdraw.Tx.Amount.Int) require.Equal(t, big.NewInt(params.Ether), withdraw.Tx.Amount.Int)
...@@ -129,7 +131,7 @@ func TestE2EBridgeTransactionsL2ToL1MessagePasserWithdrawal(t *testing.T) { ...@@ -129,7 +131,7 @@ func TestE2EBridgeTransactionsL2ToL1MessagePasserWithdrawal(t *testing.T) {
withdrawParams, proveReceipt := op_e2e.ProveWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpSys.Nodes["sequencer"], testSuite.OpCfg.Secrets.Alice, withdrawReceipt) withdrawParams, proveReceipt := op_e2e.ProveWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpSys.Nodes["sequencer"], testSuite.OpCfg.Secrets.Alice, withdrawReceipt)
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader() l1Header := testSuite.Indexer.BridgeProcessor.LatestL1Header
return l1Header != nil && l1Header.Number.Uint64() >= proveReceipt.BlockNumber.Uint64(), nil return l1Header != nil && l1Header.Number.Uint64() >= proveReceipt.BlockNumber.Uint64(), nil
})) }))
...@@ -147,7 +149,7 @@ func TestE2EBridgeTransactionsL2ToL1MessagePasserWithdrawal(t *testing.T) { ...@@ -147,7 +149,7 @@ func TestE2EBridgeTransactionsL2ToL1MessagePasserWithdrawal(t *testing.T) {
finalizeReceipt := op_e2e.FinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpCfg.Secrets.Alice, proveReceipt, withdrawParams) finalizeReceipt := op_e2e.FinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpCfg.Secrets.Alice, proveReceipt, withdrawParams)
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader() l1Header := testSuite.Indexer.BridgeProcessor.LatestL1Header
return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil
})) }))
...@@ -189,7 +191,7 @@ func TestE2EBridgeTransactionsL2ToL1MessagePasserFailedWithdrawal(t *testing.T) ...@@ -189,7 +191,7 @@ func TestE2EBridgeTransactionsL2ToL1MessagePasserFailedWithdrawal(t *testing.T)
// Prove&Finalize withdrawal // Prove&Finalize withdrawal
_, finalizeReceipt := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpSys.Nodes["sequencer"], testSuite.OpCfg.Secrets.Alice, withdrawReceipt) _, finalizeReceipt := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpSys.Nodes["sequencer"], testSuite.OpCfg.Secrets.Alice, withdrawReceipt)
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader() l1Header := testSuite.Indexer.BridgeProcessor.LatestL1Header
return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil
})) }))
......
...@@ -43,7 +43,7 @@ func TestE2EBridgeTransfersStandardBridgeETHDeposit(t *testing.T) { ...@@ -43,7 +43,7 @@ func TestE2EBridgeTransfersStandardBridgeETHDeposit(t *testing.T) {
// wait for processor catchup // wait for processor catchup
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader() l1Header := testSuite.Indexer.BridgeProcessor.LatestL1Header
return l1Header != nil && l1Header.Number.Uint64() >= depositReceipt.BlockNumber.Uint64(), nil return l1Header != nil && l1Header.Number.Uint64() >= depositReceipt.BlockNumber.Uint64(), nil
})) }))
...@@ -73,11 +73,11 @@ func TestE2EBridgeTransfersStandardBridgeETHDeposit(t *testing.T) { ...@@ -73,11 +73,11 @@ func TestE2EBridgeTransfersStandardBridgeETHDeposit(t *testing.T) {
require.NotNil(t, deposit.CrossDomainMessageHash) require.NotNil(t, deposit.CrossDomainMessageHash)
// (2) Test Deposit Finalization via CrossDomainMessenger relayed message // (2) Test Deposit Finalization via CrossDomainMessenger relayed message
depositReceipt, err = wait.ForReceiptOK(context.Background(), testSuite.L2Client, types.NewTx(depositInfo.DepositTx).Hash()) l2DepositReceipt, err := wait.ForReceiptOK(context.Background(), testSuite.L2Client, types.NewTx(depositInfo.DepositTx).Hash())
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader() l2Header := testSuite.Indexer.BridgeProcessor.LatestL2Header
return l2Header != nil && l2Header.Number.Uint64() >= depositReceipt.BlockNumber.Uint64(), nil return l2Header != nil && l2Header.Number.Uint64() >= l2DepositReceipt.BlockNumber.Uint64(), nil
})) }))
crossDomainBridgeMessage, err := testSuite.DB.BridgeMessages.L1BridgeMessage(*deposit.CrossDomainMessageHash) crossDomainBridgeMessage, err := testSuite.DB.BridgeMessages.L1BridgeMessage(*deposit.CrossDomainMessageHash)
...@@ -221,7 +221,7 @@ func TestE2EBridgeTransfersOptimismPortalETHReceive(t *testing.T) { ...@@ -221,7 +221,7 @@ func TestE2EBridgeTransfersOptimismPortalETHReceive(t *testing.T) {
// wait for processor catchup // wait for processor catchup
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader() l1Header := testSuite.Indexer.BridgeProcessor.LatestL1Header
return l1Header != nil && l1Header.Number.Uint64() >= portalDepositReceipt.BlockNumber.Uint64(), nil return l1Header != nil && l1Header.Number.Uint64() >= portalDepositReceipt.BlockNumber.Uint64(), nil
})) }))
...@@ -242,11 +242,11 @@ func TestE2EBridgeTransfersOptimismPortalETHReceive(t *testing.T) { ...@@ -242,11 +242,11 @@ func TestE2EBridgeTransfersOptimismPortalETHReceive(t *testing.T) {
require.Nil(t, deposit.CrossDomainMessageHash) require.Nil(t, deposit.CrossDomainMessageHash)
// (2) Test Deposit Finalization // (2) Test Deposit Finalization
depositReceipt, err := wait.ForReceiptOK(context.Background(), testSuite.L2Client, types.NewTx(depositInfo.DepositTx).Hash()) l2DepositReceipt, err := wait.ForReceiptOK(context.Background(), testSuite.L2Client, types.NewTx(depositInfo.DepositTx).Hash())
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader() l2Header := testSuite.Indexer.BridgeProcessor.LatestL2Header
return l2Header != nil && l2Header.Number.Uint64() >= depositReceipt.BlockNumber.Uint64(), nil return l2Header != nil && l2Header.Number.Uint64() >= l2DepositReceipt.BlockNumber.Uint64(), nil
})) }))
// Still nil as the withdrawal did not occur through the standard bridge // Still nil as the withdrawal did not occur through the standard bridge
...@@ -286,7 +286,7 @@ func TestE2EBridgeTransfersStandardBridgeETHWithdrawal(t *testing.T) { ...@@ -286,7 +286,7 @@ func TestE2EBridgeTransfersStandardBridgeETHWithdrawal(t *testing.T) {
// wait for processor catchup // wait for processor catchup
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader() l2Header := testSuite.Indexer.BridgeProcessor.LatestL2Header
return l2Header != nil && l2Header.Number.Uint64() >= withdrawReceipt.BlockNumber.Uint64(), nil return l2Header != nil && l2Header.Number.Uint64() >= withdrawReceipt.BlockNumber.Uint64(), nil
})) }))
...@@ -324,7 +324,7 @@ func TestE2EBridgeTransfersStandardBridgeETHWithdrawal(t *testing.T) { ...@@ -324,7 +324,7 @@ func TestE2EBridgeTransfersStandardBridgeETHWithdrawal(t *testing.T) {
// wait for processor catchup // wait for processor catchup
proveReceipt, finalizeReceipt := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpSys.Nodes["sequencer"], testSuite.OpCfg.Secrets.Alice, withdrawReceipt) proveReceipt, finalizeReceipt := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpSys.Nodes["sequencer"], testSuite.OpCfg.Secrets.Alice, withdrawReceipt)
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader() l1Header := testSuite.Indexer.BridgeProcessor.LatestL1Header
return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil
})) }))
...@@ -370,7 +370,7 @@ func TestE2EBridgeTransfersL2ToL1MessagePasserReceive(t *testing.T) { ...@@ -370,7 +370,7 @@ func TestE2EBridgeTransfersL2ToL1MessagePasserReceive(t *testing.T) {
// wait for processor catchup // wait for processor catchup
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader() l2Header := testSuite.Indexer.BridgeProcessor.LatestL2Header
return l2Header != nil && l2Header.Number.Uint64() >= l2ToL1WithdrawReceipt.BlockNumber.Uint64(), nil return l2Header != nil && l2Header.Number.Uint64() >= l2ToL1WithdrawReceipt.BlockNumber.Uint64(), nil
})) }))
...@@ -402,7 +402,7 @@ func TestE2EBridgeTransfersL2ToL1MessagePasserReceive(t *testing.T) { ...@@ -402,7 +402,7 @@ func TestE2EBridgeTransfersL2ToL1MessagePasserReceive(t *testing.T) {
// wait for processor catchup // wait for processor catchup
proveReceipt, finalizeReceipt := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpSys.Nodes["sequencer"], testSuite.OpCfg.Secrets.Alice, l2ToL1WithdrawReceipt) proveReceipt, finalizeReceipt := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpSys.Nodes["sequencer"], testSuite.OpCfg.Secrets.Alice, l2ToL1WithdrawReceipt)
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader() l1Header := testSuite.Indexer.BridgeProcessor.LatestL1Header
return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil
})) }))
......
...@@ -6,43 +6,45 @@ import ( ...@@ -6,43 +6,45 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum-optimism/optimism/indexer/node" "github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"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"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestE2EBlockHeaders(t *testing.T) { func TestE2EETL(t *testing.T) {
testSuite := createE2ETestSuite(t) testSuite := createE2ETestSuite(t)
l2OutputOracle, err := bindings.NewL2OutputOracle(testSuite.OpCfg.L1Deployments.L2OutputOracleProxy, testSuite.L1Client) l2OutputOracle, err := bindings.NewL2OutputOracle(testSuite.OpCfg.L1Deployments.L2OutputOracleProxy, testSuite.L1Client)
require.NoError(t, err) require.NoError(t, err)
// wait for at least 10 L2 blocks to be created & posted on L1 // wait for at least 10 L2 blocks posted on L1
require.NoError(t, wait.For(context.Background(), time.Second, func() (bool, error) { require.NoError(t, wait.For(context.Background(), time.Second, func() (bool, error) {
l2Height, err := l2OutputOracle.LatestBlockNumber(&bind.CallOpts{Context: context.Background()}) l2Height, err := l2OutputOracle.LatestBlockNumber(&bind.CallOpts{Context: context.Background()})
return l2Height != nil && l2Height.Uint64() >= 9, err return l2Height != nil && l2Height.Uint64() >= 9, err
})) }))
// ensure the processors are caught up to this state // ensure we've indexed up to this state
l1Height, err := testSuite.L1Client.BlockNumber(context.Background()) l1Height, err := testSuite.L1Client.BlockNumber(context.Background())
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, wait.For(context.Background(), time.Second, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 100*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.L1Processor.LatestProcessedHeader() l1Header, err := testSuite.DB.Blocks.L1LatestBlockHeader()
l2Header := testSuite.Indexer.L2Processor.LatestProcessedHeader() require.NoError(t, err)
return (l1Header != nil && l1Header.Number.Uint64() >= l1Height) && (l2Header != nil && l2Header.Number.Uint64() >= 9), nil
l2Header, err := testSuite.DB.Blocks.L2LatestBlockHeader()
require.NoError(t, err)
return (l1Header != nil && l1Header.Number.Int.Uint64() >= l1Height) && (l2Header != nil && l2Header.Number.Int.Uint64() >= 9), nil
})) }))
t.Run("indexes L2 blocks", func(t *testing.T) { t.Run("indexes all L2 blocks", func(t *testing.T) {
latestL2Header, err := testSuite.DB.Blocks.LatestL2BlockHeader() latestL2Header, err := testSuite.DB.Blocks.L2LatestBlockHeader()
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, latestL2Header) require.NotNil(t, latestL2Header)
require.True(t, latestL2Header.Number.Int.Uint64() >= 9) require.True(t, latestL2Header.Number.Int.Uint64() >= 9)
...@@ -50,7 +52,7 @@ func TestE2EBlockHeaders(t *testing.T) { ...@@ -50,7 +52,7 @@ func TestE2EBlockHeaders(t *testing.T) {
for i := int64(0); i < 10; i++ { for i := int64(0); i < 10; i++ {
height := big.NewInt(i) height := big.NewInt(i)
indexedHeader, err := testSuite.DB.Blocks.L2BlockHeader(height) indexedHeader, err := testSuite.DB.Blocks.L2BlockHeaderWithFilter(database.BlockHeader{Number: database.U256{Int: height}})
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, indexedHeader) require.NotNil(t, indexedHeader)
...@@ -63,11 +65,14 @@ func TestE2EBlockHeaders(t *testing.T) { ...@@ -63,11 +65,14 @@ func TestE2EBlockHeaders(t *testing.T) {
require.Equal(t, header.ParentHash, indexedHeader.ParentHash) require.Equal(t, header.ParentHash, indexedHeader.ParentHash)
require.Equal(t, header.Time, indexedHeader.Timestamp) require.Equal(t, header.Time, indexedHeader.Timestamp)
// ensure the right rlp encoding is stored. checking the hashes sufficies // ensure the right rlp encoding is stored. checking the hashes
// suffices as it is based on the rlp bytes of the header
require.Equal(t, header.Hash(), indexedHeader.RLPHeader.Hash()) require.Equal(t, header.Hash(), indexedHeader.RLPHeader.Hash())
} }
}) })
/*
TODO: ADD THIS BACK IN WHEN THESE MARKERS ARE INDEXED
t.Run("indexes L2 checkpoints", func(t *testing.T) { t.Run("indexes L2 checkpoints", func(t *testing.T) {
latestOutput, err := testSuite.DB.Blocks.LatestCheckpointedOutput() latestOutput, err := testSuite.DB.Blocks.LatestCheckpointedOutput()
require.NoError(t, err) require.NoError(t, err)
...@@ -103,19 +108,18 @@ func TestE2EBlockHeaders(t *testing.T) { ...@@ -103,19 +108,18 @@ func TestE2EBlockHeaders(t *testing.T) {
require.Equal(t, crypto.Keccak256Hash(outputRootPreImage[:]), output.OutputRoot) require.Equal(t, crypto.Keccak256Hash(outputRootPreImage[:]), output.OutputRoot)
} }
}) })
*/
t.Run("indexes L1 logs and associated blocks", func(t *testing.T) { t.Run("indexes L1 blocks with accompanying contract event", func(t *testing.T) {
testCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
l1Contracts := []common.Address{} l1Contracts := []common.Address{}
testSuite.OpCfg.L1Deployments.ForEach(func(name string, addr common.Address) { l1Contracts = append(l1Contracts, addr) }) testSuite.OpCfg.L1Deployments.ForEach(func(name string, addr common.Address) { l1Contracts = append(l1Contracts, addr) })
logFilter := ethereum.FilterQuery{FromBlock: big.NewInt(0), ToBlock: big.NewInt(int64(l1Height)), Addresses: l1Contracts} logFilter := ethereum.FilterQuery{FromBlock: big.NewInt(0), ToBlock: big.NewInt(int64(l1Height)), Addresses: l1Contracts}
logs, err := testSuite.L1Client.FilterLogs(testCtx, logFilter) // []types.Log logs, err := testSuite.L1Client.FilterLogs(context.Background(), logFilter) // []types.Log
require.NoError(t, err) require.NoError(t, err)
for _, log := range logs { for i := range logs {
contractEvent, err := testSuite.DB.ContractEvents.L1ContractEventByTxLogIndex(log.TxHash, uint64(log.Index)) log := logs[i]
contractEvent, err := testSuite.DB.ContractEvents.L1ContractEventWithFilter(database.ContractEvent{TransactionHash: log.TxHash, LogIndex: uint64(log.Index)})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, log.Topics[0], contractEvent.EventSignature) require.Equal(t, log.Topics[0], contractEvent.EventSignature)
require.Equal(t, log.BlockHash, contractEvent.BlockHash) require.Equal(t, log.BlockHash, contractEvent.BlockHash)
...@@ -131,12 +135,12 @@ func TestE2EBlockHeaders(t *testing.T) { ...@@ -131,12 +135,12 @@ func TestE2EBlockHeaders(t *testing.T) {
require.ElementsMatch(t, logRlp, contractEventRlp) require.ElementsMatch(t, logRlp, contractEventRlp)
// ensure the block is also indexed // ensure the block is also indexed
block, err := testSuite.L1Client.BlockByNumber(testCtx, big.NewInt(int64(log.BlockNumber))) block, err := testSuite.L1Client.BlockByNumber(context.Background(), big.NewInt(int64(log.BlockNumber)))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, block.Time(), contractEvent.Timestamp) require.Equal(t, block.Time(), contractEvent.Timestamp)
require.Equal(t, block.Hash(), contractEvent.BlockHash)
l1BlockHeader, err := testSuite.DB.Blocks.L1BlockHeader(block.Number()) l1BlockHeader, err := testSuite.DB.Blocks.L1BlockHeader(block.Hash())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, block.Hash(), l1BlockHeader.Hash) require.Equal(t, block.Hash(), l1BlockHeader.Hash)
require.Equal(t, block.ParentHash(), l1BlockHeader.ParentHash) require.Equal(t, block.ParentHash(), l1BlockHeader.ParentHash)
......
...@@ -71,23 +71,17 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite { ...@@ -71,23 +71,17 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
}, },
Chain: config.ChainConfig{ Chain: config.ChainConfig{
L1Contracts: config.L1Contracts{ L1Contracts: config.L1Contracts{
OptimismPortal: opCfg.L1Deployments.OptimismPortalProxy, OptimismPortalProxy: opCfg.L1Deployments.OptimismPortalProxy,
L2OutputOracle: opCfg.L1Deployments.L2OutputOracleProxy, L2OutputOracleProxy: opCfg.L1Deployments.L2OutputOracleProxy,
L1CrossDomainMessenger: opCfg.L1Deployments.L1CrossDomainMessengerProxy, L1CrossDomainMessengerProxy: opCfg.L1Deployments.L1CrossDomainMessengerProxy,
L1StandardBridge: opCfg.L1Deployments.L1StandardBridgeProxy, L1StandardBridgeProxy: opCfg.L1Deployments.L1StandardBridgeProxy,
L1ERC721Bridge: opCfg.L1Deployments.L1ERC721BridgeProxy,
}, },
}, },
} }
db, err := database.NewDB(indexerCfg.DB) db, err := database.NewDB(indexerCfg.DB)
require.NoError(t, err) require.NoError(t, err)
indexer, err := indexer.NewIndexer( indexer, err := indexer.NewIndexer(logger, indexerCfg.Chain, indexerCfg.RPCs, db)
indexerCfg.Chain,
indexerCfg.RPCs,
db,
logger,
)
require.NoError(t, err) require.NoError(t, err)
indexerStoppedCh := make(chan interface{}, 1) indexerStoppedCh := make(chan interface{}, 1)
......
...@@ -4,7 +4,7 @@ import ( ...@@ -4,7 +4,7 @@ import (
"errors" "errors"
"math/big" "math/big"
"github.com/ethereum-optimism/optimism/indexer/processor" "github.com/ethereum-optimism/optimism/indexer/processors/contracts"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -57,5 +57,5 @@ func CrossDomainMessengerSentMessageHash(sentMessage *bindings.CrossDomainMessen ...@@ -57,5 +57,5 @@ func CrossDomainMessengerSentMessageHash(sentMessage *bindings.CrossDomainMessen
return common.Hash{}, err return common.Hash{}, err
} }
return processor.CrossDomainMessageHash(abi, sentMessage, value) return contracts.CrossDomainMessageHash(abi, sentMessage, value)
} }
package etl
import (
"context"
"errors"
"time"
"github.com/ethereum-optimism/optimism/indexer/node"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
)
const (
defaultLoopInterval = 5 * time.Second
defaultHeaderBufferSize = 500
)
type ETL struct {
log log.Logger
headerTraversal *node.HeaderTraversal
ethClient *ethclient.Client
contracts []common.Address
etlBatches chan ETLBatch
}
type ETLBatch struct {
Logger log.Logger
Headers []types.Header
HeaderMap map[common.Hash]*types.Header
Logs []types.Log
HeadersWithLog map[common.Hash]bool
}
func (etl *ETL) Start(ctx context.Context) error {
done := ctx.Done()
pollTicker := time.NewTicker(defaultLoopInterval)
defer pollTicker.Stop()
etl.log.Info("starting etl...")
var headers []types.Header
for {
select {
case <-done:
etl.log.Info("stopping etl")
return nil
case <-pollTicker.C:
if len(headers) == 0 {
newHeaders, err := etl.headerTraversal.NextFinalizedHeaders(defaultHeaderBufferSize)
if err != nil {
etl.log.Error("error querying for headers", "err", err)
continue
}
if len(newHeaders) == 0 {
// Logged as an error since this loop should be operating at a longer interval than the provider
etl.log.Error("no new headers. processor unexpectedly at head...")
continue
}
headers = newHeaders
} else {
etl.log.Info("retrying previous batch")
}
firstHeader := headers[0]
lastHeader := headers[len(headers)-1]
batchLog := etl.log.New("batch_start_block_number", firstHeader.Number, "batch_end_block_number", lastHeader.Number)
batchLog.Info("extracting batch", "size", len(headers))
headerMap := make(map[common.Hash]*types.Header, len(headers))
for i := range headers {
headerMap[headers[i].Hash()] = &headers[i]
}
headersWithLog := make(map[common.Hash]bool, len(headers))
logFilter := ethereum.FilterQuery{FromBlock: firstHeader.Number, ToBlock: lastHeader.Number, Addresses: etl.contracts}
logs, err := etl.ethClient.FilterLogs(context.Background(), logFilter)
if err != nil {
batchLog.Info("unable to extract logs within batch", "err", err)
continue // spin and try again
}
for i := range logs {
if _, ok := headerMap[logs[i].BlockHash]; !ok {
// NOTE. Definitely an error state if the none of the headers were re-orged out in between
// the blocks and logs retreival operations. However, we need to gracefully handle reorgs
batchLog.Error("log found with block hash not in the batch", "block_hash", logs[i].BlockHash, "log_index", logs[i].Index)
return errors.New("parsed log with a block hash not in the fetched batch")
}
headersWithLog[logs[i].BlockHash] = true
}
if len(logs) > 0 {
batchLog.Info("detected logs", "size", len(logs))
}
// create a new reference such that subsequent changes to `headers` does not affect the reference
headersRef := headers
batch := ETLBatch{Logger: batchLog, Headers: headersRef, HeaderMap: headerMap, Logs: logs, HeadersWithLog: headersWithLog}
headers = nil
etl.etlBatches <- batch
}
}
}
package etl
import (
"context"
"errors"
"reflect"
"github.com/ethereum-optimism/optimism/indexer/config"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/node"
"github.com/ethereum-optimism/optimism/op-service/retry"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
type L1ETL struct {
ETL
db *database.DB
}
func NewL1ETL(log log.Logger, db *database.DB, client node.EthClient, contracts config.L1Contracts) (*L1ETL, error) {
log = log.New("etl", "l1")
contractValue := reflect.ValueOf(contracts)
fields := reflect.VisibleFields(reflect.TypeOf(contracts))
l1Contracts := make([]common.Address, len(fields))
for i, field := range fields {
// ruleid: unsafe-reflect-by-name
addr, ok := (contractValue.FieldByName(field.Name).Interface()).(common.Address)
if !ok {
log.Error("non-address found in L1Contracts", "name", field.Name)
return nil, errors.New("non-address found in L1Contracts")
}
log.Info("configured contract", "name", field.Name, "addr", addr)
l1Contracts[i] = addr
}
latestHeader, err := db.Blocks.L1LatestBlockHeader()
if err != nil {
return nil, err
}
var fromHeader *types.Header
if latestHeader != nil {
log.Info("detected last indexed block", "number", latestHeader.Number.Int, "hash", latestHeader.Hash)
fromHeader = latestHeader.RLPHeader.Header()
} else {
log.Info("no indexed state, starting from genesis")
}
etlBatches := make(chan ETLBatch)
etl := ETL{
log: log,
headerTraversal: node.NewHeaderTraversal(client, fromHeader),
ethClient: client.GethEthClient(),
contracts: l1Contracts,
etlBatches: etlBatches,
}
return &L1ETL{ETL: etl, db: db}, nil
}
func (l1Etl *L1ETL) Start(ctx context.Context) error {
errCh := make(chan error, 1)
go func() {
errCh <- l1Etl.ETL.Start(ctx)
}()
for {
select {
case err := <-errCh:
return err
// Index incoming batches
case batch := <-l1Etl.etlBatches:
// Pull out only L1 blocks that have emitted a log ( <= batch.Headers )
l1BlockHeaders := make([]database.L1BlockHeader, 0, len(batch.Headers))
for i := range batch.Headers {
if _, ok := batch.HeadersWithLog[batch.Headers[i].Hash()]; ok {
l1BlockHeaders = append(l1BlockHeaders, database.L1BlockHeader{BlockHeader: database.BlockHeaderFromHeader(&batch.Headers[i])})
}
}
if len(l1BlockHeaders) == 0 {
batch.Logger.Info("no l1 blocks with logs in batch")
continue
}
l1ContractEvents := make([]database.L1ContractEvent, len(batch.Logs))
for i := range batch.Logs {
timestamp := batch.HeaderMap[batch.Logs[i].BlockHash].Time
l1ContractEvents[i] = database.L1ContractEvent{ContractEvent: database.ContractEventFromLog(&batch.Logs[i], timestamp)}
}
// Continually try to persist this batch. If it fails after 10 attempts, we simply error out
retryStrategy := &retry.ExponentialStrategy{Min: 1000, Max: 20_000, MaxJitter: 250}
_, err := retry.Do[interface{}](ctx, 10, retryStrategy, func() (interface{}, error) {
err := l1Etl.db.Transaction(func(tx *database.DB) error {
if err := tx.Blocks.StoreL1BlockHeaders(l1BlockHeaders); err != nil {
return err
}
// we must have logs if we have l1 blocks
if err := tx.ContractEvents.StoreL1ContractEvents(l1ContractEvents); err != nil {
return err
}
return nil
})
if err != nil {
batch.Logger.Error("unable to persist batch", "err", err)
return nil, err
}
// a-ok! Can merge with the above block but being explicit
return nil, nil
})
if err != nil {
return err
}
batch.Logger.Info("indexed batch")
}
}
}
package etl
import (
"context"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/node"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-service/retry"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
type L2ETL struct {
ETL
db *database.DB
}
func NewL2ETL(log log.Logger, db *database.DB, client node.EthClient) (*L2ETL, error) {
log = log.New("etl", "l2")
// allow predeploys to be overridable
l2Contracts := []common.Address{}
for name, addr := range predeploys.Predeploys {
log.Info("configured contract", "name", name, "addr", addr)
l2Contracts = append(l2Contracts, *addr)
}
latestHeader, err := db.Blocks.L2LatestBlockHeader()
if err != nil {
return nil, err
}
var fromHeader *types.Header
if latestHeader != nil {
log.Info("detected last indexed block", "number", latestHeader.Number.Int, "hash", latestHeader.Hash)
fromHeader = latestHeader.RLPHeader.Header()
} else {
log.Info("no indexed state, starting from genesis")
}
etlBatches := make(chan ETLBatch)
etl := ETL{
log: log,
headerTraversal: node.NewHeaderTraversal(client, fromHeader),
ethClient: client.GethEthClient(),
contracts: l2Contracts,
etlBatches: etlBatches,
}
return &L2ETL{ETL: etl, db: db}, nil
}
func (l2Etl *L2ETL) Start(ctx context.Context) error {
errCh := make(chan error, 1)
go func() {
errCh <- l2Etl.ETL.Start(ctx)
}()
for {
select {
case err := <-errCh:
return err
// Index incoming batches
case batch := <-l2Etl.etlBatches:
// We're indexing every L2 block.
l2BlockHeaders := make([]database.L2BlockHeader, len(batch.Headers))
for i := range batch.Headers {
l2BlockHeaders[i] = database.L2BlockHeader{BlockHeader: database.BlockHeaderFromHeader(&batch.Headers[i])}
}
l2ContractEvents := make([]database.L2ContractEvent, len(batch.Logs))
for i := range batch.Logs {
timestamp := batch.HeaderMap[batch.Logs[i].BlockHash].Time
l2ContractEvents[i] = database.L2ContractEvent{ContractEvent: database.ContractEventFromLog(&batch.Logs[i], timestamp)}
}
// Continually try to persist this batch. If it fails after 5 attempts, we simply error out
retryStrategy := &retry.ExponentialStrategy{Min: 1000, Max: 20_000, MaxJitter: 250}
_, err := retry.Do[interface{}](ctx, 10, retryStrategy, func() (interface{}, error) {
err := l2Etl.db.Transaction(func(tx *database.DB) error {
if err := tx.Blocks.StoreL2BlockHeaders(l2BlockHeaders); err != nil {
return err
}
if len(l2ContractEvents) > 0 {
if err := tx.ContractEvents.StoreL2ContractEvents(l2ContractEvents); err != nil {
return err
}
}
return nil
})
if err != nil {
batch.Logger.Error("unable to persist batch", "err", err)
return nil, err
}
// a-ok! Can merge with the above block but being explicit
return nil, nil
})
if err != nil {
return err
}
batch.Logger.Info("indexed batch")
}
}
}
...@@ -3,14 +3,16 @@ package indexer ...@@ -3,14 +3,16 @@ package indexer
import ( import (
"context" "context"
"fmt" "fmt"
"runtime/debug"
"sync" "sync"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/indexer/config" "github.com/ethereum-optimism/optimism/indexer/config"
"github.com/ethereum-optimism/optimism/indexer/database" "github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/etl"
"github.com/ethereum-optimism/optimism/indexer/node" "github.com/ethereum-optimism/optimism/indexer/node"
"github.com/ethereum-optimism/optimism/indexer/processor" "github.com/ethereum-optimism/optimism/indexer/processors"
) )
// Indexer contains the necessary resources for // Indexer contains the necessary resources for
...@@ -19,29 +21,36 @@ type Indexer struct { ...@@ -19,29 +21,36 @@ type Indexer struct {
db *database.DB db *database.DB
log log.Logger log log.Logger
L1Processor *processor.L1Processor L1ETL *etl.L1ETL
L2Processor *processor.L2Processor L2ETL *etl.L2ETL
BridgeProcessor *processors.BridgeProcessor
} }
// NewIndexer initializes an instance of the Indexer // NewIndexer initializes an instance of the Indexer
func NewIndexer(chainConfig config.ChainConfig, rpcsConfig config.RPCsConfig, db *database.DB, logger log.Logger) (*Indexer, error) { func NewIndexer(logger log.Logger, chainConfig config.ChainConfig, rpcsConfig config.RPCsConfig, db *database.DB) (*Indexer, error) {
l1Contracts := chainConfig.L1Contracts
l1EthClient, err := node.DialEthClient(rpcsConfig.L1RPC) l1EthClient, err := node.DialEthClient(rpcsConfig.L1RPC)
if err != nil { if err != nil {
return nil, err return nil, err
} }
l1Processor, err := processor.NewL1Processor(logger, l1EthClient, db, l1Contracts)
l1Etl, err := etl.NewL1ETL(logger, db, l1EthClient, chainConfig.L1Contracts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// L2Processor (predeploys). Although most likely the right setting, make this configurable?
l2Contracts := processor.L2ContractPredeploys()
l2EthClient, err := node.DialEthClient(rpcsConfig.L2RPC) l2EthClient, err := node.DialEthClient(rpcsConfig.L2RPC)
if err != nil { if err != nil {
return nil, err return nil, err
} }
l2Processor, err := processor.NewL2Processor(logger, l2EthClient, db, l2Contracts)
// Currently defaults to the predeploys
l2Etl, err := etl.NewL2ETL(logger, db, l2EthClient)
if err != nil {
return nil, err
}
bridgeProcessor, err := processors.NewBridgeProcessor(logger, db, chainConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -49,8 +58,10 @@ func NewIndexer(chainConfig config.ChainConfig, rpcsConfig config.RPCsConfig, db ...@@ -49,8 +58,10 @@ func NewIndexer(chainConfig config.ChainConfig, rpcsConfig config.RPCsConfig, db
indexer := &Indexer{ indexer := &Indexer{
db: db, db: db,
log: logger, log: logger,
L1Processor: l1Processor,
L2Processor: l2Processor, L1ETL: l1Etl,
L2ETL: l2Etl,
BridgeProcessor: bridgeProcessor,
} }
return indexer, nil return indexer, nil
...@@ -59,39 +70,41 @@ func NewIndexer(chainConfig config.ChainConfig, rpcsConfig config.RPCsConfig, db ...@@ -59,39 +70,41 @@ func NewIndexer(chainConfig config.ChainConfig, rpcsConfig config.RPCsConfig, db
// Start starts the indexing service on L1 and L2 chains // Start starts the indexing service on L1 and L2 chains
func (i *Indexer) Run(ctx context.Context) error { func (i *Indexer) Run(ctx context.Context) error {
var wg sync.WaitGroup var wg sync.WaitGroup
errCh := make(chan error, 1) errCh := make(chan error, 3)
// If either processor errors out, we stop // If either processor errors out, we stop
processorCtx, cancel := context.WithCancel(ctx) subCtx, cancel := context.WithCancel(ctx)
run := func(start func(ctx context.Context) error) { run := func(start func(ctx context.Context) error) {
wg.Add(1) wg.Add(1)
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
i.log.Error("halting indexer on panic", "err", err) i.log.Error("halting indexer on panic", "err", err)
debug.PrintStack()
errCh <- fmt.Errorf("panic: %v", err) errCh <- fmt.Errorf("panic: %v", err)
} }
cancel()
wg.Done() wg.Done()
}() }()
err := start(processorCtx) err := start(subCtx)
if err != nil { if err != nil {
i.log.Error("halting indexer on error", "err", err) i.log.Error("halting indexer on error", "err", err)
cancel()
} }
// Send a value down regardless if we've received an error or halted // Send a value down regardless if we've received an error
// via cancellation where err == nil // or halted via cancellation where err == nil
errCh <- err errCh <- err
} }
// Kick off the processors // Kick off all the dependent routines
go run(i.L1Processor.Start) go run(i.L1ETL.Start)
go run(i.L2Processor.Start) go run(i.L2ETL.Start)
go run(i.BridgeProcessor.Start)
err := <-errCh err := <-errCh
// ensure both processors have halted before returning
wg.Wait() wg.Wait()
i.log.Info("indexer stopped")
return err return err
} }
......
...@@ -27,12 +27,13 @@ const ( ...@@ -27,12 +27,13 @@ const (
type EthClient interface { type EthClient interface {
FinalizedBlockHeight() (*big.Int, error) FinalizedBlockHeight() (*big.Int, error)
BlockHeadersByRange(*big.Int, *big.Int) ([]*types.Header, error) BlockHeadersByRange(*big.Int, *big.Int) ([]types.Header, error)
BlockHeaderByHash(common.Hash) (*types.Header, error) BlockHeaderByHash(common.Hash) (*types.Header, error)
StorageHash(common.Address, *big.Int) (common.Hash, error) StorageHash(common.Address, *big.Int) (common.Hash, error)
RawRpcClient() *rpc.Client GethRpcClient() *rpc.Client
GethEthClient() *ethclient.Client
} }
type client struct { type client struct {
...@@ -56,10 +57,14 @@ func NewEthClient(rpcClient *rpc.Client) EthClient { ...@@ -56,10 +57,14 @@ func NewEthClient(rpcClient *rpc.Client) EthClient {
return &client{rpcClient} return &client{rpcClient}
} }
func (c *client) RawRpcClient() *rpc.Client { func (c *client) GethRpcClient() *rpc.Client {
return c.rpcClient return c.rpcClient
} }
func (c *client) GethEthClient() *ethclient.Client {
return ethclient.NewClient(c.GethRpcClient())
}
// FinalizedBlockHeight retrieves the latest block height in a finalized state // FinalizedBlockHeight retrieves the latest block height in a finalized state
func (c *client) FinalizedBlockHeight() (*big.Int, error) { func (c *client) FinalizedBlockHeight() (*big.Int, error) {
ctxwt, cancel := context.WithTimeout(context.Background(), defaultRequestTimeout) ctxwt, cancel := context.WithTimeout(context.Background(), defaultRequestTimeout)
...@@ -97,7 +102,7 @@ func (c *client) BlockHeaderByHash(hash common.Hash) (*types.Header, error) { ...@@ -97,7 +102,7 @@ func (c *client) BlockHeaderByHash(hash common.Hash) (*types.Header, error) {
// BlockHeadersByRange will retrieve block headers within the specified range -- includsive. No restrictions // BlockHeadersByRange will retrieve block headers within the specified range -- includsive. No restrictions
// are placed on the range such as blocks in the "latest", "safe" or "finalized" states. If the specified // are placed on the range such as blocks in the "latest", "safe" or "finalized" states. If the specified
// range is too large, `endHeight > latest`, the resulting list is truncated to the available headers // range is too large, `endHeight > latest`, the resulting list is truncated to the available headers
func (c *client) BlockHeadersByRange(startHeight, endHeight *big.Int) ([]*types.Header, error) { func (c *client) BlockHeadersByRange(startHeight, endHeight *big.Int) ([]types.Header, error) {
count := new(big.Int).Sub(endHeight, startHeight).Uint64() + 1 count := new(big.Int).Sub(endHeight, startHeight).Uint64() + 1
batchElems := make([]rpc.BatchElem, count) batchElems := make([]rpc.BatchElem, count)
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
...@@ -121,7 +126,7 @@ func (c *client) BlockHeadersByRange(startHeight, endHeight *big.Int) ([]*types. ...@@ -121,7 +126,7 @@ func (c *client) BlockHeadersByRange(startHeight, endHeight *big.Int) ([]*types.
// - Ensure integrity that they build on top of each other // - Ensure integrity that they build on top of each other
// - Truncate out headers that do not exist (endHeight > "latest") // - Truncate out headers that do not exist (endHeight > "latest")
size := 0 size := 0
headers := make([]*types.Header, count) headers := make([]types.Header, count)
for i, batchElem := range batchElems { for i, batchElem := range batchElems {
if batchElem.Error != nil { if batchElem.Error != nil {
return nil, batchElem.Error return nil, batchElem.Error
...@@ -129,17 +134,19 @@ func (c *client) BlockHeadersByRange(startHeight, endHeight *big.Int) ([]*types. ...@@ -129,17 +134,19 @@ func (c *client) BlockHeadersByRange(startHeight, endHeight *big.Int) ([]*types.
break break
} }
header := batchElem.Result.(*types.Header) header, ok := batchElem.Result.(*types.Header)
if !ok {
return nil, fmt.Errorf("unable to transform rpc response %v into types.Header", batchElem.Result)
}
if i > 0 && header.ParentHash != headers[i-1].Hash() { if i > 0 && header.ParentHash != headers[i-1].Hash() {
// Warn here that we got a bad (malicious?) response return nil, fmt.Errorf("queried header %s does not follow parent %s", header.Hash(), headers[i-1].Hash())
break
} }
headers[i] = header headers[i] = *header
size = size + 1 size = size + 1
} }
headers = headers[:size]
headers = headers[:size]
return headers, nil return headers, nil
} }
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
...@@ -21,9 +22,9 @@ func (m *MockEthClient) FinalizedBlockHeight() (*big.Int, error) { ...@@ -21,9 +22,9 @@ func (m *MockEthClient) FinalizedBlockHeight() (*big.Int, error) {
return args.Get(0).(*big.Int), args.Error(1) return args.Get(0).(*big.Int), args.Error(1)
} }
func (m *MockEthClient) BlockHeadersByRange(from, to *big.Int) ([]*types.Header, error) { func (m *MockEthClient) BlockHeadersByRange(from, to *big.Int) ([]types.Header, error) {
args := m.Called(from, to) args := m.Called(from, to)
return args.Get(0).([]*types.Header), args.Error(1) return args.Get(0).([]types.Header), args.Error(1)
} }
func (m *MockEthClient) BlockHeaderByHash(hash common.Hash) (*types.Header, error) { func (m *MockEthClient) BlockHeaderByHash(hash common.Hash) (*types.Header, error) {
...@@ -36,7 +37,12 @@ func (m *MockEthClient) StorageHash(address common.Address, blockNumber *big.Int ...@@ -36,7 +37,12 @@ func (m *MockEthClient) StorageHash(address common.Address, blockNumber *big.Int
return args.Get(0).(common.Hash), args.Error(1) return args.Get(0).(common.Hash), args.Error(1)
} }
func (m *MockEthClient) RawRpcClient() *rpc.Client { func (m *MockEthClient) GethRpcClient() *rpc.Client {
args := m.Called() args := m.Called()
return args.Get(0).(*rpc.Client) return args.Get(0).(*rpc.Client)
} }
func (m *MockEthClient) GethEthClient() *ethclient.Client {
args := m.Called()
return args.Get(0).(*ethclient.Client)
}
...@@ -26,7 +26,7 @@ func NewHeaderTraversal(ethClient EthClient, fromHeader *types.Header) *HeaderTr ...@@ -26,7 +26,7 @@ func NewHeaderTraversal(ethClient EthClient, fromHeader *types.Header) *HeaderTr
// NextFinalizedHeaders retrives the next set of headers that have been // NextFinalizedHeaders retrives the next set of headers that have been
// marked as finalized by the connected client, bounded by the supplied size // marked as finalized by the connected client, bounded by the supplied size
func (f *HeaderTraversal) NextFinalizedHeaders(maxSize uint64) ([]*types.Header, error) { func (f *HeaderTraversal) NextFinalizedHeaders(maxSize uint64) ([]types.Header, error) {
finalizedBlockHeight, err := f.ethClient.FinalizedBlockHeight() finalizedBlockHeight, err := f.ethClient.FinalizedBlockHeight()
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -61,6 +61,6 @@ func (f *HeaderTraversal) NextFinalizedHeaders(maxSize uint64) ([]*types.Header, ...@@ -61,6 +61,6 @@ func (f *HeaderTraversal) NextFinalizedHeaders(maxSize uint64) ([]*types.Header,
return nil, ErrHeaderTraversalAndProviderMismatchedState return nil, ErrHeaderTraversalAndProviderMismatchedState
} }
f.lastHeader = headers[numHeaders-1] f.lastHeader = &headers[numHeaders-1]
return headers, nil return headers, nil
} }
...@@ -11,22 +11,21 @@ import ( ...@@ -11,22 +11,21 @@ import (
) )
// make a set of headers which chain correctly // make a set of headers which chain correctly
func makeHeaders(numHeaders uint64, prevHeader *types.Header) []*types.Header { func makeHeaders(numHeaders uint64, prevHeader *types.Header) []types.Header {
headers := make([]*types.Header, numHeaders) headers := make([]types.Header, numHeaders)
for i := range headers { for i := range headers {
if i == 0 { if i == 0 {
if prevHeader == nil { if prevHeader == nil {
// genesis // genesis
headers[i] = &types.Header{Number: big.NewInt(0)} headers[i] = types.Header{Number: big.NewInt(0)}
} else { } else {
// chain onto the previous header // chain onto the previous header
headers[i] = &types.Header{Number: big.NewInt(prevHeader.Number.Int64() + 1)} headers[i] = types.Header{Number: big.NewInt(prevHeader.Number.Int64() + 1)}
headers[i].ParentHash = prevHeader.Hash() headers[i].ParentHash = prevHeader.Hash()
} }
} else { } else {
prevHeader = headers[i-1] headers[i] = types.Header{Number: big.NewInt(headers[i-1].Number.Int64() + 1)}
headers[i] = &types.Header{Number: big.NewInt(prevHeader.Number.Int64() + 1)} headers[i].ParentHash = headers[i-1].Hash()
headers[i].ParentHash = prevHeader.Hash()
} }
} }
...@@ -62,7 +61,7 @@ func TestHeaderTraversalNextFinalizedHeadersCursored(t *testing.T) { ...@@ -62,7 +61,7 @@ func TestHeaderTraversalNextFinalizedHeadersCursored(t *testing.T) {
require.Len(t, headers, 5) require.Len(t, headers, 5)
// blocks [5..9] // blocks [5..9]
headers = makeHeaders(5, headers[len(headers)-1]) headers = makeHeaders(5, &headers[len(headers)-1])
client.On("FinalizedBlockHeight").Return(big.NewInt(9), nil) client.On("FinalizedBlockHeight").Return(big.NewInt(9), nil)
client.On("BlockHeadersByRange", mock.MatchedBy(bigIntMatcher(5)), mock.MatchedBy(bigIntMatcher(9))).Return(headers, nil) client.On("BlockHeadersByRange", mock.MatchedBy(bigIntMatcher(5)), mock.MatchedBy(bigIntMatcher(9))).Return(headers, nil)
headers, err = headerTraversal.NextFinalizedHeaders(5) headers, err = headerTraversal.NextFinalizedHeaders(5)
...@@ -87,7 +86,7 @@ func TestHeaderTraversalNextFinalizedHeadersMaxSize(t *testing.T) { ...@@ -87,7 +86,7 @@ func TestHeaderTraversalNextFinalizedHeadersMaxSize(t *testing.T) {
require.Len(t, headers, 5) require.Len(t, headers, 5)
// clamped by the supplied size. FinalizedHeight == 100 // clamped by the supplied size. FinalizedHeight == 100
headers = makeHeaders(10, headers[len(headers)-1]) headers = makeHeaders(10, &headers[len(headers)-1])
client.On("BlockHeadersByRange", mock.MatchedBy(bigIntMatcher(5)), mock.MatchedBy(bigIntMatcher(14))).Return(headers, nil) client.On("BlockHeadersByRange", mock.MatchedBy(bigIntMatcher(5)), mock.MatchedBy(bigIntMatcher(14))).Return(headers, nil)
headers, err = headerTraversal.NextFinalizedHeaders(10) headers, err = headerTraversal.NextFinalizedHeaders(10)
require.NoError(t, err) require.NoError(t, err)
......
package processor
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
)
var (
// Standard ABI types copied from golang ABI tests
Uint256Type, _ = abi.NewType("uint256", "", nil)
BytesType, _ = abi.NewType("bytes", "", nil)
AddressType, _ = abi.NewType("address", "", nil)
LegacyCrossDomainMessengerRelayMessageMethod = abi.NewMethod(
"relayMessage",
"relayMessage",
abi.Function,
"external", // mutability
false, // isConst
true, // payable
abi.Arguments{ // inputs
{Name: "sender", Type: AddressType},
{Name: "target", Type: AddressType},
{Name: "data", Type: BytesType},
{Name: "nonce", Type: Uint256Type},
},
abi.Arguments{}, // outputs
)
)
type CrossDomainMessengerSentMessageEvent struct {
*bindings.CrossDomainMessengerSentMessage
Value *big.Int
MessageHash common.Hash
Event *database.ContractEvent
}
type CrossDomainMessengerRelayedMessageEvent struct {
*bindings.CrossDomainMessengerRelayedMessage
Event *database.ContractEvent
}
func CrossDomainMessengerSentMessageEvents(events *ProcessedContractEvents) ([]CrossDomainMessengerSentMessageEvent, error) {
crossDomainMessengerABI, err := bindings.CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return nil, err
}
sentMessageEventAbi := crossDomainMessengerABI.Events["SentMessage"]
sentMessageEventExtensionAbi := crossDomainMessengerABI.Events["SentMessageExtension1"]
processedSentMessageEvents := events.eventsBySignature[sentMessageEventAbi.ID]
crossDomainMessageEvents := make([]CrossDomainMessengerSentMessageEvent, len(processedSentMessageEvents))
for i, sentMessageEvent := range processedSentMessageEvents {
log := sentMessageEvent.RLPLog
var sentMsgData bindings.CrossDomainMessengerSentMessage
sentMsgData.Raw = *log
err = UnpackLog(&sentMsgData, log, sentMessageEventAbi.Name, crossDomainMessengerABI)
if err != nil {
return nil, err
}
var sentMsgExtensionData bindings.CrossDomainMessengerSentMessageExtension1
extensionLog := events.eventByLogIndex[ProcessedContractEventLogIndexKey{log.BlockHash, log.Index + 1}].RLPLog
sentMsgExtensionData.Raw = *extensionLog
err = UnpackLog(&sentMsgExtensionData, extensionLog, sentMessageEventExtensionAbi.Name, crossDomainMessengerABI)
if err != nil {
return nil, err
}
msgHash, err := CrossDomainMessageHash(crossDomainMessengerABI, &sentMsgData, sentMsgExtensionData.Value)
if err != nil {
return nil, err
}
crossDomainMessageEvents[i] = CrossDomainMessengerSentMessageEvent{
CrossDomainMessengerSentMessage: &sentMsgData,
Value: sentMsgExtensionData.Value,
MessageHash: msgHash,
Event: sentMessageEvent,
}
}
return crossDomainMessageEvents, nil
}
func CrossDomainMessengerRelayedMessageEvents(events *ProcessedContractEvents) ([]CrossDomainMessengerRelayedMessageEvent, error) {
crossDomainMessengerABI, err := bindings.L1CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return nil, err
}
relayedMessageEventAbi := crossDomainMessengerABI.Events["RelayedMessage"]
processedRelayedMessageEvents := events.eventsBySignature[relayedMessageEventAbi.ID]
crossDomainMessageEvents := make([]CrossDomainMessengerRelayedMessageEvent, len(processedRelayedMessageEvents))
for i, relayedMessageEvent := range processedRelayedMessageEvents {
log := relayedMessageEvent.RLPLog
var relayedMsgData bindings.CrossDomainMessengerRelayedMessage
relayedMsgData.Raw = *log
err = UnpackLog(&relayedMsgData, log, relayedMessageEventAbi.Name, crossDomainMessengerABI)
if err != nil {
return nil, err
}
crossDomainMessageEvents[i] = CrossDomainMessengerRelayedMessageEvent{
CrossDomainMessengerRelayedMessage: &relayedMsgData,
Event: relayedMessageEvent,
}
}
return crossDomainMessageEvents, nil
}
// Replica of `Hashing.sol#hashCrossDomainMessage` solidity implementation
func CrossDomainMessageHash(abi *abi.ABI, sentMsg *bindings.CrossDomainMessengerSentMessage, value *big.Int) (common.Hash, error) {
version, _ := DecodeVersionedNonce(sentMsg.MessageNonce)
switch version {
case 0:
// Legacy Message
inputBytes, err := LegacyCrossDomainMessengerRelayMessageMethod.Inputs.Pack(sentMsg.Sender, sentMsg.Target, sentMsg.Message, sentMsg.MessageNonce)
if err != nil {
return common.Hash{}, err
}
msgBytes := append(LegacyCrossDomainMessengerRelayMessageMethod.ID, inputBytes...)
return crypto.Keccak256Hash(msgBytes), nil
case 1:
// Current Message
msgBytes, err := abi.Pack("relayMessage", sentMsg.MessageNonce, sentMsg.Sender, sentMsg.Target, value, sentMsg.GasLimit, sentMsg.Message)
if err != nil {
return common.Hash{}, err
}
return crypto.Keccak256Hash(msgBytes), nil
}
return common.Hash{}, fmt.Errorf("unsupported cross domain messenger version: %d", version)
}
package processor
import (
"encoding/binary"
"math/big"
)
// DecodeVersionNonce is an re-implementation of Encoding.sol#decodeVersionedNonce.
// If the nonce is greater than 32 bytes (solidity uint256), bytes [32:] are ignored
func DecodeVersionedNonce(nonce *big.Int) (uint16, *big.Int) {
nonceBytes := nonce.Bytes()
nonceByteLen := len(nonceBytes)
if nonceByteLen < 30 {
// version is 0x0000
return 0, nonce
} else if nonceByteLen == 31 {
// version is 0x00[01..ff]
return uint16(nonceBytes[0]), new(big.Int).SetBytes(nonceBytes[1:])
} else {
// fully specified
version := binary.BigEndian.Uint16(nonceBytes[:2])
return version, new(big.Int).SetBytes(nonceBytes[2:])
}
}
package processor
import (
"bytes"
"context"
"errors"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/indexer/config"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/node"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
legacy_bindings "github.com/ethereum-optimism/optimism/op-bindings/legacy-bindings"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
)
type checkpointAbi struct {
l2OutputOracle *abi.ABI
legacyStateCommitmentChain *abi.ABI
}
type L1Processor struct {
processor
}
func NewL1Processor(logger log.Logger, ethClient node.EthClient, db *database.DB, l1Contracts config.L1Contracts) (*L1Processor, error) {
l1ProcessLog := logger.New("processor", "l1")
l1ProcessLog.Info("initializing processor")
l2OutputOracleABI, err := bindings.L2OutputOracleMetaData.GetAbi()
if err != nil {
l1ProcessLog.Error("unable to generate L2OutputOracle ABI", "err", err)
return nil, err
}
legacyStateCommitmentChainABI, err := legacy_bindings.StateCommitmentChainMetaData.GetAbi()
if err != nil {
l1ProcessLog.Error("unable to generate legacy StateCommitmentChain ABI", "err", err)
return nil, err
}
checkpointAbi := checkpointAbi{l2OutputOracle: l2OutputOracleABI, legacyStateCommitmentChain: legacyStateCommitmentChainABI}
latestHeader, err := db.Blocks.LatestL1BlockHeader()
if err != nil {
return nil, err
}
var fromL1Header *types.Header
if latestHeader != nil {
l1ProcessLog.Info("detected last indexed block", "height", latestHeader.Number.Int, "hash", latestHeader.Hash)
l1Header, err := ethClient.BlockHeaderByHash(latestHeader.Hash)
if err != nil {
l1ProcessLog.Error("unable to fetch header for last indexed block", "hash", latestHeader.Hash, "err", err)
return nil, err
}
fromL1Header = l1Header
} else {
// we shouldn't start from genesis with l1. Need a "genesis" L1 height provided for the rollup
l1ProcessLog.Info("no indexed state, starting from genesis")
fromL1Header = nil
}
l1Processor := &L1Processor{
processor: processor{
headerTraversal: node.NewHeaderTraversal(ethClient, fromL1Header),
db: db,
processFn: l1ProcessFn(l1ProcessLog, ethClient, l1Contracts, checkpointAbi),
processLog: l1ProcessLog,
},
}
return l1Processor, nil
}
func l1ProcessFn(processLog log.Logger, ethClient node.EthClient, l1Contracts config.L1Contracts, checkpointAbi checkpointAbi) ProcessFn {
rawEthClient := ethclient.NewClient(ethClient.RawRpcClient())
contractAddrs := l1Contracts.ToSlice()
processLog.Info("processor configured with contracts", "contracts", l1Contracts)
outputProposedEventName := "OutputProposed"
outputProposedEventSig := checkpointAbi.l2OutputOracle.Events[outputProposedEventName].ID
legacyStateBatchAppendedEventName := "StateBatchAppended"
legacyStateBatchAppendedEventSig := checkpointAbi.legacyStateCommitmentChain.Events[legacyStateBatchAppendedEventName].ID
return func(db *database.DB, headers []*types.Header) error {
headerMap := make(map[common.Hash]*types.Header)
for _, header := range headers {
headerMap[header.Hash()] = header
}
/** Watch for all Optimism Contract Events **/
logFilter := ethereum.FilterQuery{FromBlock: headers[0].Number, ToBlock: headers[len(headers)-1].Number, Addresses: contractAddrs}
logs, err := rawEthClient.FilterLogs(context.Background(), logFilter) // []types.Log
if err != nil {
return err
}
// L2 checkpoints posted on L1
outputProposals := []*database.OutputProposal{}
legacyStateBatches := []*database.LegacyStateBatch{}
l1HeadersOfInterest := make(map[common.Hash]bool)
l1ContractEvents := make([]*database.L1ContractEvent, len(logs))
processedContractEvents := NewProcessedContractEvents()
for i := range logs {
log := &logs[i]
header, ok := headerMap[log.BlockHash]
if !ok {
processLog.Error("contract event found with associated header not in the batch", "header", log.BlockHash, "log_index", log.Index)
return errors.New("parsed log with a block hash not in this batch")
}
contractEvent := processedContractEvents.AddLog(log, header.Time)
l1HeadersOfInterest[log.BlockHash] = true
l1ContractEvents[i] = &database.L1ContractEvent{ContractEvent: *contractEvent}
// Track Checkpoint Events for L2
switch contractEvent.EventSignature {
case outputProposedEventSig:
var outputProposed bindings.L2OutputOracleOutputProposed
err := UnpackLog(&outputProposed, log, outputProposedEventName, checkpointAbi.l2OutputOracle)
if err != nil {
return err
}
outputProposals = append(outputProposals, &database.OutputProposal{
OutputRoot: outputProposed.OutputRoot,
L2OutputIndex: database.U256{Int: outputProposed.L2OutputIndex},
L2BlockNumber: database.U256{Int: outputProposed.L2BlockNumber},
L1ContractEventGUID: contractEvent.GUID,
})
case legacyStateBatchAppendedEventSig:
var stateBatchAppended legacy_bindings.StateCommitmentChainStateBatchAppended
err := UnpackLog(&stateBatchAppended, log, legacyStateBatchAppendedEventName, checkpointAbi.legacyStateCommitmentChain)
if err != nil {
return err
}
legacyStateBatches = append(legacyStateBatches, &database.LegacyStateBatch{
Index: stateBatchAppended.BatchIndex.Uint64(),
Root: stateBatchAppended.BatchRoot,
Size: stateBatchAppended.BatchSize.Uint64(),
PrevTotal: stateBatchAppended.PrevTotalElements.Uint64(),
L1ContractEventGUID: contractEvent.GUID,
})
}
}
/** Aggregate applicable L1 Blocks **/
// we iterate on the original array to maintain ordering. probably can find a more efficient
// way to iterate over the `l1HeadersOfInterest` map while maintaining ordering
indexedL1Headers := []*database.L1BlockHeader{}
for _, header := range headers {
_, hasLogs := l1HeadersOfInterest[header.Hash()]
if !hasLogs {
continue
}
indexedL1Headers = append(indexedL1Headers, &database.L1BlockHeader{BlockHeader: database.BlockHeaderFromHeader(header)})
}
/** Update Database **/
numIndexedL1Headers := len(indexedL1Headers)
if numIndexedL1Headers > 0 {
processLog.Info("saving l1 blocks with optimism logs", "size", numIndexedL1Headers, "batch_size", len(headers))
err = db.Blocks.StoreL1BlockHeaders(indexedL1Headers)
if err != nil {
return err
}
// Since the headers to index are derived from the existence of logs, we know in this branch `numLogs > 0`
processLog.Info("detected contract logs", "size", len(l1ContractEvents))
err = db.ContractEvents.StoreL1ContractEvents(l1ContractEvents)
if err != nil {
return err
}
// Mark L2 checkpoints that have been recorded on L1 (L2OutputProposal & StateBatchAppended events)
numLegacyStateBatches := len(legacyStateBatches)
if numLegacyStateBatches > 0 {
latestBatch := legacyStateBatches[numLegacyStateBatches-1]
latestL2Height := latestBatch.PrevTotal + latestBatch.Size - 1
processLog.Info("detected legacy state batches", "size", numLegacyStateBatches, "latest_l2_block_number", latestL2Height)
}
numOutputProposals := len(outputProposals)
if numOutputProposals > 0 {
latestL2Height := outputProposals[numOutputProposals-1].L2BlockNumber.Int
processLog.Info("detected output proposals", "size", numOutputProposals, "latest_l2_block_number", latestL2Height)
err := db.Blocks.StoreOutputProposals(outputProposals)
if err != nil {
return err
}
}
// forward along contract events to bridge txs processor
err = l1ProcessContractEventsBridgeTransactions(processLog, db, l1Contracts, processedContractEvents)
if err != nil {
return err
}
// forward along contract events to bridge messages processor
err = l1ProcessContractEventsBridgeCrossDomainMessages(processLog, db, processedContractEvents)
if err != nil {
return err
}
// forward along contract events to standard bridge processor
err = l1ProcessContractEventsStandardBridge(processLog, db, processedContractEvents)
if err != nil {
return err
}
} else {
processLog.Info("no l1 blocks of interest within batch")
}
// a-ok!
return nil
}
}
func l1ProcessContractEventsBridgeTransactions(processLog log.Logger, db *database.DB, l1Contracts config.L1Contracts, events *ProcessedContractEvents) error {
// (1) Process New Deposits
portalDeposits, err := OptimismPortalTransactionDepositEvents(events)
if err != nil {
return err
}
ethDeposits := []*database.L1BridgeDeposit{}
transactionDeposits := make([]*database.L1TransactionDeposit, len(portalDeposits))
for i, depositEvent := range portalDeposits {
depositTx := depositEvent.DepositTx
transactionDeposits[i] = &database.L1TransactionDeposit{
SourceHash: depositTx.SourceHash,
L2TransactionHash: types.NewTx(depositTx).Hash(),
InitiatedL1EventGUID: depositEvent.Event.GUID,
GasLimit: database.U256{Int: new(big.Int).SetUint64(depositTx.Gas)},
Tx: database.Transaction{
FromAddress: depositTx.From,
ToAddress: depositTx.From,
Amount: database.U256{Int: depositTx.Value},
Data: depositTx.Data,
Timestamp: depositEvent.Event.Timestamp,
},
}
// catch ETH transfers to the portal contract.
if len(depositTx.Data) == 0 && depositTx.Value.BitLen() > 0 {
ethDeposits = append(ethDeposits, &database.L1BridgeDeposit{
TransactionSourceHash: depositTx.SourceHash,
BridgeTransfer: database.BridgeTransfer{
Tx: transactionDeposits[i].Tx,
// TODO index eth token if it doesn't exist
TokenPair: database.ETHTokenPair,
},
})
}
}
if len(transactionDeposits) > 0 {
processLog.Info("detected transaction deposits", "size", len(transactionDeposits))
err := db.BridgeTransactions.StoreL1TransactionDeposits(transactionDeposits)
if err != nil {
return err
}
if len(ethDeposits) > 0 {
processLog.Info("detected portal ETH transfers", "size", len(ethDeposits))
err := db.BridgeTransfers.StoreL1BridgeDeposits(ethDeposits)
if err != nil {
return err
}
}
}
// (2) Process Proven Withdrawals
provenWithdrawals, err := OptimismPortalWithdrawalProvenEvents(events)
if err != nil {
return err
}
latestL2Header, err := db.Blocks.LatestL2BlockHeader()
if err != nil {
return nil
} else if len(provenWithdrawals) > 0 && latestL2Header == nil {
return errors.New("no indexed L2 headers to prove withdrawals. waiting for L2Processor to catch up")
}
for _, provenWithdrawal := range provenWithdrawals {
withdrawalHash := provenWithdrawal.WithdrawalHash
withdrawal, err := db.BridgeTransactions.L2TransactionWithdrawal(withdrawalHash)
if err != nil {
return err
}
if withdrawal == nil {
// We need to ensure we are in a caught up state before claiming a missing event. Since L2 timestamps
// are derived from L1, we can simply compare the timestamp of this event with the latest L2 header.
if provenWithdrawal.Event.Timestamp > latestL2Header.Timestamp {
processLog.Warn("behind on indexed L2 withdrawals")
return errors.New("waiting for L2Processor to catch up")
} else {
processLog.Crit("L2 withdrawal missing!", "withdrawal_hash", withdrawalHash)
return errors.New("withdrawal missing!")
}
}
err = db.BridgeTransactions.MarkL2TransactionWithdrawalProvenEvent(withdrawalHash, provenWithdrawal.Event.GUID)
if err != nil {
return err
}
}
if len(provenWithdrawals) > 0 {
processLog.Info("proven transaction withdrawals", "size", len(provenWithdrawals))
}
// (2) Process Withdrawal Finalization
finalizedWithdrawals, err := OptimismPortalWithdrawalFinalizedEvents(events)
if err != nil {
return err
}
for _, finalizedWithdrawal := range finalizedWithdrawals {
withdrawalHash := finalizedWithdrawal.WithdrawalHash
withdrawal, err := db.BridgeTransactions.L2TransactionWithdrawal(withdrawalHash)
if err != nil {
return err
} else if withdrawal == nil {
// since withdrawals must be proven first, we don't have to check on the L2Processor
processLog.Crit("withdrawal missing!", "hash", withdrawalHash)
return errors.New("withdrawal missing!")
}
err = db.BridgeTransactions.MarkL2TransactionWithdrawalFinalizedEvent(withdrawalHash, finalizedWithdrawal.Event.GUID, finalizedWithdrawal.Success)
if err != nil {
return err
}
}
if len(finalizedWithdrawals) > 0 {
processLog.Info("finalized transaction withdrawals", "size", len(finalizedWithdrawals))
}
// a-ok
return nil
}
func l1ProcessContractEventsBridgeCrossDomainMessages(processLog log.Logger, db *database.DB, events *ProcessedContractEvents) error {
// (1) Process New Messages
sentMessageEvents, err := CrossDomainMessengerSentMessageEvents(events)
if err != nil {
return err
}
sentMessages := make([]*database.L1BridgeMessage, len(sentMessageEvents))
for i, sentMessageEvent := range sentMessageEvents {
log := sentMessageEvent.Event.RLPLog
// extract the deposit hash from the previous TransactionDepositedEvent
transactionDepositedLog := events.eventByLogIndex[ProcessedContractEventLogIndexKey{log.BlockHash, log.Index - 1}].RLPLog
depositTx, err := derive.UnmarshalDepositLogEvent(transactionDepositedLog)
if err != nil {
return err
}
sentMessages[i] = &database.L1BridgeMessage{
TransactionSourceHash: depositTx.SourceHash,
BridgeMessage: database.BridgeMessage{
MessageHash: sentMessageEvent.MessageHash,
Nonce: database.U256{Int: sentMessageEvent.MessageNonce},
SentMessageEventGUID: sentMessageEvent.Event.GUID,
GasLimit: database.U256{Int: sentMessageEvent.GasLimit},
Tx: database.Transaction{
FromAddress: sentMessageEvent.Sender,
ToAddress: sentMessageEvent.Target,
Amount: database.U256{Int: sentMessageEvent.Value},
Data: sentMessageEvent.Message,
Timestamp: sentMessageEvent.Event.Timestamp,
},
},
}
}
if len(sentMessages) > 0 {
processLog.Info("detected L1CrossDomainMessenger messages", "size", len(sentMessages))
err := db.BridgeMessages.StoreL1BridgeMessages(sentMessages)
if err != nil {
return err
}
}
// (2) Process Relayed Messages.
//
// NOTE: Should we care about failed messages? A failed message can be
// inferred via a finalized withdrawal that has not been marked as relayed.
relayedMessageEvents, err := CrossDomainMessengerRelayedMessageEvents(events)
if err != nil {
return err
}
for _, relayedMessage := range relayedMessageEvents {
message, err := db.BridgeMessages.L2BridgeMessage(relayedMessage.MsgHash)
if err != nil {
return err
} else if message == nil {
// Since L2 withdrawals must be proven before being relayed, the transaction processor
// ensures that we are in indexed state on L2 if we've seen this finalization event
processLog.Crit("missing indexed L2CrossDomainMessenger sent message", "message_hash", relayedMessage.MsgHash)
return fmt.Errorf("missing indexed L2CrossDomainMessager mesesage: 0x%x", relayedMessage.MsgHash)
}
err = db.BridgeMessages.MarkRelayedL2BridgeMessage(relayedMessage.MsgHash, relayedMessage.Event.GUID)
if err != nil {
return err
}
}
if len(relayedMessageEvents) > 0 {
processLog.Info("relayed L2CrossDomainMessenger messages", "size", len(relayedMessageEvents))
}
// a-ok!
return nil
}
func l1ProcessContractEventsStandardBridge(processLog log.Logger, db *database.DB, events *ProcessedContractEvents) error {
// (1) Process New Deposits
initiatedDepositEvents, err := StandardBridgeInitiatedEvents(events)
if err != nil {
return err
}
deposits := make([]*database.L1BridgeDeposit, len(initiatedDepositEvents))
for i, initiatedBridgeEvent := range initiatedDepositEvents {
log := initiatedBridgeEvent.Event.RLPLog
// extract the deposit hash from the following TransactionDeposited event. The `BlockHash` and `LogIndex`
// fields are filled in for `RLPLog` which is required for `DepositTx#SourceHash` to be computed correctly
transactionDepositedRLPLog := events.eventByLogIndex[ProcessedContractEventLogIndexKey{log.BlockHash, log.Index + 1}].RLPLog
depositTx, err := derive.UnmarshalDepositLogEvent(transactionDepositedRLPLog)
if err != nil {
return err
}
deposits[i] = &database.L1BridgeDeposit{
TransactionSourceHash: depositTx.SourceHash,
BridgeTransfer: database.BridgeTransfer{
CrossDomainMessageHash: &initiatedBridgeEvent.CrossDomainMessageHash,
// TODO index the tokens pairs if they don't exist
TokenPair: database.TokenPair{LocalTokenAddress: initiatedBridgeEvent.LocalToken, RemoteTokenAddress: initiatedBridgeEvent.RemoteToken},
Tx: database.Transaction{
FromAddress: initiatedBridgeEvent.From,
ToAddress: initiatedBridgeEvent.To,
Amount: database.U256{Int: initiatedBridgeEvent.Amount},
Data: initiatedBridgeEvent.ExtraData,
Timestamp: initiatedBridgeEvent.Event.Timestamp,
},
},
}
}
if len(deposits) > 0 {
processLog.Info("detected L1StandardBridge deposits", "size", len(deposits))
err := db.BridgeTransfers.StoreL1BridgeDeposits(deposits)
if err != nil {
return err
}
}
// (2) Process Finalized Withdrawals
// - We dont need do anything actionable on the database here as this is layered on top of the
// bridge transaction & messages that have a tracked lifecyle. We simply walk through and ensure
// that the corresponding initiated withdrawals exist and match as an integrity check
finalizedWithdrawalEvents, err := StandardBridgeFinalizedEvents(events)
if err != nil {
return err
}
for _, finalizedWithdrawalEvent := range finalizedWithdrawalEvents {
withdrawal, err := db.BridgeTransfers.L2BridgeWithdrawalWithFilter(database.BridgeTransfer{CrossDomainMessageHash: &finalizedWithdrawalEvent.CrossDomainMessageHash})
if err != nil {
return err
} else if withdrawal == nil {
processLog.Error("missing indexed L2StandardBridge withdrawal for finalization", "cross_domain_message_hash", finalizedWithdrawalEvent.CrossDomainMessageHash)
return errors.New("missing indexed L2StandardBridge withdrawal for finalization event")
}
// sanity check on the bridge fields
if finalizedWithdrawalEvent.From != withdrawal.Tx.FromAddress || finalizedWithdrawalEvent.To != withdrawal.Tx.ToAddress ||
finalizedWithdrawalEvent.Amount.Cmp(withdrawal.Tx.Amount.Int) != 0 || !bytes.Equal(finalizedWithdrawalEvent.ExtraData, withdrawal.Tx.Data) ||
finalizedWithdrawalEvent.LocalToken != withdrawal.TokenPair.LocalTokenAddress || finalizedWithdrawalEvent.RemoteToken != withdrawal.TokenPair.RemoteTokenAddress {
processLog.Crit("bridge finalization fields mismatch with initiated fields!", "tx_withdrawal_hash", withdrawal.TransactionWithdrawalHash, "cross_domain_message_hash", withdrawal.CrossDomainMessageHash)
}
}
// a-ok!
return nil
}
package processor
import (
"bytes"
"context"
"errors"
"fmt"
"reflect"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/node"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
)
type L2Contracts struct {
L2CrossDomainMessenger common.Address
L2StandardBridge common.Address
L2ERC721Bridge common.Address
L2ToL1MessagePasser common.Address
// Some more contracts -- ProxyAdmin, SystemConfig, etcc
// Ignore the auxiliary contracts?
// Legacy Contracts? We'll add this in to index the legacy chain.
// Remove afterwards?
}
func L2ContractPredeploys() L2Contracts {
return L2Contracts{
L2CrossDomainMessenger: common.HexToAddress("0x4200000000000000000000000000000000000007"),
L2StandardBridge: common.HexToAddress("0x4200000000000000000000000000000000000010"),
L2ERC721Bridge: common.HexToAddress("0x4200000000000000000000000000000000000014"),
L2ToL1MessagePasser: common.HexToAddress("0x4200000000000000000000000000000000000016"),
}
}
func (c L2Contracts) ToSlice() []common.Address {
fields := reflect.VisibleFields(reflect.TypeOf(c))
v := reflect.ValueOf(c)
contracts := make([]common.Address, len(fields))
for i, field := range fields {
contracts[i] = (v.FieldByName(field.Name).Interface()).(common.Address)
}
return contracts
}
type L2Processor struct {
processor
}
func NewL2Processor(logger log.Logger, ethClient node.EthClient, db *database.DB, l2Contracts L2Contracts) (*L2Processor, error) {
l2ProcessLog := logger.New("processor", "l2")
l2ProcessLog.Info("initializing processor")
latestHeader, err := db.Blocks.LatestL2BlockHeader()
if err != nil {
return nil, err
}
var fromL2Header *types.Header
if latestHeader != nil {
l2ProcessLog.Info("detected last indexed block", "height", latestHeader.Number.Int, "hash", latestHeader.Hash)
l2Header, err := ethClient.BlockHeaderByHash(latestHeader.Hash)
if err != nil {
l2ProcessLog.Error("unable to fetch header for last indexed block", "hash", latestHeader.Hash, "err", err)
return nil, err
}
fromL2Header = l2Header
} else {
l2ProcessLog.Info("no indexed state, starting from genesis")
fromL2Header = nil
}
l2Processor := &L2Processor{
processor: processor{
headerTraversal: node.NewHeaderTraversal(ethClient, fromL2Header),
db: db,
processFn: l2ProcessFn(l2ProcessLog, ethClient, l2Contracts),
processLog: l2ProcessLog,
},
}
return l2Processor, nil
}
func l2ProcessFn(processLog log.Logger, ethClient node.EthClient, l2Contracts L2Contracts) ProcessFn {
rawEthClient := ethclient.NewClient(ethClient.RawRpcClient())
contractAddrs := l2Contracts.ToSlice()
processLog.Info("processor configured with contracts", "contracts", l2Contracts)
return func(db *database.DB, headers []*types.Header) error {
numHeaders := len(headers)
/** Index all L2 blocks **/
l2Headers := make([]*database.L2BlockHeader, len(headers))
l2HeaderMap := make(map[common.Hash]*types.Header)
for i, header := range headers {
l2Headers[i] = &database.L2BlockHeader{BlockHeader: database.BlockHeaderFromHeader(header)}
l2HeaderMap[l2Headers[i].Hash] = header
}
/** Watch for Contract Events **/
logFilter := ethereum.FilterQuery{FromBlock: headers[0].Number, ToBlock: headers[numHeaders-1].Number, Addresses: contractAddrs}
logs, err := rawEthClient.FilterLogs(context.Background(), logFilter)
if err != nil {
return err
}
l2ContractEvents := make([]*database.L2ContractEvent, len(logs))
processedContractEvents := NewProcessedContractEvents()
for i := range logs {
log := &logs[i]
header, ok := l2HeaderMap[log.BlockHash]
if !ok {
processLog.Error("contract event found with associated header not in the batch", "header", header, "log_index", log.Index)
return errors.New("parsed log with a block hash not in this batch")
}
contractEvent := processedContractEvents.AddLog(log, header.Time)
l2ContractEvents[i] = &database.L2ContractEvent{ContractEvent: *contractEvent}
}
/** Update Database **/
processLog.Info("saving l2 blocks", "size", numHeaders)
err = db.Blocks.StoreL2BlockHeaders(l2Headers)
if err != nil {
return err
}
numLogs := len(l2ContractEvents)
if numLogs > 0 {
processLog.Info("detected contract logs", "size", numLogs)
err = db.ContractEvents.StoreL2ContractEvents(l2ContractEvents)
if err != nil {
return err
}
// forward along contract events to bridge txs processor
err = l2ProcessContractEventsBridgeTransactions(processLog, db, processedContractEvents)
if err != nil {
return err
}
err = l2ProcessContractEventsBridgeCrossDomainMessages(processLog, db, processedContractEvents)
if err != nil {
return err
}
// forward along contract events to standard bridge processor
err = l2ProcessContractEventsStandardBridge(processLog, db, processedContractEvents)
if err != nil {
return err
}
}
// a-ok!
return nil
}
}
func l2ProcessContractEventsBridgeTransactions(processLog log.Logger, db *database.DB, events *ProcessedContractEvents) error {
// (1) Process New Withdrawals
messagesPassed, err := L2ToL1MessagePasserMessagesPassed(events)
if err != nil {
return err
}
ethWithdrawals := []*database.L2BridgeWithdrawal{}
transactionWithdrawals := make([]*database.L2TransactionWithdrawal, len(messagesPassed))
for i, withdrawalEvent := range messagesPassed {
transactionWithdrawals[i] = &database.L2TransactionWithdrawal{
WithdrawalHash: withdrawalEvent.WithdrawalHash,
InitiatedL2EventGUID: withdrawalEvent.Event.GUID,
Nonce: database.U256{Int: withdrawalEvent.Nonce},
GasLimit: database.U256{Int: withdrawalEvent.GasLimit},
Tx: database.Transaction{
FromAddress: withdrawalEvent.Sender,
ToAddress: withdrawalEvent.Target,
Amount: database.U256{Int: withdrawalEvent.Value},
Data: withdrawalEvent.Data,
Timestamp: withdrawalEvent.Event.Timestamp,
},
}
if len(withdrawalEvent.Data) == 0 && withdrawalEvent.Value.BitLen() > 0 {
ethWithdrawals = append(ethWithdrawals, &database.L2BridgeWithdrawal{
TransactionWithdrawalHash: withdrawalEvent.WithdrawalHash,
BridgeTransfer: database.BridgeTransfer{
Tx: transactionWithdrawals[i].Tx,
TokenPair: database.ETHTokenPair,
},
})
}
}
if len(transactionWithdrawals) > 0 {
processLog.Info("detected transaction withdrawals", "size", len(transactionWithdrawals))
err := db.BridgeTransactions.StoreL2TransactionWithdrawals(transactionWithdrawals)
if err != nil {
return err
}
if len(ethWithdrawals) > 0 {
processLog.Info("detected L2ToL1MessagePasser ETH transfers", "size", len(ethWithdrawals))
err := db.BridgeTransfers.StoreL2BridgeWithdrawals(ethWithdrawals)
if err != nil {
return err
}
}
}
// (2) Process Deposit Finalization
// - Since L2 deposits are apart of the block derivation processes, we dont track finalization as it's too tricky
// to do so purely from the L2-side since there is not a way to easily identify deposit transactions on L2 without walking
// the transaction list of every L2 epoch.
// a-ok!
return nil
}
func l2ProcessContractEventsBridgeCrossDomainMessages(processLog log.Logger, db *database.DB, events *ProcessedContractEvents) error {
l2ToL1MessagePasserABI, err := bindings.NewL2ToL1MessagePasser(common.Address{}, nil)
if err != nil {
return err
}
// (2) Process New Messages
sentMessageEvents, err := CrossDomainMessengerSentMessageEvents(events)
if err != nil {
return err
}
sentMessages := make([]*database.L2BridgeMessage, len(sentMessageEvents))
for i, sentMessageEvent := range sentMessageEvents {
log := sentMessageEvent.Event.RLPLog
// extract the withdrawal hash from the previous MessagePassed event
msgPassedLog := events.eventByLogIndex[ProcessedContractEventLogIndexKey{log.BlockHash, log.Index - 1}].RLPLog
msgPassedEvent, err := l2ToL1MessagePasserABI.ParseMessagePassed(*msgPassedLog)
if err != nil {
return err
}
sentMessages[i] = &database.L2BridgeMessage{
TransactionWithdrawalHash: msgPassedEvent.WithdrawalHash,
BridgeMessage: database.BridgeMessage{
MessageHash: sentMessageEvent.MessageHash,
Nonce: database.U256{Int: sentMessageEvent.MessageNonce},
SentMessageEventGUID: sentMessageEvent.Event.GUID,
GasLimit: database.U256{Int: sentMessageEvent.GasLimit},
Tx: database.Transaction{
FromAddress: sentMessageEvent.Sender,
ToAddress: sentMessageEvent.Target,
Amount: database.U256{Int: sentMessageEvent.Value},
Data: sentMessageEvent.Message,
Timestamp: sentMessageEvent.Event.Timestamp,
},
},
}
}
if len(sentMessages) > 0 {
processLog.Info("detected L2CrossDomainMessenger messages", "size", len(sentMessages))
err := db.BridgeMessages.StoreL2BridgeMessages(sentMessages)
if err != nil {
return err
}
}
// (2) Process Relayed Messages.
//
// NOTE: Should we care about failed messages? A failed message can be
// inferred via an included deposit on L2 that has not been marked as relayed.
relayedMessageEvents, err := CrossDomainMessengerRelayedMessageEvents(events)
if err != nil {
return err
}
latestL1Header, err := db.Blocks.LatestL1BlockHeader()
if err != nil {
return err
} else if len(relayedMessageEvents) > 0 && latestL1Header == nil {
return errors.New("no indexed L1 headers to relay messages. waiting for L1Processor to catch up")
}
for _, relayedMessage := range relayedMessageEvents {
message, err := db.BridgeMessages.L1BridgeMessage(relayedMessage.MsgHash)
if err != nil {
return err
}
if message == nil {
// Since the transaction processor running prior does not ensure the deposit inclusion, we need to
// ensure we are in a caught up state before claiming a missing event. Since L2 timestamps are derived
// from L1, we can simply compare the timestamp of this event with the latest L1 header.
if latestL1Header == nil || relayedMessage.Event.Timestamp > latestL1Header.Timestamp {
processLog.Warn("waiting for L1Processor to catch up on L1CrossDomainMessages")
return errors.New("waiting for L1Processor to catch up")
} else {
processLog.Crit("missing indexed L1CrossDomainMessenger message", "message_hash", relayedMessage.MsgHash)
return fmt.Errorf("missing indexed L1CrossDomainMessager mesesage: 0x%x", relayedMessage.MsgHash)
}
}
err = db.BridgeMessages.MarkRelayedL1BridgeMessage(relayedMessage.MsgHash, relayedMessage.Event.GUID)
if err != nil {
return err
}
}
if len(relayedMessageEvents) > 0 {
processLog.Info("relayed L1CrossDomainMessenger messages", "size", len(relayedMessageEvents))
}
// a-ok!
return nil
}
func l2ProcessContractEventsStandardBridge(processLog log.Logger, db *database.DB, events *ProcessedContractEvents) error {
l2ToL1MessagePasserABI, err := bindings.NewL2ToL1MessagePasser(common.Address{}, nil)
if err != nil {
return err
}
// (1) Process New Withdrawals
initiatedWithdrawalEvents, err := StandardBridgeInitiatedEvents(events)
if err != nil {
return err
}
withdrawals := make([]*database.L2BridgeWithdrawal, len(initiatedWithdrawalEvents))
for i, initiatedBridgeEvent := range initiatedWithdrawalEvents {
log := initiatedBridgeEvent.Event.RLPLog
// extract the withdrawal hash from the following MessagePassed event
msgPassedLog := events.eventByLogIndex[ProcessedContractEventLogIndexKey{log.BlockHash, log.Index + 1}].RLPLog
msgPassedEvent, err := l2ToL1MessagePasserABI.ParseMessagePassed(*msgPassedLog)
if err != nil {
return err
}
withdrawals[i] = &database.L2BridgeWithdrawal{
TransactionWithdrawalHash: msgPassedEvent.WithdrawalHash,
BridgeTransfer: database.BridgeTransfer{
CrossDomainMessageHash: &initiatedBridgeEvent.CrossDomainMessageHash,
TokenPair: database.TokenPair{LocalTokenAddress: initiatedBridgeEvent.LocalToken, RemoteTokenAddress: initiatedBridgeEvent.RemoteToken},
Tx: database.Transaction{
FromAddress: initiatedBridgeEvent.From,
ToAddress: initiatedBridgeEvent.To,
Amount: database.U256{Int: initiatedBridgeEvent.Amount},
Data: initiatedBridgeEvent.ExtraData,
Timestamp: initiatedBridgeEvent.Event.Timestamp,
},
},
}
}
if len(withdrawals) > 0 {
processLog.Info("detected L2StandardBridge withdrawals", "num", len(withdrawals))
err := db.BridgeTransfers.StoreL2BridgeWithdrawals(withdrawals)
if err != nil {
return err
}
}
// (2) Process Finalized Deposits
// - We dont need do anything actionable on the database here as this is layered on top of the
// bridge transaction & messages that have a tracked lifecyle. We simply walk through and ensure
// that the corresponding initiated deposits exist as an integrity check
finalizedDepositEvents, err := StandardBridgeFinalizedEvents(events)
if err != nil {
return err
}
for _, finalizedDepositEvent := range finalizedDepositEvents {
deposit, err := db.BridgeTransfers.L1BridgeDepositWithFilter(database.BridgeTransfer{CrossDomainMessageHash: &finalizedDepositEvent.CrossDomainMessageHash})
if err != nil {
return err
} else if deposit == nil {
// Indexed CrossDomainMessenger messages ensure we're in a caught up state here
processLog.Error("missing indexed L1StandardBridge deposit on finalization", "cross_domain_massage_hash", finalizedDepositEvent.CrossDomainMessageHash)
return errors.New("missing indexed L1StandardBridge deposit on finalization")
}
// sanity check on the bridge fields
if finalizedDepositEvent.From != deposit.Tx.FromAddress || finalizedDepositEvent.To != deposit.Tx.ToAddress ||
finalizedDepositEvent.Amount.Cmp(deposit.Tx.Amount.Int) != 0 || !bytes.Equal(finalizedDepositEvent.ExtraData, deposit.Tx.Data) ||
finalizedDepositEvent.LocalToken != deposit.TokenPair.LocalTokenAddress || finalizedDepositEvent.RemoteToken != deposit.TokenPair.RemoteTokenAddress {
processLog.Error("bridge finalization fields mismatch with initiated fields!", "tx_source_hash", deposit.TransactionSourceHash, "cross_domain_message_hash", deposit.CrossDomainMessageHash)
return errors.New("bridge tx mismatch")
}
}
// a-ok!
return nil
}
package processor
import (
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
)
type L2ToL1MessagePasserMessagePassed struct {
*bindings.L2ToL1MessagePasserMessagePassed
Event *database.ContractEvent
}
func L2ToL1MessagePasserMessagesPassed(events *ProcessedContractEvents) ([]L2ToL1MessagePasserMessagePassed, error) {
l2ToL1MessagePasserAbi, err := bindings.L2ToL1MessagePasserMetaData.GetAbi()
if err != nil {
return nil, err
}
eventName := "MessagePassed"
processedMessagePassedEvents := events.eventsBySignature[l2ToL1MessagePasserAbi.Events[eventName].ID]
messagesPassed := make([]L2ToL1MessagePasserMessagePassed, len(processedMessagePassedEvents))
for i, messagePassedEvent := range processedMessagePassedEvents {
log := messagePassedEvent.RLPLog
var messagePassed bindings.L2ToL1MessagePasserMessagePassed
messagePassed.Raw = *log
err := UnpackLog(&messagePassed, log, eventName, l2ToL1MessagePasserAbi)
if err != nil {
return nil, err
}
messagesPassed[i] = L2ToL1MessagePasserMessagePassed{
L2ToL1MessagePasserMessagePassed: &messagePassed,
Event: messagePassedEvent,
}
}
return messagesPassed, nil
}
package processor
import (
"errors"
"math/big"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/core/types"
)
type OptimismPortalTransactionDepositEvent struct {
*bindings.OptimismPortalTransactionDeposited
DepositTx *types.DepositTx
Event *database.ContractEvent
}
type OptimismPortalWithdrawalProvenEvent struct {
*bindings.OptimismPortalWithdrawalProven
Event *database.ContractEvent
}
type OptimismPortalWithdrawalFinalizedEvent struct {
*bindings.OptimismPortalWithdrawalFinalized
Event *database.ContractEvent
}
type OptimismPortalProvenWithdrawal struct {
OutputRoot [32]byte
Timestamp *big.Int
L2OutputIndex *big.Int
}
func OptimismPortalTransactionDepositEvents(events *ProcessedContractEvents) ([]OptimismPortalTransactionDepositEvent, error) {
optimismPortalAbi, err := bindings.OptimismPortalMetaData.GetAbi()
if err != nil {
return nil, err
}
eventName := "TransactionDeposited"
if optimismPortalAbi.Events[eventName].ID != derive.DepositEventABIHash {
return nil, errors.New("op-node deposit event abi hash & optimism portal tx deposit mismatch")
}
processedTxDepositedEvents := events.eventsBySignature[derive.DepositEventABIHash]
txDeposits := make([]OptimismPortalTransactionDepositEvent, len(processedTxDepositedEvents))
for i, txDepositEvent := range processedTxDepositedEvents {
log := txDepositEvent.RLPLog
depositTx, err := derive.UnmarshalDepositLogEvent(log)
if err != nil {
return nil, err
}
var txDeposit bindings.OptimismPortalTransactionDeposited
txDeposit.Raw = *log
err = UnpackLog(&txDeposit, log, eventName, optimismPortalAbi)
if err != nil {
return nil, err
}
txDeposits[i] = OptimismPortalTransactionDepositEvent{
OptimismPortalTransactionDeposited: &txDeposit,
DepositTx: depositTx,
Event: txDepositEvent,
}
}
return txDeposits, nil
}
func OptimismPortalWithdrawalProvenEvents(events *ProcessedContractEvents) ([]OptimismPortalWithdrawalProvenEvent, error) {
optimismPortalAbi, err := bindings.OptimismPortalMetaData.GetAbi()
if err != nil {
return nil, err
}
eventName := "WithdrawalProven"
processedWithdrawalProvenEvents := events.eventsBySignature[optimismPortalAbi.Events[eventName].ID]
provenEvents := make([]OptimismPortalWithdrawalProvenEvent, len(processedWithdrawalProvenEvents))
for i, provenEvent := range processedWithdrawalProvenEvents {
log := provenEvent.RLPLog
var withdrawalProven bindings.OptimismPortalWithdrawalProven
withdrawalProven.Raw = *log
err := UnpackLog(&withdrawalProven, log, eventName, optimismPortalAbi)
if err != nil {
return nil, err
}
provenEvents[i] = OptimismPortalWithdrawalProvenEvent{
OptimismPortalWithdrawalProven: &withdrawalProven,
Event: provenEvent,
}
}
return provenEvents, nil
}
func OptimismPortalWithdrawalFinalizedEvents(events *ProcessedContractEvents) ([]OptimismPortalWithdrawalFinalizedEvent, error) {
optimismPortalAbi, err := bindings.OptimismPortalMetaData.GetAbi()
if err != nil {
return nil, err
}
eventName := "WithdrawalFinalized"
processedWithdrawalFinalizedEvents := events.eventsBySignature[optimismPortalAbi.Events[eventName].ID]
finalizedEvents := make([]OptimismPortalWithdrawalFinalizedEvent, len(processedWithdrawalFinalizedEvents))
for i, finalizedEvent := range processedWithdrawalFinalizedEvents {
log := finalizedEvent.RLPLog
var withdrawalFinalized bindings.OptimismPortalWithdrawalFinalized
err := UnpackLog(&withdrawalFinalized, log, eventName, optimismPortalAbi)
if err != nil {
return nil, err
}
finalizedEvents[i] = OptimismPortalWithdrawalFinalizedEvent{
OptimismPortalWithdrawalFinalized: &withdrawalFinalized,
Event: finalizedEvent,
}
}
return finalizedEvents, nil
}
package processor
import (
"context"
"time"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/node"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
const (
defaultLoopInterval = 5 * time.Second
defaultHeaderBufferSize = 500
)
// ProcessFn is the the entrypoint for processing a batch of headers.
// In the event of failure, database operations are rolled back
type ProcessFn func(*database.DB, []*types.Header) error
type processor struct {
headerTraversal *node.HeaderTraversal
db *database.DB
processFn ProcessFn
processLog log.Logger
paused bool
latestProcessedHeader *types.Header
}
// Start kicks off the processing loop. This is a block operation
// unless the processor encountering an error, abrupting the loop,
// or the supplied context is cancelled.
func (p *processor) Start(ctx context.Context) error {
done := ctx.Done()
pollTicker := time.NewTicker(defaultLoopInterval)
defer pollTicker.Stop()
p.processLog.Info("starting processor...")
var unprocessedHeaders []*types.Header
for {
select {
case <-done:
p.processLog.Info("stopping processor")
return nil
case <-pollTicker.C:
if p.paused {
p.processLog.Warn("processor is paused...")
continue
}
if len(unprocessedHeaders) == 0 {
newHeaders, err := p.headerTraversal.NextFinalizedHeaders(defaultHeaderBufferSize)
if err != nil {
p.processLog.Error("error querying for headers", "err", err)
continue
} else if len(newHeaders) == 0 {
// Logged as an error since this loop should be operating at a longer interval than the provider
p.processLog.Error("no new headers. processor unexpectedly at head...")
continue
}
unprocessedHeaders = newHeaders
} else {
p.processLog.Info("retrying previous batch")
}
firstHeader := unprocessedHeaders[0]
lastHeader := unprocessedHeaders[len(unprocessedHeaders)-1]
batchLog := p.processLog.New("batch_start_block_number", firstHeader.Number, "batch_end_block_number", lastHeader.Number)
err := p.db.Transaction(func(db *database.DB) error {
batchLog.Info("processing batch")
return p.processFn(db, unprocessedHeaders)
})
// Eventually, we want to halt the processor on any error rather than rely
// on this loop for retry functionality.
if err != nil {
batchLog.Warn("error processing batch. no operations committed", "err", err)
} else {
batchLog.Info("fully committed batch")
unprocessedHeaders = nil
p.latestProcessedHeader = lastHeader
}
}
}
}
func (p processor) LatestProcessedHeader() *types.Header {
return p.latestProcessedHeader
}
// Useful ONLY for tests!
func (p *processor) PauseForTest() {
p.paused = true
}
func (p *processor) ResumeForTest() {
p.paused = false
}
package processor
import (
"bytes"
"errors"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum/go-ethereum/common"
)
type StandardBridgeInitiatedEvent struct {
// We hardcode to ERC20 since ETH can be pseudo-represented as an ERC20 utilizing
// the hardcoded ETH address
*bindings.StandardBridgeERC20BridgeInitiated
CrossDomainMessageHash common.Hash
Event *database.ContractEvent
}
type StandardBridgeFinalizedEvent struct {
// We hardcode to ERC20 since ETH can be pseudo-represented as an ERC20 utilizing
// the hardcoded ETH address
*bindings.StandardBridgeERC20BridgeFinalized
CrossDomainMessageHash common.Hash
Event *database.ContractEvent
}
// StandardBridgeInitiatedEvents extracts all initiated bridge events from the contracts that follow the StandardBridge ABI. The
// correlated CrossDomainMessenger nonce is also parsed from the associated messenger events.
func StandardBridgeInitiatedEvents(events *ProcessedContractEvents) ([]StandardBridgeInitiatedEvent, error) {
ethBridgeInitiatedEvents, err := _standardBridgeInitiatedEvents[bindings.StandardBridgeETHBridgeInitiated](events)
if err != nil {
return nil, err
}
erc20BridgeInitiatedEvents, err := _standardBridgeInitiatedEvents[bindings.StandardBridgeERC20BridgeInitiated](events)
if err != nil {
return nil, err
}
return append(ethBridgeInitiatedEvents, erc20BridgeInitiatedEvents...), nil
}
// StandardBridgeFinalizedEvents extracts all finalization bridge events from the contracts that follow the StandardBridge ABI. The
// correlated CrossDomainMessenger nonce is also parsed by looking at the parameters of the corresponding relayMessage transaction data.
func StandardBridgeFinalizedEvents(events *ProcessedContractEvents) ([]StandardBridgeFinalizedEvent, error) {
ethBridgeFinalizedEvents, err := _standardBridgeFinalizedEvents[bindings.StandardBridgeETHBridgeFinalized](events)
if err != nil {
return nil, err
}
erc20BridgeFinalizedEvents, err := _standardBridgeFinalizedEvents[bindings.StandardBridgeERC20BridgeFinalized](events)
if err != nil {
return nil, err
}
return append(ethBridgeFinalizedEvents, erc20BridgeFinalizedEvents...), nil
}
// parse out eth or erc20 bridge initiated events
func _standardBridgeInitiatedEvents[BridgeEvent bindings.StandardBridgeETHBridgeInitiated | bindings.StandardBridgeERC20BridgeInitiated](
events *ProcessedContractEvents,
) ([]StandardBridgeInitiatedEvent, error) {
standardBridgeABI, err := bindings.StandardBridgeMetaData.GetAbi()
if err != nil {
return nil, err
}
crossDomainMessengerABI, err := bindings.CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return nil, err
}
sentMessageEventAbi := crossDomainMessengerABI.Events["SentMessage"]
sentMessageExtensionEventAbi := crossDomainMessengerABI.Events["SentMessageExtension1"]
var tmp BridgeEvent
var eventName string
var finalizeMethodName string
switch any(tmp).(type) {
case bindings.StandardBridgeETHBridgeInitiated:
eventName = "ETHBridgeInitiated"
finalizeMethodName = "finalizeBridgeETH"
case bindings.StandardBridgeERC20BridgeInitiated:
eventName = "ERC20BridgeInitiated"
finalizeMethodName = "finalizeBridgeERC20"
default:
panic("should not be here")
}
processedInitiatedBridgeEvents := events.eventsBySignature[standardBridgeABI.Events[eventName].ID]
initiatedBridgeEvents := make([]StandardBridgeInitiatedEvent, len(processedInitiatedBridgeEvents))
for i, bridgeInitiatedEvent := range processedInitiatedBridgeEvents {
log := bridgeInitiatedEvent.RLPLog
var bridgeData BridgeEvent
err := UnpackLog(&bridgeData, log, eventName, standardBridgeABI)
if err != nil {
return nil, err
}
// Look for the sent message event to compute the message hash of the relayed tx
// - L1: BridgeInitiated -> Portal#DepositTransaction -> SentMessage ...
// - L1: BridgeInitiated -> L2ToL1MessagePasser#MessagePassed -> SentMessage ...
var sentMsgData bindings.CrossDomainMessengerSentMessage
sentMsgLog := events.eventByLogIndex[ProcessedContractEventLogIndexKey{log.BlockHash, log.Index + 2}].RLPLog
if sentMsgLog.Topics[0] != sentMessageEventAbi.ID {
return nil, errors.New("unexpected bridge event ordering")
}
sentMsgData.Raw = *sentMsgLog
err = UnpackLog(&sentMsgData, sentMsgLog, sentMessageEventAbi.Name, crossDomainMessengerABI)
if err != nil {
return nil, err
}
var sentMsgExtensionData bindings.CrossDomainMessengerSentMessageExtension1
sentMsgExtensionLog := events.eventByLogIndex[ProcessedContractEventLogIndexKey{log.BlockHash, log.Index + 3}].RLPLog
if sentMsgExtensionLog.Topics[0] != sentMessageExtensionEventAbi.ID {
return nil, errors.New("unexpected bridge event ordering")
}
sentMsgData.Raw = *sentMsgLog
err = UnpackLog(&sentMsgExtensionData, sentMsgExtensionLog, sentMessageExtensionEventAbi.Name, crossDomainMessengerABI)
if err != nil {
return nil, err
}
msgHash, err := CrossDomainMessageHash(crossDomainMessengerABI, &sentMsgData, sentMsgExtensionData.Value)
if err != nil {
return nil, err
}
var erc20BridgeData *bindings.StandardBridgeERC20BridgeInitiated
var expectedCrossDomainMessage []byte
switch any(bridgeData).(type) {
case bindings.StandardBridgeETHBridgeInitiated:
ethBridgeData := any(bridgeData).(bindings.StandardBridgeETHBridgeInitiated)
expectedCrossDomainMessage, err = standardBridgeABI.Pack(finalizeMethodName, ethBridgeData.From, ethBridgeData.To, ethBridgeData.Amount, ethBridgeData.ExtraData)
if err != nil {
return nil, err
}
// represent eth bridge as an erc20
erc20BridgeData = &bindings.StandardBridgeERC20BridgeInitiated{
Raw: *log,
// Represent ETH using the hardcoded address
LocalToken: predeploys.LegacyERC20ETHAddr, RemoteToken: predeploys.LegacyERC20ETHAddr,
// Bridge data
From: ethBridgeData.From, To: ethBridgeData.To, Amount: ethBridgeData.Amount, ExtraData: ethBridgeData.ExtraData,
}
case bindings.StandardBridgeERC20BridgeInitiated:
_temp := any(bridgeData).(bindings.StandardBridgeERC20BridgeInitiated)
erc20BridgeData = &_temp
erc20BridgeData.Raw = *log
expectedCrossDomainMessage, err = standardBridgeABI.Pack(finalizeMethodName, erc20BridgeData.RemoteToken, erc20BridgeData.LocalToken, erc20BridgeData.From, erc20BridgeData.To, erc20BridgeData.Amount, erc20BridgeData.ExtraData)
if err != nil {
return nil, err
}
}
if !bytes.Equal(sentMsgData.Message, expectedCrossDomainMessage) {
return nil, errors.New("bridge cross domain message mismatch")
}
initiatedBridgeEvents[i] = StandardBridgeInitiatedEvent{
StandardBridgeERC20BridgeInitiated: erc20BridgeData,
CrossDomainMessageHash: msgHash,
Event: bridgeInitiatedEvent,
}
}
return initiatedBridgeEvents, nil
}
// parse out eth or erc20 bridge finalization events
func _standardBridgeFinalizedEvents[BridgeEvent bindings.StandardBridgeETHBridgeFinalized | bindings.StandardBridgeERC20BridgeFinalized](
events *ProcessedContractEvents,
) ([]StandardBridgeFinalizedEvent, error) {
standardBridgeABI, err := bindings.StandardBridgeMetaData.GetAbi()
if err != nil {
return nil, err
}
crossDomainMessengerABI, err := bindings.CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return nil, err
}
relayedMessageEventAbi := crossDomainMessengerABI.Events["RelayedMessage"]
var bridgeData BridgeEvent
var eventName string
switch any(bridgeData).(type) {
case bindings.StandardBridgeETHBridgeFinalized:
eventName = "ETHBridgeFinalized"
case bindings.StandardBridgeERC20BridgeFinalized:
eventName = "ERC20BridgeFinalized"
default:
panic("should not be here")
}
processedFinalizedBridgeEvents := events.eventsBySignature[standardBridgeABI.Events[eventName].ID]
finalizedBridgeEvents := make([]StandardBridgeFinalizedEvent, len(processedFinalizedBridgeEvents))
for i, bridgeFinalizedEvent := range processedFinalizedBridgeEvents {
log := bridgeFinalizedEvent.RLPLog
var bridgeData BridgeEvent
err := UnpackLog(&bridgeData, log, eventName, standardBridgeABI)
if err != nil {
return nil, err
}
// Look for the RelayedMessage event that follows right after the BridgeFinalized Event
var relayedMsgData bindings.CrossDomainMessengerRelayedMessage
relayedMsgLog := events.eventByLogIndex[ProcessedContractEventLogIndexKey{log.BlockHash, log.Index + 1}].RLPLog
if relayedMsgLog.Topics[0] != relayedMessageEventAbi.ID {
return nil, errors.New("unexpected bridge event ordering")
}
err = UnpackLog(&relayedMsgData, relayedMsgLog, relayedMessageEventAbi.Name, crossDomainMessengerABI)
if err != nil {
return nil, err
}
var erc20BridgeData *bindings.StandardBridgeERC20BridgeFinalized
switch any(bridgeData).(type) {
case bindings.StandardBridgeETHBridgeFinalized:
ethBridgeData := any(bridgeData).(bindings.StandardBridgeETHBridgeFinalized)
erc20BridgeData = &bindings.StandardBridgeERC20BridgeFinalized{
Raw: *log,
// Represent ETH using the hardcoded address
LocalToken: predeploys.LegacyERC20ETHAddr, RemoteToken: predeploys.LegacyERC20ETHAddr,
// Bridge data
From: ethBridgeData.From, To: ethBridgeData.To, Amount: ethBridgeData.Amount, ExtraData: ethBridgeData.ExtraData,
}
case bindings.StandardBridgeERC20BridgeFinalized:
_temp := any(bridgeData).(bindings.StandardBridgeERC20BridgeFinalized)
erc20BridgeData = &_temp
erc20BridgeData.Raw = *log
}
finalizedBridgeEvents[i] = StandardBridgeFinalizedEvent{
StandardBridgeERC20BridgeFinalized: erc20BridgeData,
CrossDomainMessageHash: relayedMsgData.MsgHash,
Event: bridgeFinalizedEvent,
}
}
return finalizedBridgeEvents, nil
}
package processors
import (
"context"
"errors"
"math/big"
"time"
"github.com/ethereum-optimism/optimism/indexer/config"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/processors/bridge"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
type BridgeProcessor struct {
log log.Logger
db *database.DB
chainConfig config.ChainConfig
// NOTE: We'll need this processor to handle for reorgs events.
LatestL1Header *types.Header
LatestL2Header *types.Header
}
func NewBridgeProcessor(log log.Logger, db *database.DB, chainConfig config.ChainConfig) (*BridgeProcessor, error) {
log = log.New("processor", "bridge")
latestL1Header, err := bridge.L1LatestBridgeEventHeader(db, chainConfig)
if err != nil {
return nil, err
}
latestL2Header, err := bridge.L2LatestBridgeEventHeader(db)
if err != nil {
return nil, err
}
// Since the bridge processor indexes events based on epochs, there's
// no scenario in which we have indexed L2 data with no L1 data.
//
// NOTE: Technically there is an exception if our bridging contracts are
// used to bridges native from L2 and an op-chain happens to launch where
// only L2 native bridge events have occurred. This is a rare situation now
// and it's worth the assertion as an integrity check. We can revisit this
// as more chains launch with primarily L2-native activity.
if latestL1Header == nil && latestL2Header != nil {
log.Error("detected indexed L2 bridge activity with no indexed L1 state", "l2_block_number", latestL2Header.Number)
return nil, errors.New("detected indexed L2 bridge activity with no indexed L1 state")
}
if latestL1Header == nil && latestL2Header == nil {
log.Info("no indexed state, starting from genesis")
} else {
log.Info("detected the latest indexed state", "l1_block_number", latestL1Header.Number, "l2_block_number", latestL2Header.Number)
}
return &BridgeProcessor{log, db, chainConfig, latestL1Header, latestL2Header}, nil
}
func (b *BridgeProcessor) Start(ctx context.Context) error {
done := ctx.Done()
// NOTE: This should run on same iterval as L1 ETL rather than as finding the
// lasted epoch is constrained to how much L1 data we've indexed.
pollTicker := time.NewTicker(5 * time.Second)
defer pollTicker.Stop()
// In order to ensure all seen bridge finalization events correspond with seen
// bridge initiated events, we establish a shared marker between L1 and L2 when
// processing events.
//
// As L1 and L2 blocks are indexed, the highest indexed L2 block starting a new
// sequencing epoch and corresponding L1 origin that has also been indexed
// serves as this shared marker.
// TODOs:
// 1. Fix Logging. Should be clear if we're looking at L1 or L2 side of things
b.log.Info("starting bridge processor...")
for {
select {
case <-done:
b.log.Info("stopping bridge processor")
return nil
case <-pollTicker.C:
latestEpoch, err := b.db.Blocks.LatestEpoch()
if err != nil {
return err
}
if latestEpoch == nil {
if b.LatestL1Header != nil {
// Once we have some satte `latestEpoch` should never return nil.
b.log.Error("started with indexed bridge state, but no blocks epochs returned", "latest_bridge_l1_block_number", b.LatestL1Header.Number)
return errors.New("started with indexed bridge state, but no blocks epochs returned")
} else {
b.log.Warn("no indexed block state. waiting...")
continue
}
}
if b.LatestL1Header != nil && latestEpoch.L1BlockHeader.Hash == b.LatestL1Header.Hash() {
// Marked as a warning since the bridge should always be processing at least 1 new epoch
b.log.Warn("all available epochs indexed by the bridge", "latest_epoch_number", b.LatestL1Header.Number)
continue
}
toL1Height, toL2Height := latestEpoch.L1BlockHeader.Number.Int, latestEpoch.L2BlockHeader.Number.Int
fromL1Height, fromL2Height := big.NewInt(0), big.NewInt(0)
if b.LatestL1Header != nil {
// `NewBridgeProcessor` ensures that LatestL2Header must not be nil if LatestL1Header is set
fromL1Height = new(big.Int).Add(b.LatestL1Header.Number, big.NewInt(1))
fromL2Height = new(big.Int).Add(b.LatestL2Header.Number, big.NewInt(1))
}
batchLog := b.log.New("epoch_start_number", fromL1Height, "epoch_end_number", toL1Height)
batchLog.Info("scanning bridge events")
err = b.db.Transaction(func(tx *database.DB) error {
l1BridgeLog := b.log.New("from_l1_block_number", fromL1Height, "to_l1_block_number", toL1Height)
l2BridgeLog := b.log.New("from_l2_block_number", fromL2Height, "to_l2_block_number", toL2Height)
// First, find all possible initiated bridge events
if err := bridge.L1ProcessInitiatedBridgeEvents(l1BridgeLog, tx, b.chainConfig, fromL1Height, toL1Height); err != nil {
return err
}
if err := bridge.L2ProcessInitiatedBridgeEvents(l2BridgeLog, tx, fromL2Height, toL2Height); err != nil {
return err
}
// Now that all initiated events have been indexed, it is ensured that all finalization can find their counterpart.
if err := bridge.L1ProcessFinalizedBridgeEvents(l1BridgeLog, tx, b.chainConfig, fromL1Height, toL1Height); err != nil {
return err
}
if err := bridge.L2ProcessFinalizedBridgeEvents(l2BridgeLog, tx, fromL2Height, toL2Height); err != nil {
return err
}
// a-ok
return nil
})
if err != nil {
// Try again on a subsequent interval
batchLog.Error("unable to index new bridge events", "err", err)
} else {
batchLog.Info("done indexing new bridge events", "latest_l1_block_number", toL1Height, "latest_l2_block_number", toL2Height)
b.LatestL1Header = latestEpoch.L1BlockHeader.RLPHeader.Header()
b.LatestL2Header = latestEpoch.L2BlockHeader.RLPHeader.Header()
}
}
}
}
package bridge
import (
"errors"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/indexer/config"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/processors/contracts"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
// L1ProcessInitiatedBridgeEvents will query the database for new bridge events that have been initiated between
// the specified block range. This covers every part of the multi-layered stack:
// 1. OptimismPortal
// 2. L1CrossDomainMessenger
// 3. L1StandardBridge
func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, chainConfig config.ChainConfig, fromHeight *big.Int, toHeight *big.Int) error {
// (1) OptimismPortal
optimismPortalTxDeposits, err := contracts.OptimismPortalTransactionDepositEvents(chainConfig.L1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
if err != nil {
return err
}
ethDeposits := []database.L1BridgeDeposit{}
portalDeposits := make(map[logKey]*contracts.OptimismPortalTransactionDepositEvent, len(optimismPortalTxDeposits))
transactionDeposits := make([]database.L1TransactionDeposit, len(optimismPortalTxDeposits))
for i := range optimismPortalTxDeposits {
depositTx := optimismPortalTxDeposits[i]
portalDeposits[logKey{depositTx.Event.BlockHash, depositTx.Event.LogIndex}] = &depositTx
transactionDeposits[i] = database.L1TransactionDeposit{
SourceHash: depositTx.DepositTx.SourceHash,
L2TransactionHash: types.NewTx(depositTx.DepositTx).Hash(),
InitiatedL1EventGUID: depositTx.Event.GUID,
GasLimit: depositTx.GasLimit,
Tx: depositTx.Tx,
}
// catch ETH transfers to the portal contract.
if len(depositTx.DepositTx.Data) == 0 && depositTx.DepositTx.Value.BitLen() > 0 {
ethDeposits = append(ethDeposits, database.L1BridgeDeposit{
TransactionSourceHash: depositTx.DepositTx.SourceHash,
BridgeTransfer: database.BridgeTransfer{Tx: transactionDeposits[i].Tx, TokenPair: database.ETHTokenPair},
})
}
}
if len(transactionDeposits) > 0 {
log.Info("detected transaction deposits", "size", len(transactionDeposits))
if err := db.BridgeTransactions.StoreL1TransactionDeposits(transactionDeposits); err != nil {
return err
}
if len(ethDeposits) > 0 {
log.Info("detected portal ETH transfers", "size", len(ethDeposits))
if err := db.BridgeTransfers.StoreL1BridgeDeposits(ethDeposits); err != nil {
return err
}
}
}
// (2) L1CrossDomainMessenger
crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l1", chainConfig.L1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
if err != nil {
return err
}
if len(crossDomainSentMessages) > len(transactionDeposits) {
return fmt.Errorf("missing transaction deposit for each cross-domain message. deposits: %d, messages: %d", len(transactionDeposits), len(crossDomainSentMessages))
}
sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages))
l1BridgeMessages := make([]database.L1BridgeMessage, len(crossDomainSentMessages))
for i := range crossDomainSentMessages {
sentMessage := crossDomainSentMessages[i]
sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage
// extract the deposit hash from the previous TransactionDepositedEvent
portalDeposit, ok := portalDeposits[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex - 1}]
if !ok {
return fmt.Errorf("missing expected preceding TransactionDeposit for SentMessage. tx_hash = %s", sentMessage.Event.TransactionHash)
}
l1BridgeMessages[i] = database.L1BridgeMessage{TransactionSourceHash: portalDeposit.DepositTx.SourceHash, BridgeMessage: sentMessage.BridgeMessage}
}
if len(l1BridgeMessages) > 0 {
log.Info("detected L1CrossDomainMessenger messages", "size", len(l1BridgeMessages))
if err := db.BridgeMessages.StoreL1BridgeMessages(l1BridgeMessages); err != nil {
return err
}
}
// (3) L1StandardBridge
initiatedBridges, err := contracts.StandardBridgeInitiatedEvents("l1", chainConfig.L1Contracts.L1StandardBridgeProxy, db, fromHeight, toHeight)
if err != nil {
return err
}
if len(initiatedBridges) > len(crossDomainSentMessages) {
return fmt.Errorf("missing cross-domain message for each initiated bridge event. messages: %d, bridges: %d", len(crossDomainSentMessages), len(initiatedBridges))
}
l1BridgeDeposits := make([]database.L1BridgeDeposit, len(initiatedBridges))
for i := range initiatedBridges {
initiatedBridge := initiatedBridges[i]
// extract the cross domain message hash & deposit source hash from the following events
portalDeposit, ok := portalDeposits[logKey{initiatedBridge.Event.BlockHash, initiatedBridge.Event.LogIndex + 1}]
if !ok {
return fmt.Errorf("missing expected following TransactionDeposit for BridgeInitiated. tx_hash = %s", initiatedBridge.Event.TransactionHash)
}
sentMessage, ok := sentMessages[logKey{initiatedBridge.Event.BlockHash, initiatedBridge.Event.LogIndex + 2}]
if !ok {
return fmt.Errorf("missing expected following SentMessage for BridgeInitiated. tx_hash = %s", initiatedBridge.Event.TransactionHash)
}
initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash
l1BridgeDeposits[i] = database.L1BridgeDeposit{
TransactionSourceHash: portalDeposit.DepositTx.SourceHash,
BridgeTransfer: initiatedBridge.BridgeTransfer,
}
}
if len(l1BridgeDeposits) > 0 {
log.Info("detected L1StandardBridge deposits", "size", len(l1BridgeDeposits))
if err := db.BridgeTransfers.StoreL1BridgeDeposits(l1BridgeDeposits); err != nil {
return err
}
}
return nil
}
// L1ProcessFinalizedBridgeEvent will query the database for all the finalization markers for all initiated
// bridge events. This covers every part of the multi-layered stack:
// 1. OptimismPortal (Bedrock prove & finalize steps)
// 2. L1CrossDomainMessenger (relayMessage marker)
// 3. L1StandardBridge (no-op, since this is simply a wrapper over the L1CrossDomainMEssenger)
func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, chainConfig config.ChainConfig, fromHeight *big.Int, toHeight *big.Int) error {
// (1) OptimismPortal (proven withdrawals)
provenWithdrawals, err := contracts.OptimismPortalWithdrawalProvenEvents(chainConfig.L1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
if err != nil {
return err
}
for i := range provenWithdrawals {
proven := provenWithdrawals[i]
withdrawal, err := db.BridgeTransactions.L2TransactionWithdrawal(proven.WithdrawalHash)
if err != nil {
return err
} else if withdrawal == nil {
log.Crit("missing indexed withdrawal on prove event!", "withdrawal_hash", proven.WithdrawalHash, "tx_hash", proven.Event.TransactionHash)
return errors.New("missing indexed withdrawal")
}
if err := db.BridgeTransactions.MarkL2TransactionWithdrawalProvenEvent(proven.WithdrawalHash, provenWithdrawals[i].Event.GUID); err != nil {
return err
}
}
if len(provenWithdrawals) > 0 {
log.Info("proven transaction withdrawals", "size", len(provenWithdrawals))
}
// (2) OptimismPortal (finalized withdrawals)
finalizedWithdrawals, err := contracts.OptimismPortalWithdrawalFinalizedEvents(chainConfig.L1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
if err != nil {
return err
}
for i := range finalizedWithdrawals {
finalized := finalizedWithdrawals[i]
withdrawal, err := db.BridgeTransactions.L2TransactionWithdrawal(finalized.WithdrawalHash)
if err != nil {
return err
} else if withdrawal == nil {
log.Crit("missing indexed withdrawal on finalization event!", "withdrawal_hash", finalized.WithdrawalHash, "tx_hash", finalized.Event.TransactionHash)
return errors.New("missing indexed withdrawal")
}
if err = db.BridgeTransactions.MarkL2TransactionWithdrawalFinalizedEvent(finalized.WithdrawalHash, finalized.Event.GUID, finalized.Success); err != nil {
return err
}
}
if len(finalizedWithdrawals) > 0 {
log.Info("finalized transaction withdrawals", "size", len(finalizedWithdrawals))
}
// (3) L1CrossDomainMessenger
crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l1", chainConfig.L1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
if err != nil {
return err
}
relayedMessages := make(map[logKey]*contracts.CrossDomainMessengerRelayedMessageEvent, len(crossDomainRelayedMessages))
for i := range crossDomainRelayedMessages {
relayed := crossDomainRelayedMessages[i]
relayedMessages[logKey{BlockHash: relayed.Event.BlockHash, LogIndex: relayed.Event.LogIndex}] = &relayed
message, err := db.BridgeMessages.L2BridgeMessage(relayed.MessageHash)
if err != nil {
return err
} else if message == nil {
log.Crit("missing indexed L2CrossDomainMessenger message", "message_hash", relayed.MessageHash, "tx_hash", relayed.Event.TransactionHash)
return fmt.Errorf("missing indexed L2CrossDomainMessager message")
}
if err := db.BridgeMessages.MarkRelayedL2BridgeMessage(relayed.MessageHash, relayed.Event.GUID); err != nil {
return err
}
}
if len(crossDomainRelayedMessages) > 0 {
log.Info("relayed L2CrossDomainMessenger messages", "size", len(crossDomainRelayedMessages))
}
// (4) L1StandardBridge
finalizedBridges, err := contracts.StandardBridgeFinalizedEvents("l1", chainConfig.L1Contracts.L1StandardBridgeProxy, db, fromHeight, toHeight)
if err != nil {
return err
}
if len(finalizedBridges) > len(crossDomainRelayedMessages) {
return fmt.Errorf("missing cross-domain message for each finalized bridge event. messages: %d, bridges: %d", len(crossDomainRelayedMessages), len(finalizedBridges))
}
for i := range finalizedBridges {
// Nothing actionable on the database. However, we can treat the relayed message
// as an invariant by ensuring we can query for a deposit by the same hash
finalizedBridge := finalizedBridges[i]
relayedMessage, ok := relayedMessages[logKey{finalizedBridge.Event.BlockHash, finalizedBridge.Event.LogIndex + 1}]
if !ok {
return fmt.Errorf("missing following RelayedMessage for BridgeFinalized event. tx_hash = %s", finalizedBridge.Event.TransactionHash)
}
// Since the message hash is computed from the relayed message, this ensures the deposit fields must match. For good measure,
// we may choose to make sure `withdrawal.BridgeTransfer` matches with the finalized bridge
withdrawal, err := db.BridgeTransfers.L2BridgeWithdrawalWithFilter(database.BridgeTransfer{CrossDomainMessageHash: &relayedMessage.MessageHash})
if err != nil {
return err
} else if withdrawal == nil {
log.Crit("missing L2StandardBridge withdrawal on L1 finalization", "tx_hash", finalizedBridge.Event.TransactionHash)
return errors.New("missing L2StandardBridge withdrawal on L1 finalization")
}
}
// a-ok!
return nil
}
// L1LatestBridgeEventHeader returns the latest header for which and on-chain event
// has been observed on L1 -- Both initiated L1 events and finalization markers on L2.
func L1LatestBridgeEventHeader(db *database.DB, chainConfig config.ChainConfig) (*types.Header, error) {
portalAbi, err := bindings.OptimismPortalMetaData.GetAbi()
if err != nil {
return nil, err
}
depositEventID := portalAbi.Events["TransactionDeposited"].ID
provenEventID := portalAbi.Events["WithdrawalProven"].ID
finalizedEventID := portalAbi.Events["WithdrawalFinalized"].ID
// (1) Initiated L1 Events
// Since all initaited bridge events eventually reach the OptimismPortal to
// conduct the deposit, we can simply look for the last deposited transaction
// event on L2.
var latestDepositHeader *types.Header
contractEventFilter := database.ContractEvent{ContractAddress: chainConfig.L1Contracts.OptimismPortalProxy, EventSignature: depositEventID}
depositEvent, err := db.ContractEvents.L1LatestContractEventWithFilter(contractEventFilter)
if err != nil {
return nil, err
}
if depositEvent != nil {
l1BlockHeader, err := db.Blocks.L1BlockHeader(depositEvent.BlockHash)
if err != nil {
return nil, err
}
if l1BlockHeader != nil {
latestDepositHeader = l1BlockHeader.RLPHeader.Header()
}
}
// (2) Finalization markers for L2
// Like initiated L1 events, all withdrawals must flow through the OptimismPortal
// contract. We must look for both proven and finalized withdrawal events.
var latestWithdrawHeader *types.Header
contractEventFilter.EventSignature = finalizedEventID
withdrawEvent, err := db.ContractEvents.L1LatestContractEventWithFilter(contractEventFilter)
if err != nil {
return nil, err
}
if withdrawEvent != nil {
// Check if a have a later detected proven event
contractEventFilter.EventSignature = provenEventID
provenEvent, err := db.ContractEvents.L1LatestContractEventWithFilter(contractEventFilter)
if err != nil {
return nil, err
}
if provenEvent != nil && provenEvent.Timestamp > withdrawEvent.Timestamp {
withdrawEvent = provenEvent
}
l1BlockHeader, err := db.Blocks.L1BlockHeader(withdrawEvent.BlockHash)
if err != nil {
return nil, err
}
latestWithdrawHeader = l1BlockHeader.RLPHeader.Header()
}
if latestDepositHeader == nil {
// If there has been no seen deposits yet, there could have been no seen withdrawals
if latestWithdrawHeader != nil {
return nil, errors.New("detected an indexed withdrawal without any deposits")
}
return nil, nil
} else if latestWithdrawHeader == nil {
return latestDepositHeader, nil
} else {
// both deposits & withdrawals have occurred
if latestDepositHeader.Time > latestWithdrawHeader.Time {
return latestDepositHeader, nil
} else {
return latestWithdrawHeader, nil
}
}
}
package bridge
import (
"errors"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/processors/contracts"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
// L2ProcessInitiatedBridgeEvents will query the database for new bridge events that have been initiated between
// the specified block range. This covers every part of the multi-layered stack:
// 1. OptimismPortal
// 2. L2CrossDomainMessenger
// 3. L2StandardBridge
func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, fromHeight *big.Int, toHeight *big.Int) error {
// (1) L2ToL1MessagePasser
l2ToL1MPMessagesPassed, err := contracts.L2ToL1MessagePasserMessagePassedEvents(predeploys.L2ToL1MessagePasserAddr, db, fromHeight, toHeight)
if err != nil {
return err
}
ethWithdrawals := []database.L2BridgeWithdrawal{}
messagesPassed := make(map[logKey]*contracts.L2ToL1MessagePasserMessagePassed, len(l2ToL1MPMessagesPassed))
transactionWithdrawals := make([]database.L2TransactionWithdrawal, len(l2ToL1MPMessagesPassed))
for i := range l2ToL1MPMessagesPassed {
messagePassed := l2ToL1MPMessagesPassed[i]
messagesPassed[logKey{messagePassed.Event.BlockHash, messagePassed.Event.LogIndex}] = &messagePassed
transactionWithdrawals[i] = database.L2TransactionWithdrawal{
WithdrawalHash: messagePassed.WithdrawalHash,
InitiatedL2EventGUID: messagePassed.Event.GUID,
Nonce: messagePassed.Nonce,
GasLimit: messagePassed.GasLimit,
Tx: messagePassed.Tx,
}
if len(messagePassed.Tx.Data) == 0 && messagePassed.Tx.Amount.Int.BitLen() > 0 {
ethWithdrawals = append(ethWithdrawals, database.L2BridgeWithdrawal{
TransactionWithdrawalHash: messagePassed.WithdrawalHash,
BridgeTransfer: database.BridgeTransfer{Tx: transactionWithdrawals[i].Tx, TokenPair: database.ETHTokenPair},
})
}
}
if len(messagesPassed) > 0 {
log.Info("detected transaction withdrawals", "size", len(transactionWithdrawals))
if err := db.BridgeTransactions.StoreL2TransactionWithdrawals(transactionWithdrawals); err != nil {
return err
}
if len(ethWithdrawals) > 0 {
log.Info("detected L2ToL1MessagePasser ETH transfers", "size", len(ethWithdrawals))
if err := db.BridgeTransfers.StoreL2BridgeWithdrawals(ethWithdrawals); err != nil {
return err
}
}
}
// (2) L2CrossDomainMessenger
crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l2", predeploys.L2CrossDomainMessengerAddr, db, fromHeight, toHeight)
if err != nil {
return err
}
if len(crossDomainSentMessages) > len(messagesPassed) {
return fmt.Errorf("missing L2ToL1MP withdrawal for each cross-domain message. withdrawals: %d, messages: %d", len(messagesPassed), len(crossDomainSentMessages))
}
sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages))
l2BridgeMessages := make([]database.L2BridgeMessage, len(crossDomainSentMessages))
for i := range crossDomainSentMessages {
sentMessage := crossDomainSentMessages[i]
sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage
// extract the withdrawal hash from the previous MessagePassed event
messagePassed, ok := messagesPassed[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex - 1}]
if !ok {
return fmt.Errorf("missing expected preceding MessagePassedEvent for SentMessage. tx_hash = %s", sentMessage.Event.TransactionHash)
}
l2BridgeMessages[i] = database.L2BridgeMessage{TransactionWithdrawalHash: messagePassed.WithdrawalHash, BridgeMessage: sentMessage.BridgeMessage}
}
if len(l2BridgeMessages) > 0 {
log.Info("detected L2CrossDomainMessenger messages", "size", len(l2BridgeMessages))
if err := db.BridgeMessages.StoreL2BridgeMessages(l2BridgeMessages); err != nil {
return err
}
}
// (3) L2StandardBridge
initiatedBridges, err := contracts.StandardBridgeInitiatedEvents("l2", predeploys.L2StandardBridgeAddr, db, fromHeight, toHeight)
if err != nil {
return err
}
if len(initiatedBridges) > len(crossDomainSentMessages) {
return fmt.Errorf("missing cross-domain message for each initiated bridge event. messages: %d, bridges: %d", len(crossDomainSentMessages), len(initiatedBridges))
}
l2BridgeWithdrawals := make([]database.L2BridgeWithdrawal, len(initiatedBridges))
for i := range initiatedBridges {
initiatedBridge := initiatedBridges[i]
// extract the cross domain message hash & deposit source hash from the following events
messagePassed, ok := messagesPassed[logKey{initiatedBridge.Event.BlockHash, initiatedBridge.Event.LogIndex + 1}]
if !ok {
return fmt.Errorf("missing expected following MessagePassed for BridgeInitiated. tx_hash = %s", initiatedBridge.Event.TransactionHash)
}
sentMessage, ok := sentMessages[logKey{initiatedBridge.Event.BlockHash, initiatedBridge.Event.LogIndex + 2}]
if !ok {
return fmt.Errorf("missing expected following SentMessage for BridgeInitiated. tx_hash = %s", initiatedBridge.Event.TransactionHash)
}
initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash
l2BridgeWithdrawals[i] = database.L2BridgeWithdrawal{TransactionWithdrawalHash: messagePassed.WithdrawalHash, BridgeTransfer: initiatedBridge.BridgeTransfer}
}
if len(l2BridgeWithdrawals) > 0 {
log.Info("detected L2StandardBridge withdrawals", "size", len(l2BridgeWithdrawals))
if err := db.BridgeTransfers.StoreL2BridgeWithdrawals(l2BridgeWithdrawals); err != nil {
return err
}
}
// a-ok!
return nil
}
// L2ProcessFinalizedBridgeEvent will query the database for all the finalization markers for all initiated
// bridge events. This covers every part of the multi-layered stack:
// 1. L2CrossDomainMessenger (relayMessage marker)
// 2. L2StandardBridge (no-op, since this is simply a wrapper over the L2CrossDomainMEssenger)
//
// NOTE: Unlike L1, there's no L2ToL1MessagePasser stage since transaction deposits are apart of the block derivation process.
func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, fromHeight *big.Int, toHeight *big.Int) error {
// (1) L2CrossDomainMessenger relayedMessage
crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l2", predeploys.L2CrossDomainMessengerAddr, db, fromHeight, toHeight)
if err != nil {
return err
}
relayedMessages := make(map[logKey]*contracts.CrossDomainMessengerRelayedMessageEvent, len(crossDomainRelayedMessages))
for i := range crossDomainRelayedMessages {
relayed := crossDomainRelayedMessages[i]
relayedMessages[logKey{BlockHash: relayed.Event.BlockHash, LogIndex: relayed.Event.LogIndex}] = &relayed
message, err := db.BridgeMessages.L1BridgeMessage(relayed.MessageHash)
if err != nil {
return err
} else if message == nil {
log.Crit("missing indexed L1CrossDomainMessenger message", "message_hash", relayed.MessageHash, "tx_hash", relayed.Event.TransactionHash)
return fmt.Errorf("missing indexed L1CrossDomainMessager message")
}
if err := db.BridgeMessages.MarkRelayedL1BridgeMessage(relayed.MessageHash, relayed.Event.GUID); err != nil {
return err
}
}
if len(crossDomainRelayedMessages) > 0 {
log.Info("relayed L1CrossDomainMessenger messages", "size", len(crossDomainRelayedMessages))
}
// (2) L2StandardBridge BridgeFinalized
finalizedBridges, err := contracts.StandardBridgeFinalizedEvents("l2", predeploys.L2StandardBridgeAddr, db, fromHeight, toHeight)
if err != nil {
return err
}
if len(finalizedBridges) > len(crossDomainRelayedMessages) {
return fmt.Errorf("missing cross-domain message for each finalized bridge event. messages: %d, bridges: %d", len(crossDomainRelayedMessages), len(finalizedBridges))
}
for i := range finalizedBridges {
// Nothing actionable on the database. However, we can treat the relayed message
// as an invariant by ensuring we can query for a deposit by the same hash
finalizedBridge := finalizedBridges[i]
relayedMessage, ok := relayedMessages[logKey{finalizedBridge.Event.BlockHash, finalizedBridge.Event.LogIndex + 1}]
if !ok {
return fmt.Errorf("missing following RelayedMessage for BridgeFinalized event. tx_hash = %s", finalizedBridge.Event.TransactionHash)
}
// Since the message hash is computed from the relayed message, this ensures the withdrawal fields must match. For good measure,
// we may choose to make sure `deposit.BridgeTransfer` matches with the finalized bridge
deposit, err := db.BridgeTransfers.L1BridgeDepositWithFilter(database.BridgeTransfer{CrossDomainMessageHash: &relayedMessage.MessageHash})
if err != nil {
return err
} else if deposit == nil {
log.Crit("missing L1StandardBridge deposit on L2 finalization", "tx_hash", finalizedBridge.Event.TransactionHash)
return errors.New("missing L1StandardBridge deposit on L2 finalization")
}
}
// a-ok!
return nil
}
// L2LatestBridgeEventHeader returns the latest header for which and on-chain event
// has been observed on L2 -- Both initiated L2 events and finalization markers from L1.
func L2LatestBridgeEventHeader(db *database.DB) (*types.Header, error) {
l2ToL1MessagePasserAbi, err := bindings.L2ToL1MessagePasserMetaData.GetAbi()
if err != nil {
return nil, err
}
crossDomainMessengerAbi, err := bindings.CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return nil, err
}
messagePassedID := l2ToL1MessagePasserAbi.Events["MessagePassed"].ID
relayedEventID := crossDomainMessengerAbi.Events["RelayedMessage"].ID
// (1) Initiated L2 Events
// Since all initiated bridge events eventually reach the L2ToL1MessagePasser to
// initiate the withdrawal, we can simply look for the last message passed from
// this cont
var latestWithdrawHeader *types.Header
contractEventFilter := database.ContractEvent{ContractAddress: predeploys.L2ToL1MessagePasserAddr, EventSignature: messagePassedID}
withdrawEvent, err := db.ContractEvents.L2LatestContractEventWithFilter(contractEventFilter)
if err != nil {
return nil, err
}
if withdrawEvent != nil {
l2BlockHeader, err := db.Blocks.L2BlockHeader(withdrawEvent.BlockHash)
if err != nil {
return nil, err
}
if l2BlockHeader != nil {
latestWithdrawHeader = l2BlockHeader.RLPHeader.Header()
}
}
// (2) Finalization markers for L1
// Since deposited transactions from L1 are apart of the block derivation process,
// there are no native finalization markers for OptimismPortal#TransactionDeposited.
// The lowest layer to check for here is the CrossDomainMessenger#RelayedMessage event.
// This also converts the StandardBridge which simply is an extension of the messenger.
var latestRelayedMessageHeader *types.Header
contractEventFilter = database.ContractEvent{ContractAddress: predeploys.L2CrossDomainMessengerAddr, EventSignature: relayedEventID}
relayedEvent, err := db.ContractEvents.L2LatestContractEventWithFilter(contractEventFilter)
if err != nil {
return nil, err
}
if relayedEvent != nil {
l2BlockHeader, err := db.Blocks.L2BlockHeader(relayedEvent.BlockHash)
if err != nil {
return nil, err
}
if l2BlockHeader != nil {
latestRelayedMessageHeader = l2BlockHeader.RLPHeader.Header()
}
}
// No causaal relationship between withdraw and relayed messages
if latestWithdrawHeader == nil || latestRelayedMessageHeader == nil {
return nil, nil
} else {
if latestWithdrawHeader.Time > latestRelayedMessageHeader.Time {
return latestWithdrawHeader, nil
} else {
return latestRelayedMessageHeader, nil
}
}
}
package bridge
import "github.com/ethereum/go-ethereum/common"
type logKey struct {
BlockHash common.Hash
LogIndex uint64
}
package contracts
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
)
var (
// Standard ABI types copied from golang ABI tests
uint256Type, _ = abi.NewType("uint256", "", nil)
bytesType, _ = abi.NewType("bytes", "", nil)
addressType, _ = abi.NewType("address", "", nil)
legacyCrossDomainMessengerRelayMessageMethod = abi.NewMethod(
"relayMessage",
"relayMessage",
abi.Function,
"external", // mutability
false, // isConst
true, // payable
abi.Arguments{ // inputs
{Name: "sender", Type: addressType},
{Name: "target", Type: addressType},
{Name: "data", Type: bytesType},
{Name: "nonce", Type: uint256Type},
},
abi.Arguments{}, // outputs
)
)
type CrossDomainMessengerSentMessageEvent struct {
Event *database.ContractEvent
BridgeMessage database.BridgeMessage
}
type CrossDomainMessengerRelayedMessageEvent struct {
Event *database.ContractEvent
MessageHash common.Hash
}
func CrossDomainMessengerSentMessageEvents(chainSelector string, contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]CrossDomainMessengerSentMessageEvent, error) {
crossDomainMessengerAbi, err := bindings.CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return nil, err
}
sentMessageEventAbi := crossDomainMessengerAbi.Events["SentMessage"]
contractEventFilter := database.ContractEvent{ContractAddress: contractAddress, EventSignature: sentMessageEventAbi.ID}
sentMessageEvents, err := db.ContractEvents.ContractEventsWithFilter(contractEventFilter, chainSelector, fromHeight, toHeight)
if err != nil {
return nil, err
}
if len(sentMessageEvents) == 0 {
// prevent the following db queries if we dont need them
return nil, nil
}
sentMessageExtensionEventAbi := crossDomainMessengerAbi.Events["SentMessageExtension1"]
contractEventFilter = database.ContractEvent{ContractAddress: contractAddress, EventSignature: sentMessageExtensionEventAbi.ID}
sentMessageExtensionEvents, err := db.ContractEvents.ContractEventsWithFilter(contractEventFilter, chainSelector, fromHeight, toHeight)
if err != nil {
return nil, err
}
if len(sentMessageEvents) != len(sentMessageExtensionEvents) {
return nil, fmt.Errorf("mismatch in SentMessage events. %d sent messages & %d sent message extensions", len(sentMessageEvents), len(sentMessageExtensionEvents))
}
crossDomainSentMessages := make([]CrossDomainMessengerSentMessageEvent, len(sentMessageEvents))
for i := range sentMessageEvents {
sentMessage := bindings.CrossDomainMessengerSentMessage{Raw: *sentMessageEvents[i].RLPLog}
err = UnpackLog(&sentMessage, sentMessageEvents[i].RLPLog, sentMessageEventAbi.Name, crossDomainMessengerAbi)
if err != nil {
return nil, err
}
sentMessageExtension := bindings.CrossDomainMessengerSentMessageExtension1{Raw: *sentMessageExtensionEvents[i].RLPLog}
err = UnpackLog(&sentMessageExtension, sentMessageExtensionEvents[i].RLPLog, sentMessageExtensionEventAbi.Name, crossDomainMessengerAbi)
if err != nil {
return nil, err
}
messageHash, err := CrossDomainMessageHash(crossDomainMessengerAbi, &sentMessage, sentMessageExtension.Value)
if err != nil {
return nil, err
}
crossDomainSentMessages[i] = CrossDomainMessengerSentMessageEvent{
Event: &sentMessageEvents[i],
BridgeMessage: database.BridgeMessage{
MessageHash: messageHash,
Nonce: database.U256{Int: sentMessage.MessageNonce},
SentMessageEventGUID: sentMessageEvents[i].GUID,
GasLimit: database.U256{Int: sentMessage.GasLimit},
Tx: database.Transaction{
FromAddress: sentMessage.Sender,
ToAddress: sentMessage.Target,
Amount: database.U256{Int: sentMessageExtension.Value},
Data: sentMessage.Message,
Timestamp: sentMessageEvents[i].Timestamp,
},
},
}
}
return crossDomainSentMessages, nil
}
func CrossDomainMessengerRelayedMessageEvents(chainSelector string, contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]CrossDomainMessengerRelayedMessageEvent, error) {
crossDomainMessengerAbi, err := bindings.CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return nil, err
}
relayedMessageEventAbi := crossDomainMessengerAbi.Events["RelayedMessage"]
contractEventFilter := database.ContractEvent{ContractAddress: contractAddress, EventSignature: relayedMessageEventAbi.ID}
relayedMessageEvents, err := db.ContractEvents.ContractEventsWithFilter(contractEventFilter, chainSelector, fromHeight, toHeight)
if err != nil {
return nil, err
}
crossDomainRelayedMessages := make([]CrossDomainMessengerRelayedMessageEvent, len(relayedMessageEvents))
for i := range relayedMessageEvents {
relayedMessage := bindings.CrossDomainMessengerRelayedMessage{Raw: *relayedMessageEvents[i].RLPLog}
err = UnpackLog(&relayedMessage, relayedMessageEvents[i].RLPLog, relayedMessageEventAbi.Name, crossDomainMessengerAbi)
if err != nil {
return nil, err
}
crossDomainRelayedMessages[i] = CrossDomainMessengerRelayedMessageEvent{
Event: &relayedMessageEvents[i],
MessageHash: relayedMessage.MsgHash,
}
}
return crossDomainRelayedMessages, nil
}
// Replica of `Hashing.sol#hashCrossDomainMessage` solidity implementation
func CrossDomainMessageHash(abi *abi.ABI, sentMsg *bindings.CrossDomainMessengerSentMessage, value *big.Int) (common.Hash, error) {
version, _ := DecodeVersionedNonce(sentMsg.MessageNonce)
switch version {
case 0:
// Legacy Message
inputBytes, err := legacyCrossDomainMessengerRelayMessageMethod.Inputs.Pack(sentMsg.Sender, sentMsg.Target, sentMsg.Message, sentMsg.MessageNonce)
if err != nil {
return common.Hash{}, err
}
msgBytes := append(legacyCrossDomainMessengerRelayMessageMethod.ID, inputBytes...)
return crypto.Keccak256Hash(msgBytes), nil
case 1:
// Current Message
msgBytes, err := abi.Pack("relayMessage", sentMsg.MessageNonce, sentMsg.Sender, sentMsg.Target, value, sentMsg.GasLimit, sentMsg.Message)
if err != nil {
return common.Hash{}, err
}
return crypto.Keccak256Hash(msgBytes), nil
}
return common.Hash{}, fmt.Errorf("unsupported cross domain messenger version: %d", version)
}
package contracts
import (
"math/big"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum/common"
)
type L2ToL1MessagePasserMessagePassed struct {
Event *database.ContractEvent
WithdrawalHash common.Hash
GasLimit database.U256
Nonce database.U256
Tx database.Transaction
}
func L2ToL1MessagePasserMessagePassedEvents(contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]L2ToL1MessagePasserMessagePassed, error) {
l2ToL1MessagePasserAbi, err := bindings.L2ToL1MessagePasserMetaData.GetAbi()
if err != nil {
return nil, err
}
messagePassedAbi := l2ToL1MessagePasserAbi.Events["MessagePassed"]
contractEventFilter := database.ContractEvent{ContractAddress: contractAddress, EventSignature: messagePassedAbi.ID}
messagePassedEvents, err := db.ContractEvents.L2ContractEventsWithFilter(contractEventFilter, fromHeight, toHeight)
if err != nil {
return nil, err
}
messagesPassed := make([]L2ToL1MessagePasserMessagePassed, len(messagePassedEvents))
for i := range messagePassedEvents {
messagePassed := bindings.L2ToL1MessagePasserMessagePassed{Raw: *messagePassedEvents[i].RLPLog}
err := UnpackLog(&messagePassed, messagePassedEvents[i].RLPLog, messagePassedAbi.Name, l2ToL1MessagePasserAbi)
if err != nil {
return nil, err
}
messagesPassed[i] = L2ToL1MessagePasserMessagePassed{
Event: &messagePassedEvents[i].ContractEvent,
WithdrawalHash: messagePassed.WithdrawalHash,
Nonce: database.U256{Int: messagePassed.Nonce},
GasLimit: database.U256{Int: messagePassed.GasLimit},
Tx: database.Transaction{
FromAddress: messagePassed.Sender,
ToAddress: messagePassed.Target,
Amount: database.U256{Int: messagePassed.Value},
Data: messagePassed.Data,
Timestamp: messagePassedEvents[i].Timestamp,
},
}
}
return messagesPassed, nil
}
package contracts
import (
"errors"
"math/big"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
type OptimismPortalTransactionDepositEvent struct {
Event *database.ContractEvent
DepositTx *types.DepositTx
Tx database.Transaction
GasLimit database.U256
}
type OptimismPortalWithdrawalProvenEvent struct {
*bindings.OptimismPortalWithdrawalProven
Event *database.ContractEvent
}
type OptimismPortalWithdrawalFinalizedEvent struct {
*bindings.OptimismPortalWithdrawalFinalized
Event *database.ContractEvent
}
type OptimismPortalProvenWithdrawal struct {
OutputRoot [32]byte
Timestamp *big.Int
L2OutputIndex *big.Int
}
func OptimismPortalTransactionDepositEvents(contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]OptimismPortalTransactionDepositEvent, error) {
optimismPortalAbi, err := bindings.OptimismPortalMetaData.GetAbi()
if err != nil {
return nil, err
}
transactionDepositedEventAbi := optimismPortalAbi.Events["TransactionDeposited"]
if transactionDepositedEventAbi.ID != derive.DepositEventABIHash {
return nil, errors.New("op-node DepositEventABIHash & optimism portal TransactionDeposited ID mismatch")
}
contractEventFilter := database.ContractEvent{ContractAddress: contractAddress, EventSignature: transactionDepositedEventAbi.ID}
transactionDepositEvents, err := db.ContractEvents.L1ContractEventsWithFilter(contractEventFilter, fromHeight, toHeight)
if err != nil {
return nil, err
}
optimismPortalTxDeposits := make([]OptimismPortalTransactionDepositEvent, len(transactionDepositEvents))
for i := range transactionDepositEvents {
depositTx, err := derive.UnmarshalDepositLogEvent(transactionDepositEvents[i].RLPLog)
if err != nil {
return nil, err
}
txDeposit := bindings.OptimismPortalTransactionDeposited{Raw: *transactionDepositEvents[i].RLPLog}
err = UnpackLog(&txDeposit, transactionDepositEvents[i].RLPLog, transactionDepositedEventAbi.Name, optimismPortalAbi)
if err != nil {
return nil, err
}
optimismPortalTxDeposits[i] = OptimismPortalTransactionDepositEvent{
Event: &transactionDepositEvents[i].ContractEvent,
DepositTx: depositTx,
GasLimit: database.U256{Int: new(big.Int).SetUint64(depositTx.Gas)},
Tx: database.Transaction{
FromAddress: txDeposit.From,
ToAddress: txDeposit.To,
Amount: database.U256{Int: depositTx.Value},
Data: depositTx.Data,
Timestamp: transactionDepositEvents[i].Timestamp,
},
}
}
return optimismPortalTxDeposits, nil
}
func OptimismPortalWithdrawalProvenEvents(contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]OptimismPortalWithdrawalProvenEvent, error) {
optimismPortalAbi, err := bindings.OptimismPortalMetaData.GetAbi()
if err != nil {
return nil, err
}
withdrawalProvenEventAbi := optimismPortalAbi.Events["WithdrawalProven"]
contractEventFilter := database.ContractEvent{ContractAddress: contractAddress, EventSignature: withdrawalProvenEventAbi.ID}
withdrawalProvenEvents, err := db.ContractEvents.L1ContractEventsWithFilter(contractEventFilter, fromHeight, toHeight)
if err != nil {
return nil, err
}
provenWithdrawals := make([]OptimismPortalWithdrawalProvenEvent, len(withdrawalProvenEvents))
for i := range withdrawalProvenEvents {
withdrawalProven := bindings.OptimismPortalWithdrawalProven{Raw: *withdrawalProvenEvents[i].RLPLog}
err := UnpackLog(&withdrawalProven, withdrawalProvenEvents[i].RLPLog, withdrawalProvenEventAbi.Name, optimismPortalAbi)
if err != nil {
return nil, err
}
provenWithdrawals[i] = OptimismPortalWithdrawalProvenEvent{
OptimismPortalWithdrawalProven: &withdrawalProven,
Event: &withdrawalProvenEvents[i].ContractEvent,
}
}
return provenWithdrawals, nil
}
func OptimismPortalWithdrawalFinalizedEvents(contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]OptimismPortalWithdrawalFinalizedEvent, error) {
optimismPortalAbi, err := bindings.OptimismPortalMetaData.GetAbi()
if err != nil {
return nil, err
}
withdrawalFinalizedEventAbi := optimismPortalAbi.Events["WithdrawalFinalized"]
contractEventFilter := database.ContractEvent{ContractAddress: contractAddress, EventSignature: withdrawalFinalizedEventAbi.ID}
withdrawalFinalizedEvents, err := db.ContractEvents.L1ContractEventsWithFilter(contractEventFilter, fromHeight, toHeight)
if err != nil {
return nil, err
}
finalizedWithdrawals := make([]OptimismPortalWithdrawalFinalizedEvent, len(withdrawalFinalizedEvents))
for i := range withdrawalFinalizedEvents {
withdrawalFinalized := bindings.OptimismPortalWithdrawalFinalized{Raw: *withdrawalFinalizedEvents[i].RLPLog}
err := UnpackLog(&withdrawalFinalized, withdrawalFinalizedEvents[i].RLPLog, withdrawalFinalizedEventAbi.Name, optimismPortalAbi)
if err != nil {
return nil, err
}
finalizedWithdrawals[i] = OptimismPortalWithdrawalFinalizedEvent{
OptimismPortalWithdrawalFinalized: &withdrawalFinalized,
Event: &withdrawalFinalizedEvents[i].ContractEvent,
}
}
return finalizedWithdrawals, nil
}
package contracts
import (
"math/big"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum/go-ethereum/common"
)
type StandardBridgeInitiatedEvent struct {
Event *database.ContractEvent
BridgeTransfer database.BridgeTransfer
}
type StandardBridgeFinalizedEvent struct {
Event *database.ContractEvent
BridgeTransfer database.BridgeTransfer
}
// StandardBridgeInitiatedEvents extracts all initiated bridge events from the contracts that follow the StandardBridge ABI. The
// correlated CrossDomainMessenger nonce is also parsed from the associated messenger events.
func StandardBridgeInitiatedEvents(chainSelector string, contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]StandardBridgeInitiatedEvent, error) {
ethBridgeInitiatedEvents, err := _standardBridgeInitiatedEvents[bindings.StandardBridgeETHBridgeInitiated](contractAddress, chainSelector, db, fromHeight, toHeight)
if err != nil {
return nil, err
}
erc20BridgeInitiatedEvents, err := _standardBridgeInitiatedEvents[bindings.StandardBridgeERC20BridgeInitiated](contractAddress, chainSelector, db, fromHeight, toHeight)
if err != nil {
return nil, err
}
return append(ethBridgeInitiatedEvents, erc20BridgeInitiatedEvents...), nil
}
// StandardBridgeFinalizedEvents extracts all finalization bridge events from the contracts that follow the StandardBridge ABI. The
// correlated CrossDomainMessenger nonce is also parsed by looking at the parameters of the corresponding relayMessage transaction data.
func StandardBridgeFinalizedEvents(chainSelector string, contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]StandardBridgeFinalizedEvent, error) {
ethBridgeFinalizedEvents, err := _standardBridgeFinalizedEvents[bindings.StandardBridgeETHBridgeFinalized](contractAddress, chainSelector, db, fromHeight, toHeight)
if err != nil {
return nil, err
}
erc20BridgeFinalizedEvents, err := _standardBridgeFinalizedEvents[bindings.StandardBridgeERC20BridgeFinalized](contractAddress, chainSelector, db, fromHeight, toHeight)
if err != nil {
return nil, err
}
return append(ethBridgeFinalizedEvents, erc20BridgeFinalizedEvents...), nil
}
// parse out eth or erc20 bridge initiated events
func _standardBridgeInitiatedEvents[BridgeEventType bindings.StandardBridgeETHBridgeInitiated | bindings.StandardBridgeERC20BridgeInitiated](
contractAddress common.Address, chainSelector string, db *database.DB, fromHeight, toHeight *big.Int,
) ([]StandardBridgeInitiatedEvent, error) {
standardBridgeAbi, err := bindings.StandardBridgeMetaData.GetAbi()
if err != nil {
return nil, err
}
var eventType BridgeEventType
var eventName string
switch any(eventType).(type) {
case bindings.StandardBridgeETHBridgeInitiated:
eventName = "ETHBridgeInitiated"
case bindings.StandardBridgeERC20BridgeInitiated:
eventName = "ERC20BridgeInitiated"
default:
panic("should not be here")
}
initiatedBridgeEventAbi := standardBridgeAbi.Events[eventName]
contractEventFilter := database.ContractEvent{ContractAddress: contractAddress, EventSignature: initiatedBridgeEventAbi.ID}
initiatedBridgeEvents, err := db.ContractEvents.ContractEventsWithFilter(contractEventFilter, chainSelector, fromHeight, toHeight)
if err != nil {
return nil, err
}
standardBridgeInitiatedEvents := make([]StandardBridgeInitiatedEvent, len(initiatedBridgeEvents))
for i := range initiatedBridgeEvents {
erc20Bridge := bindings.StandardBridgeERC20BridgeInitiated{Raw: *initiatedBridgeEvents[i].RLPLog}
err := UnpackLog(&erc20Bridge, initiatedBridgeEvents[i].RLPLog, eventName, standardBridgeAbi)
if err != nil {
return nil, err
}
// If an ETH bridge, lets fill in the needed fields
switch any(eventType).(type) {
case bindings.StandardBridgeETHBridgeInitiated:
erc20Bridge.LocalToken = predeploys.LegacyERC20ETHAddr
erc20Bridge.RemoteToken = predeploys.LegacyERC20ETHAddr
}
standardBridgeInitiatedEvents[i] = StandardBridgeInitiatedEvent{
Event: &initiatedBridgeEvents[i],
BridgeTransfer: database.BridgeTransfer{
TokenPair: database.TokenPair{LocalTokenAddress: erc20Bridge.LocalToken, RemoteTokenAddress: erc20Bridge.RemoteToken},
Tx: database.Transaction{
FromAddress: erc20Bridge.From,
ToAddress: erc20Bridge.To,
Amount: database.U256{Int: erc20Bridge.Amount},
Data: erc20Bridge.ExtraData,
Timestamp: initiatedBridgeEvents[i].Timestamp,
},
},
}
}
return standardBridgeInitiatedEvents, nil
}
// parse out eth or erc20 bridge finalization events
func _standardBridgeFinalizedEvents[BridgeEventType bindings.StandardBridgeETHBridgeFinalized | bindings.StandardBridgeERC20BridgeFinalized](
contractAddress common.Address, chainSelector string, db *database.DB, fromHeight, toHeight *big.Int,
) ([]StandardBridgeFinalizedEvent, error) {
standardBridgeAbi, err := bindings.StandardBridgeMetaData.GetAbi()
if err != nil {
return nil, err
}
var eventType BridgeEventType
var eventName string
switch any(eventType).(type) {
case bindings.StandardBridgeETHBridgeFinalized:
eventName = "ETHBridgeFinalized"
case bindings.StandardBridgeERC20BridgeFinalized:
eventName = "ERC20BridgeFinalized"
default:
panic("should not be here")
}
bridgeFinalizedEventAbi := standardBridgeAbi.Events[eventName]
contractEventFilter := database.ContractEvent{ContractAddress: contractAddress, EventSignature: bridgeFinalizedEventAbi.ID}
bridgeFinalizedEvents, err := db.ContractEvents.ContractEventsWithFilter(contractEventFilter, chainSelector, fromHeight, toHeight)
if err != nil {
return nil, err
}
standardBridgeFinalizedEvents := make([]StandardBridgeFinalizedEvent, len(bridgeFinalizedEvents))
for i := range bridgeFinalizedEvents {
erc20Bridge := bindings.StandardBridgeERC20BridgeFinalized{Raw: *bridgeFinalizedEvents[i].RLPLog}
err := UnpackLog(&erc20Bridge, bridgeFinalizedEvents[i].RLPLog, eventName, standardBridgeAbi)
if err != nil {
return nil, err
}
// If an ETH bridge, lets fill in the needed fields
switch any(eventType).(type) {
case bindings.StandardBridgeETHBridgeFinalized:
erc20Bridge.LocalToken = predeploys.LegacyERC20ETHAddr
erc20Bridge.RemoteToken = predeploys.LegacyERC20ETHAddr
}
standardBridgeFinalizedEvents[i] = StandardBridgeFinalizedEvent{
Event: &bridgeFinalizedEvents[i],
BridgeTransfer: database.BridgeTransfer{
TokenPair: database.TokenPair{LocalTokenAddress: erc20Bridge.LocalToken, RemoteTokenAddress: erc20Bridge.RemoteToken},
Tx: database.Transaction{
FromAddress: erc20Bridge.From,
ToAddress: erc20Bridge.To,
Amount: database.U256{Int: erc20Bridge.Amount},
Data: erc20Bridge.ExtraData,
Timestamp: bridgeFinalizedEvents[i].Timestamp,
},
},
}
}
return standardBridgeFinalizedEvents, nil
}
package processor package contracts
import ( import (
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"math/big"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
) )
type ProcessedContractEventLogIndexKey struct { // DecodeVersionNonce is an re-implementation of Encoding.sol#decodeVersionedNonce.
blockHash common.Hash // If the nonce is greater than 32 bytes (solidity uint256), bytes [32:] are ignored
index uint func DecodeVersionedNonce(nonce *big.Int) (uint16, *big.Int) {
} nonceBytes := nonce.Bytes()
nonceByteLen := len(nonceBytes)
type ProcessedContractEvents struct { if nonceByteLen < 30 {
events []*database.ContractEvent // version is 0x0000
eventsBySignature map[common.Hash][]*database.ContractEvent return 0, nonce
eventByLogIndex map[ProcessedContractEventLogIndexKey]*database.ContractEvent } else if nonceByteLen == 31 {
} // version is 0x00[01..ff]
return uint16(nonceBytes[0]), new(big.Int).SetBytes(nonceBytes[1:])
func NewProcessedContractEvents() *ProcessedContractEvents { } else {
return &ProcessedContractEvents{ // fully specified
events: []*database.ContractEvent{}, version := binary.BigEndian.Uint16(nonceBytes[:2])
eventsBySignature: make(map[common.Hash][]*database.ContractEvent), return version, new(big.Int).SetBytes(nonceBytes[2:])
eventByLogIndex: make(map[ProcessedContractEventLogIndexKey]*database.ContractEvent),
} }
} }
func (p *ProcessedContractEvents) AddLog(log *types.Log, time uint64) *database.ContractEvent {
event := database.ContractEventFromLog(log, time)
emptyHash := common.Hash{}
p.events = append(p.events, &event)
p.eventByLogIndex[ProcessedContractEventLogIndexKey{log.BlockHash, log.Index}] = &event
if event.EventSignature != emptyHash {
p.eventsBySignature[event.EventSignature] = append(p.eventsBySignature[event.EventSignature], &event)
}
return &event
}
func UnpackLog(out interface{}, log *types.Log, name string, contractAbi *abi.ABI) error { func UnpackLog(out interface{}, log *types.Log, name string, contractAbi *abi.ABI) error {
eventAbi, ok := contractAbi.Events[name] eventAbi, ok := contractAbi.Events[name]
if !ok { if !ok {
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"sync"
"github.com/ethereum-optimism/optimism/op-batcher/metrics" "github.com/ethereum-optimism/optimism/op-batcher/metrics"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
...@@ -21,8 +22,9 @@ var ErrReorg = errors.New("block does not extend existing chain") ...@@ -21,8 +22,9 @@ var ErrReorg = errors.New("block does not extend existing chain")
// For simplicity, it only creates a single pending channel at a time & waits for // For simplicity, it only creates a single pending channel at a time & waits for
// the channel to either successfully be submitted or timeout before creating a new // the channel to either successfully be submitted or timeout before creating a new
// channel. // channel.
// Functions on channelManager are not safe for concurrent access. // Public functions on channelManager are safe for concurrent access.
type channelManager struct { type channelManager struct {
mu sync.Mutex
log log.Logger log log.Logger
metr metrics.Metricer metr metrics.Metricer
cfg ChannelConfig cfg ChannelConfig
...@@ -55,6 +57,8 @@ func NewChannelManager(log log.Logger, metr metrics.Metricer, cfg ChannelConfig) ...@@ -55,6 +57,8 @@ func NewChannelManager(log log.Logger, metr metrics.Metricer, cfg ChannelConfig)
// Clear clears the entire state of the channel manager. // Clear clears the entire state of the channel manager.
// It is intended to be used after an L2 reorg. // It is intended to be used after an L2 reorg.
func (s *channelManager) Clear() { func (s *channelManager) Clear() {
s.mu.Lock()
defer s.mu.Unlock()
s.log.Trace("clearing channel manager state") s.log.Trace("clearing channel manager state")
s.blocks = s.blocks[:0] s.blocks = s.blocks[:0]
s.tip = common.Hash{} s.tip = common.Hash{}
...@@ -67,6 +71,8 @@ func (s *channelManager) Clear() { ...@@ -67,6 +71,8 @@ func (s *channelManager) Clear() {
// TxFailed records a transaction as failed. It will attempt to resubmit the data // TxFailed records a transaction as failed. It will attempt to resubmit the data
// in the failed transaction. // in the failed transaction.
func (s *channelManager) TxFailed(id txID) { func (s *channelManager) TxFailed(id txID) {
s.mu.Lock()
defer s.mu.Unlock()
if channel, ok := s.txChannels[id]; ok { if channel, ok := s.txChannels[id]; ok {
delete(s.txChannels, id) delete(s.txChannels, id)
channel.TxFailed(id) channel.TxFailed(id)
...@@ -84,6 +90,8 @@ func (s *channelManager) TxFailed(id txID) { ...@@ -84,6 +90,8 @@ func (s *channelManager) TxFailed(id txID) {
// resubmitted. // resubmitted.
// This function may reset the pending channel if the pending channel has timed out. // This function may reset the pending channel if the pending channel has timed out.
func (s *channelManager) TxConfirmed(id txID, inclusionBlock eth.BlockID) { func (s *channelManager) TxConfirmed(id txID, inclusionBlock eth.BlockID) {
s.mu.Lock()
defer s.mu.Unlock()
if channel, ok := s.txChannels[id]; ok { if channel, ok := s.txChannels[id]; ok {
delete(s.txChannels, id) delete(s.txChannels, id)
done, blocks := channel.TxConfirmed(id, inclusionBlock) done, blocks := channel.TxConfirmed(id, inclusionBlock)
...@@ -134,6 +142,8 @@ func (s *channelManager) nextTxData(channel *channel) (txData, error) { ...@@ -134,6 +142,8 @@ func (s *channelManager) nextTxData(channel *channel) (txData, error) {
// full, it only returns the remaining frames of this channel until it got // full, it only returns the remaining frames of this channel until it got
// successfully fully sent to L1. It returns io.EOF if there's no pending frame. // successfully fully sent to L1. It returns io.EOF if there's no pending frame.
func (s *channelManager) TxData(l1Head eth.BlockID) (txData, error) { func (s *channelManager) TxData(l1Head eth.BlockID) (txData, error) {
s.mu.Lock()
defer s.mu.Unlock()
var firstWithFrame *channel var firstWithFrame *channel
for _, ch := range s.channelQueue { for _, ch := range s.channelQueue {
if ch.HasFrame() { if ch.HasFrame() {
...@@ -298,6 +308,8 @@ func (s *channelManager) outputFrames() error { ...@@ -298,6 +308,8 @@ func (s *channelManager) outputFrames() error {
// if the block does not extend the last block loaded into the state. If no // if the block does not extend the last block loaded into the state. If no
// blocks were added yet, the parent hash check is skipped. // blocks were added yet, the parent hash check is skipped.
func (s *channelManager) AddL2Block(block *types.Block) error { func (s *channelManager) AddL2Block(block *types.Block) error {
s.mu.Lock()
defer s.mu.Unlock()
if s.tip != (common.Hash{}) && s.tip != block.ParentHash() { if s.tip != (common.Hash{}) && s.tip != block.ParentHash() {
return ErrReorg return ErrReorg
} }
...@@ -324,6 +336,8 @@ func l2BlockRefFromBlockAndL1Info(block *types.Block, l1info derive.L1BlockInfo) ...@@ -324,6 +336,8 @@ func l2BlockRefFromBlockAndL1Info(block *types.Block, l1info derive.L1BlockInfo)
// and prevents the creation of any new channels. // and prevents the creation of any new channels.
// Any outputted frames still need to be published. // Any outputted frames still need to be published.
func (s *channelManager) Close() error { func (s *channelManager) Close() error {
s.mu.Lock()
defer s.mu.Unlock()
if s.closed { if s.closed {
return nil return nil
} }
......
...@@ -30,8 +30,8 @@ var ( ...@@ -30,8 +30,8 @@ var (
// DisputeGameFactoryMetaData contains all meta data concerning the DisputeGameFactory contract. // DisputeGameFactoryMetaData contains all meta data concerning the DisputeGameFactory contract.
var DisputeGameFactoryMetaData = &bind.MetaData{ var DisputeGameFactoryMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"Hash\",\"name\":\"uuid\",\"type\":\"bytes32\"}],\"name\":\"GameAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"}],\"name\":\"NoImplementation\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"disputeProxy\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"indexed\":true,\"internalType\":\"Claim\",\"name\":\"rootClaim\",\"type\":\"bytes32\"}],\"name\":\"DisputeGameCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"impl\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"}],\"name\":\"ImplementationSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"internalType\":\"Claim\",\"name\":\"rootClaim\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"create\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"proxy\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_index\",\"type\":\"uint256\"}],\"name\":\"gameAtIndex\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"proxy_\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"timestamp_\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gameCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"gameCount_\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"\",\"type\":\"uint8\"}],\"name\":\"gameImpls\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"_gameType\",\"type\":\"uint8\"},{\"internalType\":\"Claim\",\"name\":\"_rootClaim\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"_extraData\",\"type\":\"bytes\"}],\"name\":\"games\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"proxy_\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"timestamp_\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"internalType\":\"Claim\",\"name\":\"rootClaim\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"getGameUUID\",\"outputs\":[{\"internalType\":\"Hash\",\"name\":\"_uuid\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"internalType\":\"contractIDisputeGame\",\"name\":\"impl\",\"type\":\"address\"}],\"name\":\"setImplementation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"Hash\",\"name\":\"uuid\",\"type\":\"bytes32\"}],\"name\":\"GameAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"}],\"name\":\"NoImplementation\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"disputeProxy\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"},{\"indexed\":true,\"internalType\":\"Claim\",\"name\":\"rootClaim\",\"type\":\"bytes32\"}],\"name\":\"DisputeGameCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"impl\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"GameType\",\"name\":\"gameType\",\"type\":\"uint8\"}],\"name\":\"ImplementationSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"_gameType\",\"type\":\"uint8\"},{\"internalType\":\"Claim\",\"name\":\"_rootClaim\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"_extraData\",\"type\":\"bytes\"}],\"name\":\"create\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"proxy_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_index\",\"type\":\"uint256\"}],\"name\":\"gameAtIndex\",\"outputs\":[{\"internalType\":\"GameType\",\"name\":\"gameType_\",\"type\":\"uint8\"},{\"internalType\":\"Timestamp\",\"name\":\"timestamp_\",\"type\":\"uint64\"},{\"internalType\":\"contractIDisputeGame\",\"name\":\"proxy_\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gameCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"gameCount_\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"\",\"type\":\"uint8\"}],\"name\":\"gameImpls\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"_gameType\",\"type\":\"uint8\"},{\"internalType\":\"Claim\",\"name\":\"_rootClaim\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"_extraData\",\"type\":\"bytes\"}],\"name\":\"games\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"proxy_\",\"type\":\"address\"},{\"internalType\":\"Timestamp\",\"name\":\"timestamp_\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"_gameType\",\"type\":\"uint8\"},{\"internalType\":\"Claim\",\"name\":\"_rootClaim\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"_extraData\",\"type\":\"bytes\"}],\"name\":\"getGameUUID\",\"outputs\":[{\"internalType\":\"Hash\",\"name\":\"uuid_\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"GameType\",\"name\":\"_gameType\",\"type\":\"uint8\"},{\"internalType\":\"contractIDisputeGame\",\"name\":\"_impl\",\"type\":\"address\"}],\"name\":\"setImplementation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
Bin: "0x60e06040523480156200001157600080fd5b506000608081905260a0819052600460c0526200002e9062000034565b620002a2565b600054610100900460ff1615808015620000555750600054600160ff909116105b8062000085575062000072306200017260201b6200097e1760201c565b15801562000085575060005460ff166001145b620000ee5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562000112576000805461ff0019166101001790555b6200011c62000181565b6200012782620001e9565b80156200016e576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6001600160a01b03163b151590565b600054610100900460ff16620001dd5760405162461bcd60e51b815260206004820152602b6024820152600080516020620015af83398151915260448201526a6e697469616c697a696e6760a81b6064820152608401620000e5565b620001e76200023b565b565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16620002975760405162461bcd60e51b815260206004820152602b6024820152600080516020620015af83398151915260448201526a6e697469616c697a696e6760a81b6064820152608401620000e5565b620001e733620001e9565b60805160a05160c0516112dd620002d26000396000610605015260006105dc015260006105b301526112dd6000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c80638da5cb5b11610081578063c4d66de81161005b578063c4d66de81461026d578063dfa162d314610280578063f2fde38b146102b657600080fd5b80638da5cb5b146101fd578063bb8aa1fc1461021b578063c49d52711461025a57600080fd5b80634d1975b4116100b25780634d1975b4146101d857806354fd4d50146101e0578063715018a6146101f557600080fd5b806326daafbe146100d95780633142e55e1461018b57806345583b7a146101c3575b600080fd5b6101786100e7366004610e82565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0810180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0830180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08086018051988652968352606087529451609f0190941683209190925291905291905290565b6040519081526020015b60405180910390f35b61019e610199366004610f6b565b6102c9565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610182565b6101d66101d1366004611014565b610525565b005b606754610178565b6101e86105ac565b604051610182919061107b565b6101d661064f565b60335473ffffffffffffffffffffffffffffffffffffffff1661019e565b61022e6102293660046110cc565b610663565b6040805173ffffffffffffffffffffffffffffffffffffffff9093168352602083019190915201610182565b61022e610268366004610f6b565b6106ad565b6101d661027b3660046110e5565b61072b565b61019e61028e366004611109565b60656020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6101d66102c43660046110e5565b6108c7565b60ff841660009081526065602052604081205473ffffffffffffffffffffffffffffffffffffffff1680610333576040517f44265d6f00000000000000000000000000000000000000000000000000000000815260ff871660048201526024015b60405180910390fd5b61039685858560405160200161034b93929190611124565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905273ffffffffffffffffffffffffffffffffffffffff83169061099a565b91508173ffffffffffffffffffffffffffffffffffffffff16638129fc1c6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156103e057600080fd5b505af11580156103f4573d6000803e3d6000fd5b50505050600061043b878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506100e792505050565b60008181526066602052604090205490915015610487576040517f014f6fe50000000000000000000000000000000000000000000000000000000081526004810182905260240161032a565b60004260a01b8417600083815260666020526040808220839055606780546001810182559083527f9787eeb91fe3101235e4a76063c7023ecb40f923f97916639c598592fa30d6ae0183905551919250889160ff8b169173ffffffffffffffffffffffffffffffffffffffff8816917ffad0599ff449d8d9685eadecca8cb9e00924c5fd8367c1c09469824939e1ffec9190a4505050949350505050565b61052d610ace565b60ff821660008181526065602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8616908117909155905190917f623713f72f6e427a8044bb8b3bd6834357cf285decbaa21bcc73c1d0632c4d8491a35050565b60606105d77f0000000000000000000000000000000000000000000000000000000000000000610b4f565b6106007f0000000000000000000000000000000000000000000000000000000000000000610b4f565b6106297f0000000000000000000000000000000000000000000000000000000000000000610b4f565b60405160200161063b9392919061113e565b604051602081830303815290604052905090565b610657610ace565b6106616000610c8c565b565b60008060006067848154811061067b5761067b6111b4565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff81169560a09190911c945092505050565b60008060006106f3878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506100e792505050565b60009081526066602052604090205473ffffffffffffffffffffffffffffffffffffffff81169860a09190911c975095505050505050565b600054610100900460ff161580801561074b5750600054600160ff909116105b806107655750303b158015610765575060005460ff166001145b6107f1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840161032a565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561084f57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b610857610d03565b61086082610c8c565b80156108c357600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6108cf610ace565b73ffffffffffffffffffffffffffffffffffffffff8116610972576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161032a565b61097b81610c8c565b50565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b60006002825101603f8101600a81036040518360581b8260e81b177f6100003d81600a3d39f3363d3d373d3d3d3d610000806035363936013d7300001781528660601b601e8201527f5af43d3d93803e603357fd5bf300000000000000000000000000000000000000603282015285519150603f8101602087015b60208410610a5257805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09093019260209182019101610a15565b517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602085900360031b1b16815260f085901b9083015282816000f0945084610abf577febfef1880000000000000000000000000000000000000000000000000000000060005260206000fd5b90910160405250909392505050565b60335473ffffffffffffffffffffffffffffffffffffffff163314610661576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161032a565b606081600003610b9257505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115610bbc5780610ba681611212565b9150610bb59050600a83611279565b9150610b96565b60008167ffffffffffffffff811115610bd757610bd7610e53565b6040519080825280601f01601f191660200182016040528015610c01576020820181803683370190505b5090505b8415610c8457610c1660018361128d565b9150610c23600a866112a4565b610c2e9060306112b8565b60f81b818381518110610c4357610c436111b4565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610c7d600a86611279565b9450610c05565b949350505050565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16610d9a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840161032a565b610661600054610100900460ff16610e34576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840161032a565b61066133610c8c565b803560ff81168114610e4e57600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080600060608486031215610e9757600080fd5b610ea084610e3d565b925060208401359150604084013567ffffffffffffffff80821115610ec457600080fd5b818601915086601f830112610ed857600080fd5b813581811115610eea57610eea610e53565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715610f3057610f30610e53565b81604052828152896020848701011115610f4957600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60008060008060608587031215610f8157600080fd5b610f8a85610e3d565b935060208501359250604085013567ffffffffffffffff80821115610fae57600080fd5b818701915087601f830112610fc257600080fd5b813581811115610fd157600080fd5b886020828501011115610fe357600080fd5b95989497505060200194505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461097b57600080fd5b6000806040838503121561102757600080fd5b61103083610e3d565b9150602083013561104081610ff2565b809150509250929050565b60005b8381101561106657818101518382015260200161104e565b83811115611075576000848401525b50505050565b602081526000825180602084015261109a81604085016020870161104b565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6000602082840312156110de57600080fd5b5035919050565b6000602082840312156110f757600080fd5b813561110281610ff2565b9392505050565b60006020828403121561111b57600080fd5b61110282610e3d565b838152818360208301376000910160200190815292915050565b6000845161115081846020890161104b565b80830190507f2e00000000000000000000000000000000000000000000000000000000000000808252855161118c816001850160208a0161104b565b600192019182015283516111a781600284016020880161104b565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611243576112436111e3565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826112885761128861124a565b500490565b60008282101561129f5761129f6111e3565b500390565b6000826112b3576112b361124a565b500690565b600082198211156112cb576112cb6111e3565b50019056fea164736f6c634300080f000a496e697469616c697a61626c653a20636f6e7472616374206973206e6f742069", Bin: "0x60e06040523480156200001157600080fd5b506000608081905260a0819052600560c0526200002e9062000034565b620002a2565b600054610100900460ff1615808015620000555750600054600160ff909116105b8062000085575062000072306200017260201b620009ed1760201c565b15801562000085575060005460ff166001145b620000ee5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562000112576000805461ff0019166101001790555b6200011c62000181565b6200012782620001e9565b80156200016e576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6001600160a01b03163b151590565b600054610100900460ff16620001dd5760405162461bcd60e51b815260206004820152602b60248201526000805160206200161e83398151915260448201526a6e697469616c697a696e6760a81b6064820152608401620000e5565b620001e76200023b565b565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16620002975760405162461bcd60e51b815260206004820152602b60248201526000805160206200161e83398151915260448201526a6e697469616c697a696e6760a81b6064820152608401620000e5565b620001e733620001e9565b60805160a05160c05161134c620002d260003960006106520152600061062901526000610600015261134c6000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c80638da5cb5b11610081578063c4d66de81161005b578063c4d66de8146102b4578063dfa162d3146102c7578063f2fde38b146102fd57600080fd5b80638da5cb5b146101fd578063bb8aa1fc1461021b578063c49d52711461026c57600080fd5b80634d1975b4116100b25780634d1975b4146101d857806354fd4d50146101e0578063715018a6146101f557600080fd5b806326daafbe146100d95780633142e55e1461018b57806345583b7a146101c3575b600080fd5b6101786100e7366004610ef1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0810180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0830180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08086018051988652968352606087529451609f0190941683209190925291905291905290565b6040519081526020015b60405180910390f35b61019e610199366004610fda565b610310565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610182565b6101d66101d1366004611083565b610572565b005b606754610178565b6101e86105f9565b60405161018291906110ea565b6101d661069c565b60335473ffffffffffffffffffffffffffffffffffffffff1661019e565b61022e61022936600461113b565b6106b0565b6040805160ff909416845267ffffffffffffffff909216602084015273ffffffffffffffffffffffffffffffffffffffff1690820152606001610182565b61027f61027a366004610fda565b610712565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835267ffffffffffffffff909116602083015201610182565b6101d66102c2366004611154565b61079a565b61019e6102d5366004611178565b60656020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6101d661030b366004611154565b610936565b60ff841660009081526065602052604081205473ffffffffffffffffffffffffffffffffffffffff168061037a576040517f44265d6f00000000000000000000000000000000000000000000000000000000815260ff871660048201526024015b60405180910390fd5b6103dd85858560405160200161039293929190611193565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905273ffffffffffffffffffffffffffffffffffffffff831690610a09565b91508173ffffffffffffffffffffffffffffffffffffffff16638129fc1c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561042757600080fd5b505af115801561043b573d6000803e3d6000fd5b505050506000610482878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506100e792505050565b600081815260666020526040902054909150156104ce576040517f014f6fe500000000000000000000000000000000000000000000000000000000815260048101829052602401610371565b60004260b81b60f889901b178417600083815260666020526040808220839055606780546001810182559083527f9787eeb91fe3101235e4a76063c7023ecb40f923f97916639c598592fa30d6ae0183905551919250889160ff8b169173ffffffffffffffffffffffffffffffffffffffff8816917ffad0599ff449d8d9685eadecca8cb9e00924c5fd8367c1c09469824939e1ffec9190a4505050949350505050565b61057a610b3d565b60ff821660008181526065602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8616908117909155905190917f623713f72f6e427a8044bb8b3bd6834357cf285decbaa21bcc73c1d0632c4d8491a35050565b60606106247f0000000000000000000000000000000000000000000000000000000000000000610bbe565b61064d7f0000000000000000000000000000000000000000000000000000000000000000610bbe565b6106767f0000000000000000000000000000000000000000000000000000000000000000610bbe565b604051602001610688939291906111ad565b604051602081830303815290604052905090565b6106a4610b3d565b6106ae6000610cfb565b565b6000806000610705606785815481106106cb576106cb611223565b906000526020600020015460f881901c9167ffffffffffffffff60b883901c169173ffffffffffffffffffffffffffffffffffffffff1690565b9196909550909350915050565b6000806000610758878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506100e792505050565b60009081526066602052604090205473ffffffffffffffffffffffffffffffffffffffff81169860b89190911c67ffffffffffffffff16975095505050505050565b600054610100900460ff16158080156107ba5750600054600160ff909116105b806107d45750303b1580156107d4575060005460ff166001145b610860576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610371565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156108be57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6108c6610d72565b6108cf82610cfb565b801561093257600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b61093e610b3d565b73ffffffffffffffffffffffffffffffffffffffff81166109e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610371565b6109ea81610cfb565b50565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b60006002825101603f8101600a81036040518360581b8260e81b177f6100003d81600a3d39f3363d3d373d3d3d3d610000806035363936013d7300001781528660601b601e8201527f5af43d3d93803e603357fd5bf300000000000000000000000000000000000000603282015285519150603f8101602087015b60208410610ac157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09093019260209182019101610a84565b517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602085900360031b1b16815260f085901b9083015282816000f0945084610b2e577febfef1880000000000000000000000000000000000000000000000000000000060005260206000fd5b90910160405250909392505050565b60335473ffffffffffffffffffffffffffffffffffffffff1633146106ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610371565b606081600003610c0157505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115610c2b5780610c1581611281565b9150610c249050600a836112e8565b9150610c05565b60008167ffffffffffffffff811115610c4657610c46610ec2565b6040519080825280601f01601f191660200182016040528015610c70576020820181803683370190505b5090505b8415610cf357610c856001836112fc565b9150610c92600a86611313565b610c9d906030611327565b60f81b818381518110610cb257610cb2611223565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610cec600a866112e8565b9450610c74565b949350505050565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16610e09576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610371565b6106ae600054610100900460ff16610ea3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610371565b6106ae33610cfb565b803560ff81168114610ebd57600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080600060608486031215610f0657600080fd5b610f0f84610eac565b925060208401359150604084013567ffffffffffffffff80821115610f3357600080fd5b818601915086601f830112610f4757600080fd5b813581811115610f5957610f59610ec2565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715610f9f57610f9f610ec2565b81604052828152896020848701011115610fb857600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60008060008060608587031215610ff057600080fd5b610ff985610eac565b935060208501359250604085013567ffffffffffffffff8082111561101d57600080fd5b818701915087601f83011261103157600080fd5b81358181111561104057600080fd5b88602082850101111561105257600080fd5b95989497505060200194505050565b73ffffffffffffffffffffffffffffffffffffffff811681146109ea57600080fd5b6000806040838503121561109657600080fd5b61109f83610eac565b915060208301356110af81611061565b809150509250929050565b60005b838110156110d55781810151838201526020016110bd565b838111156110e4576000848401525b50505050565b60208152600082518060208401526111098160408501602087016110ba565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60006020828403121561114d57600080fd5b5035919050565b60006020828403121561116657600080fd5b813561117181611061565b9392505050565b60006020828403121561118a57600080fd5b61117182610eac565b838152818360208301376000910160200190815292915050565b600084516111bf8184602089016110ba565b80830190507f2e0000000000000000000000000000000000000000000000000000000000000080825285516111fb816001850160208a016110ba565b600192019182015283516112168160028401602088016110ba565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036112b2576112b2611252565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826112f7576112f76112b9565b500490565b60008282101561130e5761130e611252565b500390565b600082611322576113226112b9565b500690565b6000821982111561133a5761133a611252565b50019056fea164736f6c634300080f000a496e697469616c697a61626c653a20636f6e7472616374206973206e6f742069",
} }
// DisputeGameFactoryABI is the input ABI used to generate the binding from. // DisputeGameFactoryABI is the input ABI used to generate the binding from.
...@@ -203,24 +203,27 @@ func (_DisputeGameFactory *DisputeGameFactoryTransactorRaw) Transact(opts *bind. ...@@ -203,24 +203,27 @@ func (_DisputeGameFactory *DisputeGameFactoryTransactorRaw) Transact(opts *bind.
// GameAtIndex is a free data retrieval call binding the contract method 0xbb8aa1fc. // GameAtIndex is a free data retrieval call binding the contract method 0xbb8aa1fc.
// //
// Solidity: function gameAtIndex(uint256 _index) view returns(address proxy_, uint256 timestamp_) // Solidity: function gameAtIndex(uint256 _index) view returns(uint8 gameType_, uint64 timestamp_, address proxy_)
func (_DisputeGameFactory *DisputeGameFactoryCaller) GameAtIndex(opts *bind.CallOpts, _index *big.Int) (struct { func (_DisputeGameFactory *DisputeGameFactoryCaller) GameAtIndex(opts *bind.CallOpts, _index *big.Int) (struct {
GameType uint8
Timestamp uint64
Proxy common.Address Proxy common.Address
Timestamp *big.Int
}, error) { }, error) {
var out []interface{} var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "gameAtIndex", _index) err := _DisputeGameFactory.contract.Call(opts, &out, "gameAtIndex", _index)
outstruct := new(struct { outstruct := new(struct {
GameType uint8
Timestamp uint64
Proxy common.Address Proxy common.Address
Timestamp *big.Int
}) })
if err != nil { if err != nil {
return *outstruct, err return *outstruct, err
} }
outstruct.Proxy = *abi.ConvertType(out[0], new(common.Address)).(*common.Address) outstruct.GameType = *abi.ConvertType(out[0], new(uint8)).(*uint8)
outstruct.Timestamp = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) outstruct.Timestamp = *abi.ConvertType(out[1], new(uint64)).(*uint64)
outstruct.Proxy = *abi.ConvertType(out[2], new(common.Address)).(*common.Address)
return *outstruct, err return *outstruct, err
...@@ -228,20 +231,22 @@ func (_DisputeGameFactory *DisputeGameFactoryCaller) GameAtIndex(opts *bind.Call ...@@ -228,20 +231,22 @@ func (_DisputeGameFactory *DisputeGameFactoryCaller) GameAtIndex(opts *bind.Call
// GameAtIndex is a free data retrieval call binding the contract method 0xbb8aa1fc. // GameAtIndex is a free data retrieval call binding the contract method 0xbb8aa1fc.
// //
// Solidity: function gameAtIndex(uint256 _index) view returns(address proxy_, uint256 timestamp_) // Solidity: function gameAtIndex(uint256 _index) view returns(uint8 gameType_, uint64 timestamp_, address proxy_)
func (_DisputeGameFactory *DisputeGameFactorySession) GameAtIndex(_index *big.Int) (struct { func (_DisputeGameFactory *DisputeGameFactorySession) GameAtIndex(_index *big.Int) (struct {
GameType uint8
Timestamp uint64
Proxy common.Address Proxy common.Address
Timestamp *big.Int
}, error) { }, error) {
return _DisputeGameFactory.Contract.GameAtIndex(&_DisputeGameFactory.CallOpts, _index) return _DisputeGameFactory.Contract.GameAtIndex(&_DisputeGameFactory.CallOpts, _index)
} }
// GameAtIndex is a free data retrieval call binding the contract method 0xbb8aa1fc. // GameAtIndex is a free data retrieval call binding the contract method 0xbb8aa1fc.
// //
// Solidity: function gameAtIndex(uint256 _index) view returns(address proxy_, uint256 timestamp_) // Solidity: function gameAtIndex(uint256 _index) view returns(uint8 gameType_, uint64 timestamp_, address proxy_)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) GameAtIndex(_index *big.Int) (struct { func (_DisputeGameFactory *DisputeGameFactoryCallerSession) GameAtIndex(_index *big.Int) (struct {
GameType uint8
Timestamp uint64
Proxy common.Address Proxy common.Address
Timestamp *big.Int
}, error) { }, error) {
return _DisputeGameFactory.Contract.GameAtIndex(&_DisputeGameFactory.CallOpts, _index) return _DisputeGameFactory.Contract.GameAtIndex(&_DisputeGameFactory.CallOpts, _index)
} }
...@@ -310,24 +315,24 @@ func (_DisputeGameFactory *DisputeGameFactoryCallerSession) GameImpls(arg0 uint8 ...@@ -310,24 +315,24 @@ func (_DisputeGameFactory *DisputeGameFactoryCallerSession) GameImpls(arg0 uint8
// Games is a free data retrieval call binding the contract method 0xc49d5271. // Games is a free data retrieval call binding the contract method 0xc49d5271.
// //
// Solidity: function games(uint8 _gameType, bytes32 _rootClaim, bytes _extraData) view returns(address proxy_, uint256 timestamp_) // Solidity: function games(uint8 _gameType, bytes32 _rootClaim, bytes _extraData) view returns(address proxy_, uint64 timestamp_)
func (_DisputeGameFactory *DisputeGameFactoryCaller) Games(opts *bind.CallOpts, _gameType uint8, _rootClaim [32]byte, _extraData []byte) (struct { func (_DisputeGameFactory *DisputeGameFactoryCaller) Games(opts *bind.CallOpts, _gameType uint8, _rootClaim [32]byte, _extraData []byte) (struct {
Proxy common.Address Proxy common.Address
Timestamp *big.Int Timestamp uint64
}, error) { }, error) {
var out []interface{} var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "games", _gameType, _rootClaim, _extraData) err := _DisputeGameFactory.contract.Call(opts, &out, "games", _gameType, _rootClaim, _extraData)
outstruct := new(struct { outstruct := new(struct {
Proxy common.Address Proxy common.Address
Timestamp *big.Int Timestamp uint64
}) })
if err != nil { if err != nil {
return *outstruct, err return *outstruct, err
} }
outstruct.Proxy = *abi.ConvertType(out[0], new(common.Address)).(*common.Address) outstruct.Proxy = *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
outstruct.Timestamp = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) outstruct.Timestamp = *abi.ConvertType(out[1], new(uint64)).(*uint64)
return *outstruct, err return *outstruct, err
...@@ -335,30 +340,30 @@ func (_DisputeGameFactory *DisputeGameFactoryCaller) Games(opts *bind.CallOpts, ...@@ -335,30 +340,30 @@ func (_DisputeGameFactory *DisputeGameFactoryCaller) Games(opts *bind.CallOpts,
// Games is a free data retrieval call binding the contract method 0xc49d5271. // Games is a free data retrieval call binding the contract method 0xc49d5271.
// //
// Solidity: function games(uint8 _gameType, bytes32 _rootClaim, bytes _extraData) view returns(address proxy_, uint256 timestamp_) // Solidity: function games(uint8 _gameType, bytes32 _rootClaim, bytes _extraData) view returns(address proxy_, uint64 timestamp_)
func (_DisputeGameFactory *DisputeGameFactorySession) Games(_gameType uint8, _rootClaim [32]byte, _extraData []byte) (struct { func (_DisputeGameFactory *DisputeGameFactorySession) Games(_gameType uint8, _rootClaim [32]byte, _extraData []byte) (struct {
Proxy common.Address Proxy common.Address
Timestamp *big.Int Timestamp uint64
}, error) { }, error) {
return _DisputeGameFactory.Contract.Games(&_DisputeGameFactory.CallOpts, _gameType, _rootClaim, _extraData) return _DisputeGameFactory.Contract.Games(&_DisputeGameFactory.CallOpts, _gameType, _rootClaim, _extraData)
} }
// Games is a free data retrieval call binding the contract method 0xc49d5271. // Games is a free data retrieval call binding the contract method 0xc49d5271.
// //
// Solidity: function games(uint8 _gameType, bytes32 _rootClaim, bytes _extraData) view returns(address proxy_, uint256 timestamp_) // Solidity: function games(uint8 _gameType, bytes32 _rootClaim, bytes _extraData) view returns(address proxy_, uint64 timestamp_)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) Games(_gameType uint8, _rootClaim [32]byte, _extraData []byte) (struct { func (_DisputeGameFactory *DisputeGameFactoryCallerSession) Games(_gameType uint8, _rootClaim [32]byte, _extraData []byte) (struct {
Proxy common.Address Proxy common.Address
Timestamp *big.Int Timestamp uint64
}, error) { }, error) {
return _DisputeGameFactory.Contract.Games(&_DisputeGameFactory.CallOpts, _gameType, _rootClaim, _extraData) return _DisputeGameFactory.Contract.Games(&_DisputeGameFactory.CallOpts, _gameType, _rootClaim, _extraData)
} }
// GetGameUUID is a free data retrieval call binding the contract method 0x26daafbe. // GetGameUUID is a free data retrieval call binding the contract method 0x26daafbe.
// //
// Solidity: function getGameUUID(uint8 gameType, bytes32 rootClaim, bytes extraData) pure returns(bytes32 _uuid) // Solidity: function getGameUUID(uint8 _gameType, bytes32 _rootClaim, bytes _extraData) pure returns(bytes32 uuid_)
func (_DisputeGameFactory *DisputeGameFactoryCaller) GetGameUUID(opts *bind.CallOpts, gameType uint8, rootClaim [32]byte, extraData []byte) ([32]byte, error) { func (_DisputeGameFactory *DisputeGameFactoryCaller) GetGameUUID(opts *bind.CallOpts, _gameType uint8, _rootClaim [32]byte, _extraData []byte) ([32]byte, error) {
var out []interface{} var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "getGameUUID", gameType, rootClaim, extraData) err := _DisputeGameFactory.contract.Call(opts, &out, "getGameUUID", _gameType, _rootClaim, _extraData)
if err != nil { if err != nil {
return *new([32]byte), err return *new([32]byte), err
...@@ -372,16 +377,16 @@ func (_DisputeGameFactory *DisputeGameFactoryCaller) GetGameUUID(opts *bind.Call ...@@ -372,16 +377,16 @@ func (_DisputeGameFactory *DisputeGameFactoryCaller) GetGameUUID(opts *bind.Call
// GetGameUUID is a free data retrieval call binding the contract method 0x26daafbe. // GetGameUUID is a free data retrieval call binding the contract method 0x26daafbe.
// //
// Solidity: function getGameUUID(uint8 gameType, bytes32 rootClaim, bytes extraData) pure returns(bytes32 _uuid) // Solidity: function getGameUUID(uint8 _gameType, bytes32 _rootClaim, bytes _extraData) pure returns(bytes32 uuid_)
func (_DisputeGameFactory *DisputeGameFactorySession) GetGameUUID(gameType uint8, rootClaim [32]byte, extraData []byte) ([32]byte, error) { func (_DisputeGameFactory *DisputeGameFactorySession) GetGameUUID(_gameType uint8, _rootClaim [32]byte, _extraData []byte) ([32]byte, error) {
return _DisputeGameFactory.Contract.GetGameUUID(&_DisputeGameFactory.CallOpts, gameType, rootClaim, extraData) return _DisputeGameFactory.Contract.GetGameUUID(&_DisputeGameFactory.CallOpts, _gameType, _rootClaim, _extraData)
} }
// GetGameUUID is a free data retrieval call binding the contract method 0x26daafbe. // GetGameUUID is a free data retrieval call binding the contract method 0x26daafbe.
// //
// Solidity: function getGameUUID(uint8 gameType, bytes32 rootClaim, bytes extraData) pure returns(bytes32 _uuid) // Solidity: function getGameUUID(uint8 _gameType, bytes32 _rootClaim, bytes _extraData) pure returns(bytes32 uuid_)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) GetGameUUID(gameType uint8, rootClaim [32]byte, extraData []byte) ([32]byte, error) { func (_DisputeGameFactory *DisputeGameFactoryCallerSession) GetGameUUID(_gameType uint8, _rootClaim [32]byte, _extraData []byte) ([32]byte, error) {
return _DisputeGameFactory.Contract.GetGameUUID(&_DisputeGameFactory.CallOpts, gameType, rootClaim, extraData) return _DisputeGameFactory.Contract.GetGameUUID(&_DisputeGameFactory.CallOpts, _gameType, _rootClaim, _extraData)
} }
// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. // Owner is a free data retrieval call binding the contract method 0x8da5cb5b.
...@@ -448,23 +453,23 @@ func (_DisputeGameFactory *DisputeGameFactoryCallerSession) Version() (string, e ...@@ -448,23 +453,23 @@ func (_DisputeGameFactory *DisputeGameFactoryCallerSession) Version() (string, e
// Create is a paid mutator transaction binding the contract method 0x3142e55e. // Create is a paid mutator transaction binding the contract method 0x3142e55e.
// //
// Solidity: function create(uint8 gameType, bytes32 rootClaim, bytes extraData) returns(address proxy) // Solidity: function create(uint8 _gameType, bytes32 _rootClaim, bytes _extraData) returns(address proxy_)
func (_DisputeGameFactory *DisputeGameFactoryTransactor) Create(opts *bind.TransactOpts, gameType uint8, rootClaim [32]byte, extraData []byte) (*types.Transaction, error) { func (_DisputeGameFactory *DisputeGameFactoryTransactor) Create(opts *bind.TransactOpts, _gameType uint8, _rootClaim [32]byte, _extraData []byte) (*types.Transaction, error) {
return _DisputeGameFactory.contract.Transact(opts, "create", gameType, rootClaim, extraData) return _DisputeGameFactory.contract.Transact(opts, "create", _gameType, _rootClaim, _extraData)
} }
// Create is a paid mutator transaction binding the contract method 0x3142e55e. // Create is a paid mutator transaction binding the contract method 0x3142e55e.
// //
// Solidity: function create(uint8 gameType, bytes32 rootClaim, bytes extraData) returns(address proxy) // Solidity: function create(uint8 _gameType, bytes32 _rootClaim, bytes _extraData) returns(address proxy_)
func (_DisputeGameFactory *DisputeGameFactorySession) Create(gameType uint8, rootClaim [32]byte, extraData []byte) (*types.Transaction, error) { func (_DisputeGameFactory *DisputeGameFactorySession) Create(_gameType uint8, _rootClaim [32]byte, _extraData []byte) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.Create(&_DisputeGameFactory.TransactOpts, gameType, rootClaim, extraData) return _DisputeGameFactory.Contract.Create(&_DisputeGameFactory.TransactOpts, _gameType, _rootClaim, _extraData)
} }
// Create is a paid mutator transaction binding the contract method 0x3142e55e. // Create is a paid mutator transaction binding the contract method 0x3142e55e.
// //
// Solidity: function create(uint8 gameType, bytes32 rootClaim, bytes extraData) returns(address proxy) // Solidity: function create(uint8 _gameType, bytes32 _rootClaim, bytes _extraData) returns(address proxy_)
func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) Create(gameType uint8, rootClaim [32]byte, extraData []byte) (*types.Transaction, error) { func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) Create(_gameType uint8, _rootClaim [32]byte, _extraData []byte) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.Create(&_DisputeGameFactory.TransactOpts, gameType, rootClaim, extraData) return _DisputeGameFactory.Contract.Create(&_DisputeGameFactory.TransactOpts, _gameType, _rootClaim, _extraData)
} }
// Initialize is a paid mutator transaction binding the contract method 0xc4d66de8. // Initialize is a paid mutator transaction binding the contract method 0xc4d66de8.
...@@ -511,23 +516,23 @@ func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) RenounceOwnershi ...@@ -511,23 +516,23 @@ func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) RenounceOwnershi
// SetImplementation is a paid mutator transaction binding the contract method 0x45583b7a. // SetImplementation is a paid mutator transaction binding the contract method 0x45583b7a.
// //
// Solidity: function setImplementation(uint8 gameType, address impl) returns() // Solidity: function setImplementation(uint8 _gameType, address _impl) returns()
func (_DisputeGameFactory *DisputeGameFactoryTransactor) SetImplementation(opts *bind.TransactOpts, gameType uint8, impl common.Address) (*types.Transaction, error) { func (_DisputeGameFactory *DisputeGameFactoryTransactor) SetImplementation(opts *bind.TransactOpts, _gameType uint8, _impl common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.contract.Transact(opts, "setImplementation", gameType, impl) return _DisputeGameFactory.contract.Transact(opts, "setImplementation", _gameType, _impl)
} }
// SetImplementation is a paid mutator transaction binding the contract method 0x45583b7a. // SetImplementation is a paid mutator transaction binding the contract method 0x45583b7a.
// //
// Solidity: function setImplementation(uint8 gameType, address impl) returns() // Solidity: function setImplementation(uint8 _gameType, address _impl) returns()
func (_DisputeGameFactory *DisputeGameFactorySession) SetImplementation(gameType uint8, impl common.Address) (*types.Transaction, error) { func (_DisputeGameFactory *DisputeGameFactorySession) SetImplementation(_gameType uint8, _impl common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.SetImplementation(&_DisputeGameFactory.TransactOpts, gameType, impl) return _DisputeGameFactory.Contract.SetImplementation(&_DisputeGameFactory.TransactOpts, _gameType, _impl)
} }
// SetImplementation is a paid mutator transaction binding the contract method 0x45583b7a. // SetImplementation is a paid mutator transaction binding the contract method 0x45583b7a.
// //
// Solidity: function setImplementation(uint8 gameType, address impl) returns() // Solidity: function setImplementation(uint8 _gameType, address _impl) returns()
func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) SetImplementation(gameType uint8, impl common.Address) (*types.Transaction, error) { func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) SetImplementation(_gameType uint8, _impl common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.SetImplementation(&_DisputeGameFactory.TransactOpts, gameType, impl) return _DisputeGameFactory.Contract.SetImplementation(&_DisputeGameFactory.TransactOpts, _gameType, _impl)
} }
// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. // TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b.
......
...@@ -13,7 +13,7 @@ const DisputeGameFactoryStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"con ...@@ -13,7 +13,7 @@ const DisputeGameFactoryStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"con
var DisputeGameFactoryStorageLayout = new(solc.StorageLayout) var DisputeGameFactoryStorageLayout = new(solc.StorageLayout)
var DisputeGameFactoryDeployedBin = "0x608060405234801561001057600080fd5b50600436106100d45760003560e01c80638da5cb5b11610081578063c4d66de81161005b578063c4d66de81461026d578063dfa162d314610280578063f2fde38b146102b657600080fd5b80638da5cb5b146101fd578063bb8aa1fc1461021b578063c49d52711461025a57600080fd5b80634d1975b4116100b25780634d1975b4146101d857806354fd4d50146101e0578063715018a6146101f557600080fd5b806326daafbe146100d95780633142e55e1461018b57806345583b7a146101c3575b600080fd5b6101786100e7366004610e82565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0810180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0830180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08086018051988652968352606087529451609f0190941683209190925291905291905290565b6040519081526020015b60405180910390f35b61019e610199366004610f6b565b6102c9565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610182565b6101d66101d1366004611014565b610525565b005b606754610178565b6101e86105ac565b604051610182919061107b565b6101d661064f565b60335473ffffffffffffffffffffffffffffffffffffffff1661019e565b61022e6102293660046110cc565b610663565b6040805173ffffffffffffffffffffffffffffffffffffffff9093168352602083019190915201610182565b61022e610268366004610f6b565b6106ad565b6101d661027b3660046110e5565b61072b565b61019e61028e366004611109565b60656020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6101d66102c43660046110e5565b6108c7565b60ff841660009081526065602052604081205473ffffffffffffffffffffffffffffffffffffffff1680610333576040517f44265d6f00000000000000000000000000000000000000000000000000000000815260ff871660048201526024015b60405180910390fd5b61039685858560405160200161034b93929190611124565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905273ffffffffffffffffffffffffffffffffffffffff83169061099a565b91508173ffffffffffffffffffffffffffffffffffffffff16638129fc1c6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156103e057600080fd5b505af11580156103f4573d6000803e3d6000fd5b50505050600061043b878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506100e792505050565b60008181526066602052604090205490915015610487576040517f014f6fe50000000000000000000000000000000000000000000000000000000081526004810182905260240161032a565b60004260a01b8417600083815260666020526040808220839055606780546001810182559083527f9787eeb91fe3101235e4a76063c7023ecb40f923f97916639c598592fa30d6ae0183905551919250889160ff8b169173ffffffffffffffffffffffffffffffffffffffff8816917ffad0599ff449d8d9685eadecca8cb9e00924c5fd8367c1c09469824939e1ffec9190a4505050949350505050565b61052d610ace565b60ff821660008181526065602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8616908117909155905190917f623713f72f6e427a8044bb8b3bd6834357cf285decbaa21bcc73c1d0632c4d8491a35050565b60606105d77f0000000000000000000000000000000000000000000000000000000000000000610b4f565b6106007f0000000000000000000000000000000000000000000000000000000000000000610b4f565b6106297f0000000000000000000000000000000000000000000000000000000000000000610b4f565b60405160200161063b9392919061113e565b604051602081830303815290604052905090565b610657610ace565b6106616000610c8c565b565b60008060006067848154811061067b5761067b6111b4565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff81169560a09190911c945092505050565b60008060006106f3878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506100e792505050565b60009081526066602052604090205473ffffffffffffffffffffffffffffffffffffffff81169860a09190911c975095505050505050565b600054610100900460ff161580801561074b5750600054600160ff909116105b806107655750303b158015610765575060005460ff166001145b6107f1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840161032a565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561084f57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b610857610d03565b61086082610c8c565b80156108c357600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6108cf610ace565b73ffffffffffffffffffffffffffffffffffffffff8116610972576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161032a565b61097b81610c8c565b50565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b60006002825101603f8101600a81036040518360581b8260e81b177f6100003d81600a3d39f3363d3d373d3d3d3d610000806035363936013d7300001781528660601b601e8201527f5af43d3d93803e603357fd5bf300000000000000000000000000000000000000603282015285519150603f8101602087015b60208410610a5257805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09093019260209182019101610a15565b517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602085900360031b1b16815260f085901b9083015282816000f0945084610abf577febfef1880000000000000000000000000000000000000000000000000000000060005260206000fd5b90910160405250909392505050565b60335473ffffffffffffffffffffffffffffffffffffffff163314610661576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161032a565b606081600003610b9257505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115610bbc5780610ba681611212565b9150610bb59050600a83611279565b9150610b96565b60008167ffffffffffffffff811115610bd757610bd7610e53565b6040519080825280601f01601f191660200182016040528015610c01576020820181803683370190505b5090505b8415610c8457610c1660018361128d565b9150610c23600a866112a4565b610c2e9060306112b8565b60f81b818381518110610c4357610c436111b4565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610c7d600a86611279565b9450610c05565b949350505050565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16610d9a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840161032a565b610661600054610100900460ff16610e34576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840161032a565b61066133610c8c565b803560ff81168114610e4e57600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080600060608486031215610e9757600080fd5b610ea084610e3d565b925060208401359150604084013567ffffffffffffffff80821115610ec457600080fd5b818601915086601f830112610ed857600080fd5b813581811115610eea57610eea610e53565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715610f3057610f30610e53565b81604052828152896020848701011115610f4957600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60008060008060608587031215610f8157600080fd5b610f8a85610e3d565b935060208501359250604085013567ffffffffffffffff80821115610fae57600080fd5b818701915087601f830112610fc257600080fd5b813581811115610fd157600080fd5b886020828501011115610fe357600080fd5b95989497505060200194505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461097b57600080fd5b6000806040838503121561102757600080fd5b61103083610e3d565b9150602083013561104081610ff2565b809150509250929050565b60005b8381101561106657818101518382015260200161104e565b83811115611075576000848401525b50505050565b602081526000825180602084015261109a81604085016020870161104b565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6000602082840312156110de57600080fd5b5035919050565b6000602082840312156110f757600080fd5b813561110281610ff2565b9392505050565b60006020828403121561111b57600080fd5b61110282610e3d565b838152818360208301376000910160200190815292915050565b6000845161115081846020890161104b565b80830190507f2e00000000000000000000000000000000000000000000000000000000000000808252855161118c816001850160208a0161104b565b600192019182015283516111a781600284016020880161104b565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611243576112436111e3565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826112885761128861124a565b500490565b60008282101561129f5761129f6111e3565b500390565b6000826112b3576112b361124a565b500690565b600082198211156112cb576112cb6111e3565b50019056fea164736f6c634300080f000a" var DisputeGameFactoryDeployedBin = "0x608060405234801561001057600080fd5b50600436106100d45760003560e01c80638da5cb5b11610081578063c4d66de81161005b578063c4d66de8146102b4578063dfa162d3146102c7578063f2fde38b146102fd57600080fd5b80638da5cb5b146101fd578063bb8aa1fc1461021b578063c49d52711461026c57600080fd5b80634d1975b4116100b25780634d1975b4146101d857806354fd4d50146101e0578063715018a6146101f557600080fd5b806326daafbe146100d95780633142e55e1461018b57806345583b7a146101c3575b600080fd5b6101786100e7366004610ef1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0810180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0830180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08086018051988652968352606087529451609f0190941683209190925291905291905290565b6040519081526020015b60405180910390f35b61019e610199366004610fda565b610310565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610182565b6101d66101d1366004611083565b610572565b005b606754610178565b6101e86105f9565b60405161018291906110ea565b6101d661069c565b60335473ffffffffffffffffffffffffffffffffffffffff1661019e565b61022e61022936600461113b565b6106b0565b6040805160ff909416845267ffffffffffffffff909216602084015273ffffffffffffffffffffffffffffffffffffffff1690820152606001610182565b61027f61027a366004610fda565b610712565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835267ffffffffffffffff909116602083015201610182565b6101d66102c2366004611154565b61079a565b61019e6102d5366004611178565b60656020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6101d661030b366004611154565b610936565b60ff841660009081526065602052604081205473ffffffffffffffffffffffffffffffffffffffff168061037a576040517f44265d6f00000000000000000000000000000000000000000000000000000000815260ff871660048201526024015b60405180910390fd5b6103dd85858560405160200161039293929190611193565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905273ffffffffffffffffffffffffffffffffffffffff831690610a09565b91508173ffffffffffffffffffffffffffffffffffffffff16638129fc1c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561042757600080fd5b505af115801561043b573d6000803e3d6000fd5b505050506000610482878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506100e792505050565b600081815260666020526040902054909150156104ce576040517f014f6fe500000000000000000000000000000000000000000000000000000000815260048101829052602401610371565b60004260b81b60f889901b178417600083815260666020526040808220839055606780546001810182559083527f9787eeb91fe3101235e4a76063c7023ecb40f923f97916639c598592fa30d6ae0183905551919250889160ff8b169173ffffffffffffffffffffffffffffffffffffffff8816917ffad0599ff449d8d9685eadecca8cb9e00924c5fd8367c1c09469824939e1ffec9190a4505050949350505050565b61057a610b3d565b60ff821660008181526065602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8616908117909155905190917f623713f72f6e427a8044bb8b3bd6834357cf285decbaa21bcc73c1d0632c4d8491a35050565b60606106247f0000000000000000000000000000000000000000000000000000000000000000610bbe565b61064d7f0000000000000000000000000000000000000000000000000000000000000000610bbe565b6106767f0000000000000000000000000000000000000000000000000000000000000000610bbe565b604051602001610688939291906111ad565b604051602081830303815290604052905090565b6106a4610b3d565b6106ae6000610cfb565b565b6000806000610705606785815481106106cb576106cb611223565b906000526020600020015460f881901c9167ffffffffffffffff60b883901c169173ffffffffffffffffffffffffffffffffffffffff1690565b9196909550909350915050565b6000806000610758878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506100e792505050565b60009081526066602052604090205473ffffffffffffffffffffffffffffffffffffffff81169860b89190911c67ffffffffffffffff16975095505050505050565b600054610100900460ff16158080156107ba5750600054600160ff909116105b806107d45750303b1580156107d4575060005460ff166001145b610860576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610371565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156108be57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6108c6610d72565b6108cf82610cfb565b801561093257600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b61093e610b3d565b73ffffffffffffffffffffffffffffffffffffffff81166109e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610371565b6109ea81610cfb565b50565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b60006002825101603f8101600a81036040518360581b8260e81b177f6100003d81600a3d39f3363d3d373d3d3d3d610000806035363936013d7300001781528660601b601e8201527f5af43d3d93803e603357fd5bf300000000000000000000000000000000000000603282015285519150603f8101602087015b60208410610ac157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09093019260209182019101610a84565b517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602085900360031b1b16815260f085901b9083015282816000f0945084610b2e577febfef1880000000000000000000000000000000000000000000000000000000060005260206000fd5b90910160405250909392505050565b60335473ffffffffffffffffffffffffffffffffffffffff1633146106ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610371565b606081600003610c0157505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115610c2b5780610c1581611281565b9150610c249050600a836112e8565b9150610c05565b60008167ffffffffffffffff811115610c4657610c46610ec2565b6040519080825280601f01601f191660200182016040528015610c70576020820181803683370190505b5090505b8415610cf357610c856001836112fc565b9150610c92600a86611313565b610c9d906030611327565b60f81b818381518110610cb257610cb2611223565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610cec600a866112e8565b9450610c74565b949350505050565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16610e09576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610371565b6106ae600054610100900460ff16610ea3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610371565b6106ae33610cfb565b803560ff81168114610ebd57600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080600060608486031215610f0657600080fd5b610f0f84610eac565b925060208401359150604084013567ffffffffffffffff80821115610f3357600080fd5b818601915086601f830112610f4757600080fd5b813581811115610f5957610f59610ec2565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715610f9f57610f9f610ec2565b81604052828152896020848701011115610fb857600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60008060008060608587031215610ff057600080fd5b610ff985610eac565b935060208501359250604085013567ffffffffffffffff8082111561101d57600080fd5b818701915087601f83011261103157600080fd5b81358181111561104057600080fd5b88602082850101111561105257600080fd5b95989497505060200194505050565b73ffffffffffffffffffffffffffffffffffffffff811681146109ea57600080fd5b6000806040838503121561109657600080fd5b61109f83610eac565b915060208301356110af81611061565b809150509250929050565b60005b838110156110d55781810151838201526020016110bd565b838111156110e4576000848401525b50505050565b60208152600082518060208401526111098160408501602087016110ba565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60006020828403121561114d57600080fd5b5035919050565b60006020828403121561116657600080fd5b813561117181611061565b9392505050565b60006020828403121561118a57600080fd5b61117182610eac565b838152818360208301376000910160200190815292915050565b600084516111bf8184602089016110ba565b80830190507f2e0000000000000000000000000000000000000000000000000000000000000080825285516111fb816001850160208a016110ba565b600192019182015283516112168160028401602088016110ba565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036112b2576112b2611252565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826112f7576112f76112b9565b500490565b60008282101561130e5761130e611252565b500390565b600082611322576113226112b9565b500690565b6000821982111561133a5761133a611252565b50019056fea164736f6c634300080f000a"
func init() { func init() {
if err := json.Unmarshal([]byte(DisputeGameFactoryStorageLayoutJSON), DisputeGameFactoryStorageLayout); err != nil { if err := json.Unmarshal([]byte(DisputeGameFactoryStorageLayoutJSON), DisputeGameFactoryStorageLayout); err != nil {
......
...@@ -15,7 +15,7 @@ var MIPSStorageLayout = new(solc.StorageLayout) ...@@ -15,7 +15,7 @@ var MIPSStorageLayout = new(solc.StorageLayout)
var MIPSDeployedBin = "0x608060405234801561001057600080fd5b50600436106100415760003560e01c8063155633fe146100465780637dc0d1d01461006b578063f8e0cb96146100af575b600080fd5b610051634000000081565b60405163ffffffff90911681526020015b60405180910390f35b60405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152602001610062565b6100c26100bd366004611bdb565b6100d0565b604051908152602001610062565b60006100da611b08565b608081146100e757600080fd5b604051610600146100f757600080fd5b6064861461010457600080fd5b610184841461011257600080fd5b8535608052602086013560a052604086013560e090811c60c09081526044880135821c82526048880135821c61010052604c880135821c610120526050880135821c61014052605488013590911c61016052605887013560f890811c610180526059880135901c6101a052605a870135901c6101c0526102006101e0819052606287019060005b60208110156101bd57823560e01c8252600490920191602090910190600101610199565b505050806101200151156101db576101d3610619565b915050610611565b6101408101805160010167ffffffffffffffff169052606081015160009061020390826106c1565b9050603f601a82901c16600281148061022257508063ffffffff166003145b156102775760006002836303ffffff1663ffffffff16901b846080015163f00000001617905061026c8263ffffffff1660021461026057601f610263565b60005b60ff168261077d565b945050505050610611565b6101608301516000908190601f601086901c81169190601587901c16602081106102a3576102a3611c47565b602002015192508063ffffffff851615806102c457508463ffffffff16601c145b156102fb578661016001518263ffffffff16602081106102e6576102e6611c47565b6020020151925050601f600b86901c166103b7565b60208563ffffffff16101561035d578463ffffffff16600c148061032557508463ffffffff16600d145b8061033657508463ffffffff16600e145b15610347578561ffff1692506103b7565b6103568661ffff166010610877565b92506103b7565b60288563ffffffff1610158061037957508463ffffffff166022145b8061038a57508463ffffffff166026145b156103b7578661016001518263ffffffff16602081106103ac576103ac611c47565b602002015192508190505b60048563ffffffff16101580156103d4575060088563ffffffff16105b806103e557508463ffffffff166001145b15610404576103f6858784876108ea565b975050505050505050610611565b63ffffffff6000602087831610610469576104248861ffff166010610877565b9095019463fffffffc861661043a8160016106c1565b915060288863ffffffff161015801561045a57508763ffffffff16603014155b1561046757809250600093505b505b600061047789888885610afa565b63ffffffff9081169150603f8a1690891615801561049c575060088163ffffffff1610155b80156104ae5750601c8163ffffffff16105b1561058a578063ffffffff16600814806104ce57508063ffffffff166009145b15610505576104f38163ffffffff166008146104ea57856104ed565b60005b8961077d565b9b505050505050505050505050610611565b8063ffffffff16600a03610525576104f3858963ffffffff8a16156111a7565b8063ffffffff16600b03610546576104f3858963ffffffff8a1615156111a7565b8063ffffffff16600c0361055c576104f361128d565b60108163ffffffff16101580156105795750601c8163ffffffff16105b1561058a576104f3818989886117c1565b8863ffffffff1660381480156105a5575063ffffffff861615155b156105da5760018b61016001518763ffffffff16602081106105c9576105c9611c47565b63ffffffff90921660209290920201525b8363ffffffff1663ffffffff146105f7576105f7846001846119bb565b610603858360016111a7565b9b5050505050505050505050505b949350505050565b60408051608051815260a051602082015260dc519181019190915260fc51604482015261011c51604882015261013c51604c82015261015c51605082015261017c51605482015261019f5160588201526101bf5160598201526101d851605a8201526000906102009060628101835b60208110156106ac57601c8401518252602090930192600490910190600101610688565b506000815281810382a0819003902092915050565b6000806106cd83611a5f565b905060038416156106dd57600080fd5b6020810190358460051c8160005b601b8110156107435760208501943583821c6001168015610713576001811461072857610739565b60008481526020839052604090209350610739565b600082815260208590526040902093505b50506001016106eb565b50608051915081811461075e57630badf00d60005260206000fd5b5050601f94909416601c0360031b9390931c63ffffffff169392505050565b6000610787611b08565b60809050806060015160040163ffffffff16816080015163ffffffff1614610810576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6a756d7020696e2064656c617920736c6f74000000000000000000000000000060448201526064015b60405180910390fd5b60608101805160808301805163ffffffff90811690935285831690529085161561086657806008018261016001518663ffffffff166020811061085557610855611c47565b63ffffffff90921660209290920201525b61086e610619565b95945050505050565b600063ffffffff8381167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80850183169190911c821615159160016020869003821681901b830191861691821b92911b01826108d45760006108d6565b815b90861663ffffffff16179250505092915050565b60006108f4611b08565b608090506000816060015160040163ffffffff16826080015163ffffffff161461097a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6272616e636820696e2064656c617920736c6f740000000000000000000000006044820152606401610807565b8663ffffffff166004148061099557508663ffffffff166005145b15610a115760008261016001518663ffffffff16602081106109b9576109b9611c47565b602002015190508063ffffffff168563ffffffff161480156109e157508763ffffffff166004145b80610a0957508063ffffffff168563ffffffff1614158015610a0957508763ffffffff166005145b915050610a8e565b8663ffffffff16600603610a2e5760008460030b13159050610a8e565b8663ffffffff16600703610a4a5760008460030b139050610a8e565b8663ffffffff16600103610a8e57601f601087901c166000819003610a735760008560030b1291505b8063ffffffff16600103610a8c5760008560030b121591505b505b606082018051608084015163ffffffff169091528115610ad4576002610ab98861ffff166010610877565b63ffffffff90811690911b8201600401166080840152610ae6565b60808301805160040163ffffffff1690525b610aee610619565b98975050505050505050565b6000603f601a86901c81169086166020821015610ec85760088263ffffffff1610158015610b2e5750600f8263ffffffff16105b15610bce578163ffffffff16600803610b4957506020610bc9565b8163ffffffff16600903610b5f57506021610bc9565b8163ffffffff16600a03610b755750602a610bc9565b8163ffffffff16600b03610b8b5750602b610bc9565b8163ffffffff16600c03610ba157506024610bc9565b8163ffffffff16600d03610bb757506025610bc9565b8163ffffffff16600e03610bc9575060265b600091505b8163ffffffff16600003610e1c57601f600688901c16602063ffffffff83161015610cf65760088263ffffffff1610610c0c57869350505050610611565b8163ffffffff16600003610c2f5763ffffffff86811691161b9250610611915050565b8163ffffffff16600203610c525763ffffffff86811691161c9250610611915050565b8163ffffffff16600303610c8657610c7c8163ffffffff168763ffffffff16901c82602003610877565b9350505050610611565b8163ffffffff16600403610ca9575050505063ffffffff8216601f84161b610611565b8163ffffffff16600603610ccc575050505063ffffffff8216601f84161c610611565b8163ffffffff16600703610cf657610c7c8763ffffffff168763ffffffff16901c88602003610877565b8163ffffffff1660201480610d1157508163ffffffff166021145b15610d23578587019350505050610611565b8163ffffffff1660221480610d3e57508163ffffffff166023145b15610d50578587039350505050610611565b8163ffffffff16602403610d6b578587169350505050610611565b8163ffffffff16602503610d86578587179350505050610611565b8163ffffffff16602603610da1578587189350505050610611565b8163ffffffff16602703610dbc575050505082821719610611565b8163ffffffff16602a03610dee578560030b8760030b12610dde576000610de1565b60015b60ff169350505050610611565b8163ffffffff16602b03610e16578563ffffffff168763ffffffff1610610dde576000610de1565b50611145565b8163ffffffff16600f03610e3e5760108563ffffffff16901b92505050610611565b8163ffffffff16601c03610ec3578063ffffffff16600203610e6557505050828202610611565b8063ffffffff1660201480610e8057508063ffffffff166021145b15610ec3578063ffffffff16602003610e97579419945b60005b6380000000871615610eb9576401fffffffe600197881b169601610e9a565b9250610611915050565b611145565b60288263ffffffff16101561102b578163ffffffff16602003610f1457610f0b8660031660080260180363ffffffff168563ffffffff16901c60ff166008610877565b92505050610611565b8163ffffffff16602103610f4957610f0b8660021660080260100363ffffffff168563ffffffff16901c61ffff166010610877565b8163ffffffff16602203610f795750505063ffffffff60086003851602811681811b198416918316901b17610611565b8163ffffffff16602303610f91578392505050610611565b8163ffffffff16602403610fc4578560031660080260180363ffffffff168463ffffffff16901c60ff1692505050610611565b8163ffffffff16602503610ff8578560021660080260100363ffffffff168463ffffffff16901c61ffff1692505050610611565b8163ffffffff16602603610ec35750505063ffffffff60086003851602601803811681811c198416918316901c17610611565b8163ffffffff166028036110625750505060ff63ffffffff60086003861602601803811682811b9091188316918416901b17610611565b8163ffffffff1660290361109a5750505061ffff63ffffffff60086002861602601003811682811b9091188316918416901b17610611565b8163ffffffff16602a036110ca5750505063ffffffff60086003851602811681811c198316918416901c17610611565b8163ffffffff16602b036110e2578492505050610611565b8163ffffffff16602e036111155750505063ffffffff60086003851602601803811681811b198316918416901b17610611565b8163ffffffff1660300361112d578392505050610611565b8163ffffffff16603803611145578492505050610611565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f696e76616c696420696e737472756374696f6e000000000000000000000000006044820152606401610807565b60006111b1611b08565b506080602063ffffffff861610611224576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f76616c69642072656769737465720000000000000000000000000000000000006044820152606401610807565b63ffffffff8516158015906112365750825b1561126a57838161016001518663ffffffff166020811061125957611259611c47565b63ffffffff90921660209290920201525b60808101805163ffffffff8082166060850152600490910116905261086e610619565b6000611297611b08565b506101e051604081015160808083015160a084015160c09094015191936000928392919063ffffffff8616610ffa036113115781610fff8116156112e057610fff811661100003015b8363ffffffff166000036113075760e08801805163ffffffff83820116909152955061130b565b8395505b50611780565b8563ffffffff16610fcd0361132c5763400000009450611780565b8563ffffffff16611018036113445760019450611780565b8563ffffffff166110960361137957600161012088015260ff831661010088015261136d610619565b97505050505050505090565b8563ffffffff16610fa3036115e35763ffffffff831615611780577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb63ffffffff84160161159d5760006113d48363fffffffc1660016106c1565b60208901519091508060001a6001036114415761143e81600090815233602052604090207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790565b90505b6040808a015190517fe03110e10000000000000000000000000000000000000000000000000000000081526004810183905263ffffffff9091166024820152600090819073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063e03110e1906044016040805180830381865afa1580156114e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115069190611c76565b9150915060038616806004038281101561151e578092505b508186101561152b578591505b8260088302610100031c9250826008828460040303021b9250600180600883600403021b036001806008858560040303021b039150811981169050838119871617955050506115828663fffffffc166001866119bb565b60408b018051820163ffffffff16905297506115de92505050565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd63ffffffff8416016115d257809450611780565b63ffffffff9450600993505b611780565b8563ffffffff16610fa4036116d45763ffffffff83166001148061160d575063ffffffff83166002145b8061161e575063ffffffff83166004145b1561162b57809450611780565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa63ffffffff8416016115d257600061166b8363fffffffc1660016106c1565b60208901519091506003841660040383811015611686578093505b83900360089081029290921c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600193850293841b0116911b17602088015260006040880152935083611780565b8563ffffffff16610fd703611780578163ffffffff166003036117745763ffffffff8316158061170a575063ffffffff83166005145b8061171b575063ffffffff83166003145b156117295760009450611780565b63ffffffff831660011480611744575063ffffffff83166002145b80611755575063ffffffff83166006145b80611766575063ffffffff83166004145b156115d25760019450611780565b63ffffffff9450601693505b6101608701805163ffffffff808816604090920191909152905185821660e09091015260808801805180831660608b0152600401909116905261136d610619565b60006117cb611b08565b506080600063ffffffff87166010036117e9575060c0810151611952565b8663ffffffff166011036118085763ffffffff861660c0830152611952565b8663ffffffff16601203611821575060a0810151611952565b8663ffffffff166013036118405763ffffffff861660a0830152611952565b8663ffffffff166018036118745763ffffffff600387810b9087900b02602081901c821660c08501521660a0830152611952565b8663ffffffff166019036118a55763ffffffff86811681871602602081901c821660c08501521660a0830152611952565b8663ffffffff16601a036118fb578460030b8660030b816118c8576118c8611c9a565b0763ffffffff1660c0830152600385810b9087900b816118ea576118ea611c9a565b0563ffffffff1660a0830152611952565b8663ffffffff16601b03611952578463ffffffff168663ffffffff168161192457611924611c9a565b0663ffffffff90811660c08401528581169087168161194557611945611c9a565b0463ffffffff1660a08301525b63ffffffff84161561198d57808261016001518563ffffffff166020811061197c5761197c611c47565b63ffffffff90921660209290920201525b60808201805163ffffffff808216606086015260049091011690526119b0610619565b979650505050505050565b60006119c683611a5f565b905060038416156119d657600080fd5b6020810190601f8516601c0360031b83811b913563ffffffff90911b1916178460051c60005b601b811015611a545760208401933582821c6001168015611a245760018114611a3957611a4a565b60008581526020839052604090209450611a4a565b600082815260208690526040902094505b50506001016119fc565b505060805250505050565b60ff811661038002610184810190369061050401811015611b02576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f636865636b207468617420746865726520697320656e6f7567682063616c6c6460448201527f61746100000000000000000000000000000000000000000000000000000000006064820152608401610807565b50919050565b6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091526101608101611b6e611b73565b905290565b6040518061040001604052806020906020820280368337509192915050565b60008083601f840112611ba457600080fd5b50813567ffffffffffffffff811115611bbc57600080fd5b602083019150836020828501011115611bd457600080fd5b9250929050565b60008060008060408587031215611bf157600080fd5b843567ffffffffffffffff80821115611c0957600080fd5b611c1588838901611b92565b90965094506020870135915080821115611c2e57600080fd5b50611c3b87828801611b92565b95989497509550505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008060408385031215611c8957600080fd5b505080516020909101519092909150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea164736f6c634300080f000a" var MIPSDeployedBin = "0x608060405234801561001057600080fd5b50600436106100415760003560e01c8063155633fe146100465780637dc0d1d01461006b578063f8e0cb96146100af575b600080fd5b610051634000000081565b60405163ffffffff90911681526020015b60405180910390f35b60405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152602001610062565b6100c26100bd366004611bdb565b6100d0565b604051908152602001610062565b60006100da611b08565b608081146100e757600080fd5b604051610600146100f757600080fd5b6064861461010457600080fd5b610184841461011257600080fd5b8535608052602086013560a052604086013560e090811c60c09081526044880135821c82526048880135821c61010052604c880135821c610120526050880135821c61014052605488013590911c61016052605887013560f890811c610180526059880135901c6101a052605a870135901c6101c0526102006101e0819052606287019060005b60208110156101bd57823560e01c8252600490920191602090910190600101610199565b505050806101200151156101db576101d3610619565b915050610611565b6101408101805160010167ffffffffffffffff169052606081015160009061020390826106c1565b9050603f601a82901c16600281148061022257508063ffffffff166003145b156102775760006002836303ffffff1663ffffffff16901b846080015163f00000001617905061026c8263ffffffff1660021461026057601f610263565b60005b60ff168261077d565b945050505050610611565b6101608301516000908190601f601086901c81169190601587901c16602081106102a3576102a3611c47565b602002015192508063ffffffff851615806102c457508463ffffffff16601c145b156102fb578661016001518263ffffffff16602081106102e6576102e6611c47565b6020020151925050601f600b86901c166103b7565b60208563ffffffff16101561035d578463ffffffff16600c148061032557508463ffffffff16600d145b8061033657508463ffffffff16600e145b15610347578561ffff1692506103b7565b6103568661ffff166010610877565b92506103b7565b60288563ffffffff1610158061037957508463ffffffff166022145b8061038a57508463ffffffff166026145b156103b7578661016001518263ffffffff16602081106103ac576103ac611c47565b602002015192508190505b60048563ffffffff16101580156103d4575060088563ffffffff16105b806103e557508463ffffffff166001145b15610404576103f6858784876108ea565b975050505050505050610611565b63ffffffff6000602087831610610469576104248861ffff166010610877565b9095019463fffffffc861661043a8160016106c1565b915060288863ffffffff161015801561045a57508763ffffffff16603014155b1561046757809250600093505b505b600061047789888885610afa565b63ffffffff9081169150603f8a1690891615801561049c575060088163ffffffff1610155b80156104ae5750601c8163ffffffff16105b1561058a578063ffffffff16600814806104ce57508063ffffffff166009145b15610505576104f38163ffffffff166008146104ea57856104ed565b60005b8961077d565b9b505050505050505050505050610611565b8063ffffffff16600a03610525576104f3858963ffffffff8a16156111a7565b8063ffffffff16600b03610546576104f3858963ffffffff8a1615156111a7565b8063ffffffff16600c0361055c576104f361128d565b60108163ffffffff16101580156105795750601c8163ffffffff16105b1561058a576104f3818989886117c1565b8863ffffffff1660381480156105a5575063ffffffff861615155b156105da5760018b61016001518763ffffffff16602081106105c9576105c9611c47565b63ffffffff90921660209290920201525b8363ffffffff1663ffffffff146105f7576105f7846001846119bb565b610603858360016111a7565b9b5050505050505050505050505b949350505050565b60408051608051815260a051602082015260dc519181019190915260fc51604482015261011c51604882015261013c51604c82015261015c51605082015261017c51605482015261019f5160588201526101bf5160598201526101d851605a8201526000906102009060628101835b60208110156106ac57601c8401518252602090930192600490910190600101610688565b506000815281810382a0819003902092915050565b6000806106cd83611a5f565b905060038416156106dd57600080fd5b6020810190358460051c8160005b601b8110156107435760208501943583821c6001168015610713576001811461072857610739565b60008481526020839052604090209350610739565b600082815260208590526040902093505b50506001016106eb565b50608051915081811461075e57630badf00d60005260206000fd5b5050601f94909416601c0360031b9390931c63ffffffff169392505050565b6000610787611b08565b60809050806060015160040163ffffffff16816080015163ffffffff1614610810576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6a756d7020696e2064656c617920736c6f74000000000000000000000000000060448201526064015b60405180910390fd5b60608101805160808301805163ffffffff90811690935285831690529085161561086657806008018261016001518663ffffffff166020811061085557610855611c47565b63ffffffff90921660209290920201525b61086e610619565b95945050505050565b600063ffffffff8381167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80850183169190911c821615159160016020869003821681901b830191861691821b92911b01826108d45760006108d6565b815b90861663ffffffff16179250505092915050565b60006108f4611b08565b608090506000816060015160040163ffffffff16826080015163ffffffff161461097a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6272616e636820696e2064656c617920736c6f740000000000000000000000006044820152606401610807565b8663ffffffff166004148061099557508663ffffffff166005145b15610a115760008261016001518663ffffffff16602081106109b9576109b9611c47565b602002015190508063ffffffff168563ffffffff161480156109e157508763ffffffff166004145b80610a0957508063ffffffff168563ffffffff1614158015610a0957508763ffffffff166005145b915050610a8e565b8663ffffffff16600603610a2e5760008460030b13159050610a8e565b8663ffffffff16600703610a4a5760008460030b139050610a8e565b8663ffffffff16600103610a8e57601f601087901c166000819003610a735760008560030b1291505b8063ffffffff16600103610a8c5760008560030b121591505b505b606082018051608084015163ffffffff169091528115610ad4576002610ab98861ffff166010610877565b63ffffffff90811690911b8201600401166080840152610ae6565b60808301805160040163ffffffff1690525b610aee610619565b98975050505050505050565b6000603f601a86901c81169086166020821015610ec85760088263ffffffff1610158015610b2e5750600f8263ffffffff16105b15610bce578163ffffffff16600803610b4957506020610bc9565b8163ffffffff16600903610b5f57506021610bc9565b8163ffffffff16600a03610b755750602a610bc9565b8163ffffffff16600b03610b8b5750602b610bc9565b8163ffffffff16600c03610ba157506024610bc9565b8163ffffffff16600d03610bb757506025610bc9565b8163ffffffff16600e03610bc9575060265b600091505b8163ffffffff16600003610e1c57601f600688901c16602063ffffffff83161015610cf65760088263ffffffff1610610c0c57869350505050610611565b8163ffffffff16600003610c2f5763ffffffff86811691161b9250610611915050565b8163ffffffff16600203610c525763ffffffff86811691161c9250610611915050565b8163ffffffff16600303610c8657610c7c8163ffffffff168763ffffffff16901c82602003610877565b9350505050610611565b8163ffffffff16600403610ca9575050505063ffffffff8216601f84161b610611565b8163ffffffff16600603610ccc575050505063ffffffff8216601f84161c610611565b8163ffffffff16600703610cf657610c7c8763ffffffff168763ffffffff16901c88602003610877565b8163ffffffff1660201480610d1157508163ffffffff166021145b15610d23578587019350505050610611565b8163ffffffff1660221480610d3e57508163ffffffff166023145b15610d50578587039350505050610611565b8163ffffffff16602403610d6b578587169350505050610611565b8163ffffffff16602503610d86578587179350505050610611565b8163ffffffff16602603610da1578587189350505050610611565b8163ffffffff16602703610dbc575050505082821719610611565b8163ffffffff16602a03610dee578560030b8760030b12610dde576000610de1565b60015b60ff169350505050610611565b8163ffffffff16602b03610e16578563ffffffff168763ffffffff1610610dde576000610de1565b50611145565b8163ffffffff16600f03610e3e5760108563ffffffff16901b92505050610611565b8163ffffffff16601c03610ec3578063ffffffff16600203610e6557505050828202610611565b8063ffffffff1660201480610e8057508063ffffffff166021145b15610ec3578063ffffffff16602003610e97579419945b60005b6380000000871615610eb9576401fffffffe600197881b169601610e9a565b9250610611915050565b611145565b60288263ffffffff16101561102b578163ffffffff16602003610f1457610f0b8660031660080260180363ffffffff168563ffffffff16901c60ff166008610877565b92505050610611565b8163ffffffff16602103610f4957610f0b8660021660080260100363ffffffff168563ffffffff16901c61ffff166010610877565b8163ffffffff16602203610f795750505063ffffffff60086003851602811681811b198416918316901b17610611565b8163ffffffff16602303610f91578392505050610611565b8163ffffffff16602403610fc4578560031660080260180363ffffffff168463ffffffff16901c60ff1692505050610611565b8163ffffffff16602503610ff8578560021660080260100363ffffffff168463ffffffff16901c61ffff1692505050610611565b8163ffffffff16602603610ec35750505063ffffffff60086003851602601803811681811c198416918316901c17610611565b8163ffffffff166028036110625750505060ff63ffffffff60086003861602601803811682811b9091188316918416901b17610611565b8163ffffffff1660290361109a5750505061ffff63ffffffff60086002861602601003811682811b9091188316918416901b17610611565b8163ffffffff16602a036110ca5750505063ffffffff60086003851602811681811c198316918416901c17610611565b8163ffffffff16602b036110e2578492505050610611565b8163ffffffff16602e036111155750505063ffffffff60086003851602601803811681811b198316918416901b17610611565b8163ffffffff1660300361112d578392505050610611565b8163ffffffff16603803611145578492505050610611565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f696e76616c696420696e737472756374696f6e000000000000000000000000006044820152606401610807565b60006111b1611b08565b506080602063ffffffff861610611224576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f76616c69642072656769737465720000000000000000000000000000000000006044820152606401610807565b63ffffffff8516158015906112365750825b1561126a57838161016001518663ffffffff166020811061125957611259611c47565b63ffffffff90921660209290920201525b60808101805163ffffffff8082166060850152600490910116905261086e610619565b6000611297611b08565b506101e051604081015160808083015160a084015160c09094015191936000928392919063ffffffff8616610ffa036113115781610fff8116156112e057610fff811661100003015b8363ffffffff166000036113075760e08801805163ffffffff83820116909152955061130b565b8395505b50611780565b8563ffffffff16610fcd0361132c5763400000009450611780565b8563ffffffff16611018036113445760019450611780565b8563ffffffff166110960361137957600161012088015260ff831661010088015261136d610619565b97505050505050505090565b8563ffffffff16610fa3036115e35763ffffffff831615611780577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb63ffffffff84160161159d5760006113d48363fffffffc1660016106c1565b60208901519091508060001a6001036114415761143e81600090815233602052604090207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790565b90505b6040808a015190517fe03110e10000000000000000000000000000000000000000000000000000000081526004810183905263ffffffff9091166024820152600090819073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063e03110e1906044016040805180830381865afa1580156114e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115069190611c76565b9150915060038616806004038281101561151e578092505b508186101561152b578591505b8260088302610100031c9250826008828460040303021b9250600180600883600403021b036001806008858560040303021b039150811981169050838119871617955050506115828663fffffffc166001866119bb565b60408b018051820163ffffffff16905297506115de92505050565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd63ffffffff8416016115d257809450611780565b63ffffffff9450600993505b611780565b8563ffffffff16610fa4036116d45763ffffffff83166001148061160d575063ffffffff83166002145b8061161e575063ffffffff83166004145b1561162b57809450611780565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa63ffffffff8416016115d257600061166b8363fffffffc1660016106c1565b60208901519091506003841660040383811015611686578093505b83900360089081029290921c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600193850293841b0116911b17602088015260006040880152935083611780565b8563ffffffff16610fd703611780578163ffffffff166003036117745763ffffffff8316158061170a575063ffffffff83166005145b8061171b575063ffffffff83166003145b156117295760009450611780565b63ffffffff831660011480611744575063ffffffff83166002145b80611755575063ffffffff83166006145b80611766575063ffffffff83166004145b156115d25760019450611780565b63ffffffff9450601693505b6101608701805163ffffffff808816604090920191909152905185821660e09091015260808801805180831660608b0152600401909116905261136d610619565b60006117cb611b08565b506080600063ffffffff87166010036117e9575060c0810151611952565b8663ffffffff166011036118085763ffffffff861660c0830152611952565b8663ffffffff16601203611821575060a0810151611952565b8663ffffffff166013036118405763ffffffff861660a0830152611952565b8663ffffffff166018036118745763ffffffff600387810b9087900b02602081901c821660c08501521660a0830152611952565b8663ffffffff166019036118a55763ffffffff86811681871602602081901c821660c08501521660a0830152611952565b8663ffffffff16601a036118fb578460030b8660030b816118c8576118c8611c9a565b0763ffffffff1660c0830152600385810b9087900b816118ea576118ea611c9a565b0563ffffffff1660a0830152611952565b8663ffffffff16601b03611952578463ffffffff168663ffffffff168161192457611924611c9a565b0663ffffffff90811660c08401528581169087168161194557611945611c9a565b0463ffffffff1660a08301525b63ffffffff84161561198d57808261016001518563ffffffff166020811061197c5761197c611c47565b63ffffffff90921660209290920201525b60808201805163ffffffff808216606086015260049091011690526119b0610619565b979650505050505050565b60006119c683611a5f565b905060038416156119d657600080fd5b6020810190601f8516601c0360031b83811b913563ffffffff90911b1916178460051c60005b601b811015611a545760208401933582821c6001168015611a245760018114611a3957611a4a565b60008581526020839052604090209450611a4a565b600082815260208690526040902094505b50506001016119fc565b505060805250505050565b60ff811661038002610184810190369061050401811015611b02576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f636865636b207468617420746865726520697320656e6f7567682063616c6c6460448201527f61746100000000000000000000000000000000000000000000000000000000006064820152608401610807565b50919050565b6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091526101608101611b6e611b73565b905290565b6040518061040001604052806020906020820280368337509192915050565b60008083601f840112611ba457600080fd5b50813567ffffffffffffffff811115611bbc57600080fd5b602083019150836020828501011115611bd457600080fd5b9250929050565b60008060008060408587031215611bf157600080fd5b843567ffffffffffffffff80821115611c0957600080fd5b611c1588838901611b92565b90965094506020870135915080821115611c2e57600080fd5b50611c3b87828801611b92565b95989497509550505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008060408385031215611c8957600080fd5b505080516020909101519092909150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea164736f6c634300080f000a"
var MIPSDeployedSourceMap = "1131:37346:105:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1710:45;;1745:10;1710:45;;;;;188:10:255;176:23;;;158:42;;146:2;131:18;1710:45:105;;;;;;;;2448:99;;;412:42:255;2534:6:105;400:55:255;382:74;;370:2;355:18;2448:99:105;211:251:255;24925:6339:105;;;;;;:::i;:::-;;:::i;:::-;;;1687:25:255;;;1675:2;1660:18;24925:6339:105;1541:177:255;24925:6339:105;25003:7;25046:18;;:::i;:::-;25193:4;25186:5;25183:15;25173:134;;25287:1;25284;25277:12;25173:134;25343:4;25337:11;25350;25334:28;25324:137;;25441:1;25438;25431:12;25324:137;25509:3;25491:16;25488:25;25478:150;;25608:1;25605;25598:12;25478:150;25672:3;25658:12;25655:21;25645:145;;25770:1;25767;25760:12;25645:145;26050:24;;26394:4;26096:20;26452:2;26154:21;;26050:24;26212:18;26096:20;26154:21;;;26050:24;26027:21;26023:52;;;26212:18;26096:20;;;26154:21;;;26050:24;26023:52;;26096:20;;26154:21;;;26050:24;26023:52;;26212:18;26096:20;26154:21;;;26050:24;26023:52;;26212:18;26096:20;26154:21;;;26050:24;26023:52;;26212:18;26096:20;26154:21;;;26050:24;26023:52;;;26212:18;26096:20;26154:21;;;26050:24;26027:21;26023:52;;;26212:18;26096:20;26154:21;;;26050:24;26023:52;;26212:18;26096:20;26154:21;;;26050:24;26023:52;;26212:18;26096:20;27070:10;26212:18;27060:21;;;26154;;;;27168:1;27153:77;27178:2;27175:1;27172:9;27153:77;;;26050:24;;26027:21;26023:52;26096:20;;27226:1;26154:21;;;;26038:2;26212:18;;;;27196:1;27189:9;27153:77;;;27157:14;;;27308:5;:12;;;27304:71;;;27347:13;:11;:13::i;:::-;27340:20;;;;;27304:71;27389:10;;;:15;;27403:1;27389:15;;;;;27474:8;;;;-1:-1:-1;;27466:20:105;;-1:-1:-1;27466:7:105;:20::i;:::-;27452:34;-1:-1:-1;27516:10:105;27524:2;27516:10;;;;27593:1;27583:11;;;:26;;;27598:6;:11;;27608:1;27598:11;27583:26;27579:310;;;27739:13;27808:1;27786:4;27793:10;27786:17;27785:24;;;;27756:5;:12;;;27771:10;27756:25;27755:54;27739:70;;27834:40;27845:6;:11;;27855:1;27845:11;:20;;27863:2;27845:20;;;27859:1;27845:20;27834:40;;27867:6;27834:10;:40::i;:::-;27827:47;;;;;;;;27579:310;28138:15;;;;27933:9;;;;28070:4;28064:2;28056:10;;;28055:19;;;28138:15;28163:2;28155:10;;;28154:19;28138:36;;;;;;;:::i;:::-;;;;;;-1:-1:-1;28203:5:105;28227:11;;;;;:29;;;28242:6;:14;;28252:4;28242:14;28227:29;28223:832;;;28319:5;:15;;;28335:5;28319:22;;;;;;;;;:::i;:::-;;;;;;-1:-1:-1;;28382:4:105;28376:2;28368:10;;;28367:19;28223:832;;;28420:4;28411:6;:13;;;28407:648;;;28541:6;:13;;28551:3;28541:13;:30;;;;28558:6;:13;;28568:3;28558:13;28541:30;:47;;;;28575:6;:13;;28585:3;28575:13;28541:47;28537:253;;;28651:4;28658:6;28651:13;28646:18;;28407:648;;28537:253;28750:21;28753:4;28760:6;28753:13;28768:2;28750;:21::i;:::-;28745:26;;28407:648;;;28824:4;28814:6;:14;;;;:32;;;;28832:6;:14;;28842:4;28832:14;28814:32;:50;;;;28850:6;:14;;28860:4;28850:14;28814:50;28810:245;;;28934:5;:15;;;28950:5;28934:22;;;;;;;;;:::i;:::-;;;;;28929:27;;29035:5;29027:13;;28810:245;29084:1;29074:6;:11;;;;:25;;;;;29098:1;29089:6;:10;;;29074:25;29073:42;;;;29104:6;:11;;29114:1;29104:11;29073:42;29069:125;;;29142:37;29155:6;29163:4;29169:5;29176:2;29142:12;:37::i;:::-;29135:44;;;;;;;;;;;29069:125;29227:13;29208:16;29379:4;29369:14;;;;29365:446;;29448:21;29451:4;29458:6;29451:13;29466:2;29448;:21::i;:::-;29442:27;;;;29506:10;29501:15;;29540:16;29501:15;29554:1;29540:7;:16::i;:::-;29534:22;;29588:4;29578:6;:14;;;;:32;;;;;29596:6;:14;;29606:4;29596:14;;29578:32;29574:223;;;29675:4;29663:16;;29777:1;29769:9;;29574:223;29385:426;29365:446;29844:10;29857:26;29865:4;29871:2;29875;29879:3;29857:7;:26::i;:::-;29886:10;29857:39;;;;-1:-1:-1;29982:4:105;29975:11;;;30014;;;:24;;;;;30037:1;30029:4;:9;;;;30014:24;:39;;;;;30049:4;30042;:11;;;30014:39;30010:847;;;30077:4;:9;;30085:1;30077:9;:22;;;;30090:4;:9;;30098:1;30090:9;30077:22;30073:144;;;30161:37;30172:4;:9;;30180:1;30172:9;:21;;30188:5;30172:21;;;30184:1;30172:21;30195:2;30161:10;:37::i;:::-;30154:44;;;;;;;;;;;;;;;30073:144;30239:4;:11;;30247:3;30239:11;30235:121;;30309:28;30318:5;30325:2;30329:7;;;;30309:8;:28::i;30235:121::-;30377:4;:11;;30385:3;30377:11;30373:121;;30447:28;30456:5;30463:2;30467:7;;;;;30447:8;:28::i;30373:121::-;30564:4;:11;;30572:3;30564:11;30560:80;;30606:15;:13;:15::i;30560:80::-;30743:4;30735;:12;;;;:27;;;;;30758:4;30751;:11;;;30735:27;30731:112;;;30793:31;30804:4;30810:2;30814;30818:5;30793:10;:31::i;30731:112::-;30917:6;:14;;30927:4;30917:14;:28;;;;-1:-1:-1;30935:10:105;;;;;30917:28;30913:93;;;30990:1;30965:5;:15;;;30981:5;30965:22;;;;;;;;;:::i;:::-;:26;;;;:22;;;;;;:26;30913:93;31052:9;:26;;31065:13;31052:26;31048:92;;31098:27;31107:9;31118:1;31121:3;31098:8;:27::i;:::-;31221:26;31230:5;31237:3;31242:4;31221:8;:26::i;:::-;31214:33;;;;;;;;;;;;;24925:6339;;;;;;;:::o;3087:1709::-;3634:4;3628:11;;3550:4;3353:31;3342:43;;3413:13;3353:31;3752:2;3452:13;;3342:43;3359:24;3353:31;3452:13;;;3342:43;;;;3359:24;3353:31;3452:13;;;3342:43;3359:24;3353:31;3452:13;;;3342:43;3359:24;3353:31;3452:13;;;3342:43;3359:24;3353:31;3452:13;;;3342:43;3359:24;3353:31;3452:13;;;3342:43;3359:24;3353:31;3452:13;;;3342:43;3359:24;3353:31;3452:13;;;3342:43;3359:24;3353:31;3452:13;;;3342:43;3128:12;;4337:13;;3452;;;3128:12;4417:84;4442:2;4439:1;4436:9;4417:84;;;3369:13;3359:24;;3353:31;3342:43;;3373:2;3413:13;;;;4497:1;3452:13;;;;4460:1;4453:9;4417:84;;;4421:14;4564:1;4560:2;4553:13;4659:5;4655:2;4651:14;4644:5;4639:27;4765:14;;;4748:32;;;3087:1709;-1:-1:-1;;3087:1709:105:o;20980:1831::-;21053:11;21164:14;21181:24;21193:11;21181;:24::i;:::-;21164:41;;21313:1;21306:5;21302:13;21299:33;;;21328:1;21325;21318:12;21299:33;21461:2;21449:15;;;21402:20;21891:5;21888:1;21884:13;21926:4;21962:1;21947:343;21972:2;21969:1;21966:9;21947:343;;;22095:2;22083:15;;;22032:20;22130:12;;;22144:1;22126:20;22167:42;;;;22235:1;22230:42;;;;22119:153;;22167:42;21625:1;21618:12;;;21658:2;21651:13;;;21703:2;21690:16;;22176:31;;22167:42;;22230;21625:1;21618:12;;;21658:2;21651:13;;;21703:2;21690:16;;22239:31;;22119:153;-1:-1:-1;;21990:1:105;21983:9;21947:343;;;21951:14;22400:4;22394:11;22379:26;;22486:7;22480:4;22477:17;22467:124;;22528:10;22525:1;22518:21;22570:2;22567:1;22560:13;22467:124;-1:-1:-1;;22718:2:105;22707:14;;;;22695:10;22691:31;22688:1;22684:39;22752:16;;;;22770:10;22748:33;;20980:1831;-1:-1:-1;;;20980:1831:105:o;18090:823::-;18159:12;18246:18;;:::i;:::-;18314:4;18305:13;;18366:5;:8;;;18377:1;18366:12;18350:28;;:5;:12;;;:28;;;18346:95;;18398:28;;;;;2114:2:255;18398:28:105;;;2096:21:255;2153:2;2133:18;;;2126:30;2192:20;2172:18;;;2165:48;2230:18;;18398:28:105;;;;;;;;18346:95;18530:8;;;;;18563:12;;;;;18552:23;;;;;;;18589:20;;;;;18530:8;18721:13;;;18717:90;;18782:6;18791:1;18782:10;18754:5;:15;;;18770:8;18754:25;;;;;;;;;:::i;:::-;:38;;;;:25;;;;;;:38;18717:90;18883:13;:11;:13::i;:::-;18876:20;18090:823;-1:-1:-1;;;;;18090:823:105:o;2645:334::-;2706:6;2765:18;;;;2774:8;;;;2765:18;;;;;;2764:25;;;;;2781:1;2828:2;:9;;;2822:16;;;;;2821:22;;2820:32;;;;;;;2882:9;;2881:15;2764:25;2939:21;;2959:1;2939:21;;;2950:6;2939:21;2924:11;;;;;:37;;-1:-1:-1;;;2645:334:105;;;;:::o;12951:2026::-;13048:12;13134:18;;:::i;:::-;13202:4;13193:13;;13234:17;13294:5;:8;;;13305:1;13294:12;13278:28;;:5;:12;;;:28;;;13274:97;;13326:30;;;;;2461:2:255;13326:30:105;;;2443:21:255;2500:2;2480:18;;;2473:30;2539:22;2519:18;;;2512:50;2579:18;;13326:30:105;2259:344:255;13274:97:105;13441:7;:12;;13452:1;13441:12;:28;;;;13457:7;:12;;13468:1;13457:12;13441:28;13437:947;;;13489:9;13501:5;:15;;;13517:6;13501:23;;;;;;;;;:::i;:::-;;;;;13489:35;;13565:2;13558:9;;:3;:9;;;:25;;;;;13571:7;:12;;13582:1;13571:12;13558:25;13557:58;;;;13596:2;13589:9;;:3;:9;;;;:25;;;;;13602:7;:12;;13613:1;13602:12;13589:25;13542:73;;13471:159;13437:947;;;13727:7;:12;;13738:1;13727:12;13723:661;;13788:1;13780:3;13774:15;;;;13759:30;;13723:661;;;13892:7;:12;;13903:1;13892:12;13888:496;;13952:1;13945:3;13939:14;;;13924:29;;13888:496;;;14073:7;:12;;14084:1;14073:12;14069:315;;14161:4;14155:2;14146:11;;;14145:20;14131:10;14188:8;;;14184:84;;14248:1;14241:3;14235:14;;;14220:29;;14184:84;14289:3;:8;;14296:1;14289:8;14285:85;;14350:1;14342:3;14336:15;;;;14321:30;;14285:85;14087:297;14069:315;14460:8;;;;;14538:12;;;;14527:23;;;;;14694:178;;;;14785:1;14759:22;14762:5;14770:6;14762:14;14778:2;14759;:22::i;:::-;:27;;;;;;;14745:42;;14754:1;14745:42;14730:57;:12;;;:57;14694:178;;;14841:12;;;;;14856:1;14841:16;14826:31;;;;14694:178;14947:13;:11;:13::i;:::-;14940:20;12951:2026;-1:-1:-1;;;;;;;;12951:2026:105:o;31310:7165::-;31397:6;31455:10;31463:2;31455:10;;;;;;31503:11;;31552:4;31543:13;;31539:6876;;;31683:1;31673:6;:11;;;;:27;;;;;31697:3;31688:6;:12;;;31673:27;31669:537;;;31728:6;:11;;31738:1;31728:11;31724:423;;-1:-1:-1;31748:4:105;31724:423;;;31792:6;:11;;31802:1;31792:11;31788:359;;-1:-1:-1;31812:4:105;31788:359;;;31857:6;:13;;31867:3;31857:13;31853:294;;-1:-1:-1;31879:4:105;31853:294;;;31923:6;:13;;31933:3;31923:13;31919:228;;-1:-1:-1;31945:4:105;31919:228;;;31990:6;:13;;32000:3;31990:13;31986:161;;-1:-1:-1;32012:4:105;31986:161;;;32056:6;:13;;32066:3;32056:13;32052:95;;-1:-1:-1;32078:4:105;32052:95;;;32121:6;:13;;32131:3;32121:13;32117:30;;-1:-1:-1;32143:4:105;32117:30;32186:1;32177:10;;31669:537;32267:6;:11;;32277:1;32267:11;32263:3554;;32331:4;32326:1;32318:9;;;32317:18;32368:4;32318:9;32361:11;;;32357:1319;;;32460:4;32452;:12;;;32448:1206;;32503:2;32496:9;;;;;;;32448:1206;32617:4;:12;;32625:4;32617:12;32613:1041;;32668:11;;;;;;;;-1:-1:-1;32661:18:105;;-1:-1:-1;;32661:18:105;32613:1041;32792:4;:12;;32800:4;32792:12;32788:866;;32843:11;;;;;;;;-1:-1:-1;32836:18:105;;-1:-1:-1;;32836:18:105;32788:866;32970:4;:12;;32978:4;32970:12;32966:688;;33021:27;33030:5;33024:11;;:2;:11;;;;33042:5;33037:2;:10;33021:2;:27::i;:::-;33014:34;;;;;;;32966:688;33170:4;:12;;33178:4;33170:12;33166:488;;-1:-1:-1;;;;33221:17:105;;;33233:4;33228:9;;33221:17;33214:24;;33166:488;33361:4;:12;;33369:4;33361:12;33357:297;;-1:-1:-1;;;;33412:17:105;;;33424:4;33419:9;;33412:17;33405:24;;33357:297;33555:4;:12;;33563:4;33555:12;33551:103;;33606:21;33615:2;33609:8;;:2;:8;;;;33624:2;33619;:7;33606:2;:21::i;33551:103::-;33836:4;:12;;33844:4;33836:12;:28;;;;33852:4;:12;;33860:4;33852:12;33836:28;33832:1151;;;33904:2;33899;:7;33892:14;;;;;;;33832:1151;33994:4;:12;;34002:4;33994:12;:28;;;;34010:4;:12;;34018:4;34010:12;33994:28;33990:993;;;34062:2;34057;:7;34050:14;;;;;;;33990:993;34144:4;:12;;34152:4;34144:12;34140:843;;34196:2;34191;:7;34184:14;;;;;;;34140:843;34277:4;:12;;34285:4;34277:12;34273:710;;34330:2;34325;:7;34317:16;;;;;;;34273:710;34413:4;:12;;34421:4;34413:12;34409:574;;34466:2;34461;:7;34453:16;;;;;;;34409:574;34549:4;:12;;34557:4;34549:12;34545:438;;-1:-1:-1;;;;34598:7:105;;;34596:10;34589:17;;34545:438;34709:4;:12;;34717:4;34709:12;34705:278;;34774:2;34756:21;;34762:2;34756:21;;;:29;;34784:1;34756:29;;;34780:1;34756:29;34749:36;;;;;;;;;34705:278;34898:4;:12;;34906:4;34898:12;34894:89;;34950:2;34945:7;;:2;:7;;;:15;;34959:1;34945:15;;34894:89;32280:2721;31539:6876;;32263:3554;35072:6;:13;;35082:3;35072:13;35068:749;;35122:2;35116;:8;;;;35109:15;;;;;;35068:749;35197:6;:14;;35207:4;35197:14;35193:624;;35266:4;:9;;35274:1;35266:9;35262:100;;-1:-1:-1;;;35317:21:105;;;35303:36;;35262:100;35414:4;:12;;35422:4;35414:12;:28;;;;35430:4;:12;;35438:4;35430:12;35414:28;35410:389;;;35474:4;:12;;35482:4;35474:12;35470:83;;35523:3;;;35470:83;35578:8;35616:127;35628:10;35623:15;;:20;35616:127;;35708:8;35675:3;35708:8;;;;;35675:3;35616:127;;;35775:1;-1:-1:-1;35768:8:105;;-1:-1:-1;;35768:8:105;35410:389;31539:6876;;;35850:4;35841:6;:13;;;35837:2578;;;35900:6;:14;;35910:4;35900:14;35896:1208;;35945:42;35963:2;35968:1;35963:6;35973:1;35962:12;35957:2;:17;35949:26;;:3;:26;;;;35979:4;35948:35;35985:1;35945:2;:42::i;:::-;35938:49;;;;;;35896:1208;36054:6;:14;;36064:4;36054:14;36050:1054;;36099:45;36117:2;36122:1;36117:6;36127:1;36116:12;36111:2;:17;36103:26;;:3;:26;;;;36133:6;36102:37;36141:2;36099;:45::i;36050:1054::-;36212:6;:14;;36222:4;36212:14;36208:896;;-1:-1:-1;;;36263:21:105;36282:1;36277;36272:6;;36271:12;36263:21;;36320:36;;;36391:5;36386:10;;36263:21;;;;;36385:18;36378:25;;36208:896;36470:6;:14;;36480:4;36470:14;36466:638;;36515:3;36508:10;;;;;;36466:638;36586:6;:14;;36596:4;36586:14;36582:522;;36646:2;36651:1;36646:6;36656:1;36645:12;36640:2;:17;36632:26;;:3;:26;;;;36662:4;36631:35;36624:42;;;;;;36582:522;36734:6;:14;;36744:4;36734:14;36730:374;;36794:2;36799:1;36794:6;36804:1;36793:12;36788:2;:17;36780:26;;:3;:26;;;;36810:6;36779:37;36772:44;;;;;;36730:374;36884:6;:14;;36894:4;36884:14;36880:224;;-1:-1:-1;;;36935:26:105;36959:1;36954;36949:6;;36948:12;36943:2;:17;36935:26;;36997:41;;;37073:5;37068:10;;36935:26;;;;;37067:18;37060:25;;35837:2578;37158:6;:14;;37168:4;37158:14;37154:1261;;-1:-1:-1;;;37211:4:105;37205:34;37237:1;37232;37227:6;;37226:12;37221:2;:17;37205:34;;37291:27;;;37271:48;;;37345:10;;37206:9;;;37205:34;;37344:18;37337:25;;37154:1261;37417:6;:14;;37427:4;37417:14;37413:1002;;-1:-1:-1;;;37470:6:105;37464:36;37498:1;37493;37488:6;;37487:12;37482:2;:17;37464:36;;37552:29;;;37532:50;;;37608:10;;37465:11;;;37464:36;;37607:18;37600:25;;37413:1002;37681:6;:14;;37691:4;37681:14;37677:738;;-1:-1:-1;;;37728:20:105;37746:1;37741;37736:6;;37735:12;37728:20;;37780:36;;;37848:5;37842:11;;37728:20;;;;;37841:19;37834:26;;37677:738;37915:6;:14;;37925:4;37915:14;37911:504;;37956:2;37949:9;;;;;;37911:504;38014:6;:14;;38024:4;38014:14;38010:405;;-1:-1:-1;;;38061:25:105;38084:1;38079;38074:6;;38073:12;38068:2;:17;38061:25;;38118:41;;;38191:5;38185:11;;38061:25;;;;;38184:19;38177:26;;38010:405;38258:6;:14;;38268:4;38258:14;38254:161;;38299:3;38292:10;;;;;;38254:161;38357:6;:14;;38367:4;38357:14;38353:62;;38398:2;38391:9;;;;;;38353:62;38429:29;;;;;2810:2:255;38429:29:105;;;2792:21:255;2849:2;2829:18;;;2822:30;2888:21;2868:18;;;2861:49;2927:18;;38429:29:105;2608:343:255;19194:782:105;19280:12;19367:18;;:::i;:::-;-1:-1:-1;19435:4:105;19542:2;19530:14;;;;19522:41;;;;;;;3158:2:255;19522:41:105;;;3140:21:255;3197:2;3177:18;;;3170:30;3236:16;3216:18;;;3209:44;3270:18;;19522:41:105;2956:338:255;19522:41:105;19659:14;;;;;;;:30;;;19677:12;19659:30;19655:102;;;19738:4;19709:5;:15;;;19725:9;19709:26;;;;;;;;;:::i;:::-;:33;;;;:26;;;;;;:33;19655:102;19812:12;;;;;19801:23;;;;:8;;;:23;19868:1;19853:16;;;19838:31;;;19946:13;:11;:13::i;4837:7728::-;4880:12;4966:18;;:::i;:::-;-1:-1:-1;5144:15:105;;:18;;;;5034:4;5304:18;;;;5348;;;;5392;;;;;5034:4;;5124:17;;;;5304:18;5348;5482;;;5496:4;5482:18;5478:6777;;5532:2;5561:4;5556:9;;:14;5552:144;;5672:4;5667:9;;5659:4;:18;5653:24;5552:144;5717:2;:7;;5723:1;5717:7;5713:161;;5753:10;;;;;5785:16;;;;;;;;5753:10;-1:-1:-1;5713:161:105;;;5853:2;5848:7;;5713:161;5502:386;5478:6777;;;5990:10;:18;;6004:4;5990:18;5986:6269;;1745:10;6028:14;;5986:6269;;;6126:10;:18;;6140:4;6126:18;6122:6133;;6169:1;6164:6;;6122:6133;;;6294:10;:18;;6308:4;6294:18;6290:5965;;6347:4;6332:12;;;:19;6369:26;;;:14;;;:26;6420:13;:11;:13::i;:::-;6413:20;;;;;;;;;4837:7728;:::o;6290:5965::-;6559:10;:18;;6573:4;6559:18;6555:5700;;6710:14;;;6706:2708;6555:5700;6706:2708;6880:22;;;;;6876:2538;;7005:10;7018:27;7026:2;7031:10;7026:15;7043:1;7018:7;:27::i;:::-;7129:17;;;;7005:40;;-1:-1:-1;7129:17:105;7107:19;7279:14;7298:1;7273:26;7269:131;;7341:36;7365:11;1277:21:106;1426:15;;;1467:8;1461:4;1454:22;1595:4;1582:18;;1602:19;1578:44;1624:11;1575:61;;1222:430;7341:36:105;7327:50;;7269:131;7486:20;;;;;7453:54;;;;;;;;3472:25:255;;;7453:54:105;3533:23:255;;;3513:18;;;3506:51;7422:11:105;;;;7453:19;:6;:19;;;;3445:18:255;;7453:54:105;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7421:86;;;;7734:1;7730:2;7726:10;7831:9;7828:1;7824:17;7913:6;7906:5;7903:17;7900:40;;;7933:5;7923:15;;7900:40;;8016:6;8012:2;8009:14;8006:34;;;8036:2;8026:12;;8006:34;8142:3;8137:1;8129:6;8125:14;8120:3;8116:24;8112:34;8105:41;;8242:3;8238:1;8226:9;8217:6;8214:1;8210:14;8206:30;8202:38;8198:48;8191:55;;8397:1;8393;8389;8377:9;8374:1;8370:17;8366:25;8362:33;8358:41;8524:1;8520;8516;8507:6;8495:9;8492:1;8488:17;8484:30;8480:38;8476:46;8472:54;8454:72;;8655:10;8651:15;8645:4;8641:26;8633:34;;8771:3;8763:4;8759:9;8754:3;8750:19;8747:28;8740:35;;;;8917:33;8926:2;8931:10;8926:15;8943:1;8946:3;8917:8;:33::i;:::-;8972:20;;;:38;;;;;;;;;-1:-1:-1;6876:2538:105;;-1:-1:-1;;;6876:2538:105;;9129:18;;;;;9125:289;;9299:2;9294:7;;6555:5700;;9125:289;9353:10;9348:15;;2053:3;9385:10;;9125:289;6555:5700;;;9543:10;:18;;9557:4;9543:18;9539:2716;;9697:15;;;1824:1;9697:15;;:34;;-1:-1:-1;9716:15:105;;;1859:1;9716:15;9697:34;:57;;;-1:-1:-1;9735:19:105;;;1936:1;9735:19;9697:57;9693:1593;;;9783:2;9778:7;;9539:2716;;9693:1593;9909:23;;;;;9905:1381;;9956:10;9969:27;9977:2;9982:10;9977:15;9994:1;9969:7;:27::i;:::-;10072:17;;;;9956:40;;-1:-1:-1;10315:1:105;10307:10;;10409:1;10405:17;10484:13;;;10481:32;;;10506:5;10500:11;;10481:32;10792:14;;;10598:1;10788:22;;;10784:32;;;;10681:26;10705:1;10590:10;;;10685:18;;;10681:26;10780:43;10586:20;;10888:12;11016:17;;;:23;11084:1;11061:20;;;:24;10594:2;-1:-1:-1;10594:2:105;6555:5700;;9539:2716;11488:10;:18;;11502:4;11488:18;11484:771;;11598:2;:7;;11604:1;11598:7;11594:647;;11691:14;;;;;:40;;-1:-1:-1;11709:22:105;;;1978:1;11709:22;11691:40;:62;;;-1:-1:-1;11735:18:105;;;1897:1;11735:18;11691:62;11687:404;;;11786:1;11781:6;;11594:647;;11687:404;11832:15;;;1824:1;11832:15;;:34;;-1:-1:-1;11851:15:105;;;1859:1;11851:15;11832:34;:61;;;-1:-1:-1;11870:23:105;;;2021:1;11870:23;11832:61;:84;;;-1:-1:-1;11897:19:105;;;1936:1;11897:19;11832:84;11828:263;;;11949:1;11944:6;;6555:5700;;11594:647;12142:10;12137:15;;2087:4;12174:11;;11594:647;12330:15;;;;;:23;;;;:18;;;;:23;;;;12367:15;;:23;;;:18;;;;:23;-1:-1:-1;12456:12:105;;;;12445:23;;;:8;;;:23;12512:1;12497:16;12482:31;;;;;12535:13;:11;:13::i;15318:2480::-;15412:12;15498:18;;:::i;:::-;-1:-1:-1;15566:4:105;15598:10;15706:13;;;15715:4;15706:13;15702:1705;;-1:-1:-1;15745:8:105;;;;15702:1705;;;15864:5;:13;;15873:4;15864:13;15860:1547;;15897:14;;;:8;;;:14;15860:1547;;;16027:5;:13;;16036:4;16027:13;16023:1384;;-1:-1:-1;16066:8:105;;;;16023:1384;;;16185:5;:13;;16194:4;16185:13;16181:1226;;16218:14;;;:8;;;:14;16181:1226;;;16359:5;:13;;16368:4;16359:13;16355:1052;;16486:9;16432:17;16412;;;16432;;;;16412:37;16493:2;16486:9;;;;;16468:8;;;:28;16514:22;:8;;;:22;16355:1052;;;16673:5;:13;;16682:4;16673:13;16669:738;;16740:11;16726;;;16740;;;16726:25;16795:2;16788:9;;;;;16770:8;;;:28;16816:22;:8;;;:22;16669:738;;;16997:5;:13;;17006:4;16997:13;16993:414;;17067:3;17048:23;;17054:3;17048:23;;;;;;;:::i;:::-;;17030:42;;:8;;;:42;17108:23;;;;;;;;;;;;;:::i;:::-;;17090:42;;:8;;;:42;16993:414;;;17301:5;:13;;17310:4;17301:13;17297:110;;17351:3;17345:9;;:3;:9;;;;;;;:::i;:::-;;17334:20;;;;:8;;;:20;17383:9;;;;;;;;;;;:::i;:::-;;17372:20;;:8;;;:20;17297:110;17500:14;;;;17496:85;;17563:3;17534:5;:15;;;17550:9;17534:26;;;;;;;;;:::i;:::-;:32;;;;:26;;;;;;:32;17496:85;17635:12;;;;;17624:23;;;;:8;;;:23;17691:1;17676:16;;;17661:31;;;17768:13;:11;:13::i;:::-;17761:20;15318:2480;-1:-1:-1;;;;;;;15318:2480:105:o;23147:1654::-;23323:14;23340:24;23352:11;23340;:24::i;:::-;23323:41;;23472:1;23465:5;23461:13;23458:33;;;23487:1;23484;23477:12;23458:33;23626:2;23820:15;;;23645:2;23634:14;;23622:10;23618:31;23615:1;23611:39;23776:16;;;23561:20;;23761:10;23750:22;;;23746:27;23736:38;23733:60;24262:5;24259:1;24255:13;24333:1;24318:343;24343:2;24340:1;24337:9;24318:343;;;24466:2;24454:15;;;24403:20;24501:12;;;24515:1;24497:20;24538:42;;;;24606:1;24601:42;;;;24490:153;;24538:42;21625:1;21618:12;;;21658:2;21651:13;;;21703:2;21690:16;;24547:31;;24538:42;;24601;21625:1;21618:12;;;21658:2;21651:13;;;21703:2;21690:16;;24610:31;;24490:153;-1:-1:-1;;24361:1:105;24354:9;24318:343;;;-1:-1:-1;;24760:4:105;24753:18;-1:-1:-1;;;;23147:1654:105:o;20180:586::-;20502:20;;;20526:7;20502:32;20495:3;:40;;;20608:14;;20663:17;;20657:24;;;20649:72;;;;;;;4209:2:255;20649:72:105;;;4191:21:255;4248:2;4228:18;;;4221:30;4287:34;4267:18;;;4260:62;4358:5;4338:18;;;4331:33;4381:19;;20649:72:105;4007:399:255;20649:72:105;20735:14;20180:586;;;:::o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;:::o;467:347:255:-;518:8;528:6;582:3;575:4;567:6;563:17;559:27;549:55;;600:1;597;590:12;549:55;-1:-1:-1;623:20:255;;666:18;655:30;;652:50;;;698:1;695;688:12;652:50;735:4;727:6;723:17;711:29;;787:3;780:4;771:6;763;759:19;755:30;752:39;749:59;;;804:1;801;794:12;749:59;467:347;;;;;:::o;819:717::-;909:6;917;925;933;986:2;974:9;965:7;961:23;957:32;954:52;;;1002:1;999;992:12;954:52;1042:9;1029:23;1071:18;1112:2;1104:6;1101:14;1098:34;;;1128:1;1125;1118:12;1098:34;1167:58;1217:7;1208:6;1197:9;1193:22;1167:58;:::i;:::-;1244:8;;-1:-1:-1;1141:84:255;-1:-1:-1;1332:2:255;1317:18;;1304:32;;-1:-1:-1;1348:16:255;;;1345:36;;;1377:1;1374;1367:12;1345:36;;1416:60;1468:7;1457:8;1446:9;1442:24;1416:60;:::i;:::-;819:717;;;;-1:-1:-1;1495:8:255;-1:-1:-1;;;;819:717:255:o;1723:184::-;1775:77;1772:1;1765:88;1872:4;1869:1;1862:15;1896:4;1893:1;1886:15;3568:245;3647:6;3655;3708:2;3696:9;3687:7;3683:23;3679:32;3676:52;;;3724:1;3721;3714:12;3676:52;-1:-1:-1;;3747:16:255;;3803:2;3788:18;;;3782:25;3747:16;;3782:25;;-1:-1:-1;3568:245:255:o;3818:184::-;3870:77;3867:1;3860:88;3967:4;3964:1;3957:15;3991:4;3988:1;3981:15" var MIPSDeployedSourceMap = "1131:37346:105:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1710:45;;1745:10;1710:45;;;;;188:10:256;176:23;;;158:42;;146:2;131:18;1710:45:105;;;;;;;;2448:99;;;412:42:256;2534:6:105;400:55:256;382:74;;370:2;355:18;2448:99:105;211:251:256;24925:6339:105;;;;;;:::i;:::-;;:::i;:::-;;;1687:25:256;;;1675:2;1660:18;24925:6339:105;1541:177:256;24925:6339:105;25003:7;25046:18;;:::i;:::-;25193:4;25186:5;25183:15;25173:134;;25287:1;25284;25277:12;25173:134;25343:4;25337:11;25350;25334:28;25324:137;;25441:1;25438;25431:12;25324:137;25509:3;25491:16;25488:25;25478:150;;25608:1;25605;25598:12;25478:150;25672:3;25658:12;25655:21;25645:145;;25770:1;25767;25760:12;25645:145;26050:24;;26394:4;26096:20;26452:2;26154:21;;26050:24;26212:18;26096:20;26154:21;;;26050:24;26027:21;26023:52;;;26212:18;26096:20;;;26154:21;;;26050:24;26023:52;;26096:20;;26154:21;;;26050:24;26023:52;;26212:18;26096:20;26154:21;;;26050:24;26023:52;;26212:18;26096:20;26154:21;;;26050:24;26023:52;;26212:18;26096:20;26154:21;;;26050:24;26023:52;;;26212:18;26096:20;26154:21;;;26050:24;26027:21;26023:52;;;26212:18;26096:20;26154:21;;;26050:24;26023:52;;26212:18;26096:20;26154:21;;;26050:24;26023:52;;26212:18;26096:20;27070:10;26212:18;27060:21;;;26154;;;;27168:1;27153:77;27178:2;27175:1;27172:9;27153:77;;;26050:24;;26027:21;26023:52;26096:20;;27226:1;26154:21;;;;26038:2;26212:18;;;;27196:1;27189:9;27153:77;;;27157:14;;;27308:5;:12;;;27304:71;;;27347:13;:11;:13::i;:::-;27340:20;;;;;27304:71;27389:10;;;:15;;27403:1;27389:15;;;;;27474:8;;;;-1:-1:-1;;27466:20:105;;-1:-1:-1;27466:7:105;:20::i;:::-;27452:34;-1:-1:-1;27516:10:105;27524:2;27516:10;;;;27593:1;27583:11;;;:26;;;27598:6;:11;;27608:1;27598:11;27583:26;27579:310;;;27739:13;27808:1;27786:4;27793:10;27786:17;27785:24;;;;27756:5;:12;;;27771:10;27756:25;27755:54;27739:70;;27834:40;27845:6;:11;;27855:1;27845:11;:20;;27863:2;27845:20;;;27859:1;27845:20;27834:40;;27867:6;27834:10;:40::i;:::-;27827:47;;;;;;;;27579:310;28138:15;;;;27933:9;;;;28070:4;28064:2;28056:10;;;28055:19;;;28138:15;28163:2;28155:10;;;28154:19;28138:36;;;;;;;:::i;:::-;;;;;;-1:-1:-1;28203:5:105;28227:11;;;;;:29;;;28242:6;:14;;28252:4;28242:14;28227:29;28223:832;;;28319:5;:15;;;28335:5;28319:22;;;;;;;;;:::i;:::-;;;;;;-1:-1:-1;;28382:4:105;28376:2;28368:10;;;28367:19;28223:832;;;28420:4;28411:6;:13;;;28407:648;;;28541:6;:13;;28551:3;28541:13;:30;;;;28558:6;:13;;28568:3;28558:13;28541:30;:47;;;;28575:6;:13;;28585:3;28575:13;28541:47;28537:253;;;28651:4;28658:6;28651:13;28646:18;;28407:648;;28537:253;28750:21;28753:4;28760:6;28753:13;28768:2;28750;:21::i;:::-;28745:26;;28407:648;;;28824:4;28814:6;:14;;;;:32;;;;28832:6;:14;;28842:4;28832:14;28814:32;:50;;;;28850:6;:14;;28860:4;28850:14;28814:50;28810:245;;;28934:5;:15;;;28950:5;28934:22;;;;;;;;;:::i;:::-;;;;;28929:27;;29035:5;29027:13;;28810:245;29084:1;29074:6;:11;;;;:25;;;;;29098:1;29089:6;:10;;;29074:25;29073:42;;;;29104:6;:11;;29114:1;29104:11;29073:42;29069:125;;;29142:37;29155:6;29163:4;29169:5;29176:2;29142:12;:37::i;:::-;29135:44;;;;;;;;;;;29069:125;29227:13;29208:16;29379:4;29369:14;;;;29365:446;;29448:21;29451:4;29458:6;29451:13;29466:2;29448;:21::i;:::-;29442:27;;;;29506:10;29501:15;;29540:16;29501:15;29554:1;29540:7;:16::i;:::-;29534:22;;29588:4;29578:6;:14;;;;:32;;;;;29596:6;:14;;29606:4;29596:14;;29578:32;29574:223;;;29675:4;29663:16;;29777:1;29769:9;;29574:223;29385:426;29365:446;29844:10;29857:26;29865:4;29871:2;29875;29879:3;29857:7;:26::i;:::-;29886:10;29857:39;;;;-1:-1:-1;29982:4:105;29975:11;;;30014;;;:24;;;;;30037:1;30029:4;:9;;;;30014:24;:39;;;;;30049:4;30042;:11;;;30014:39;30010:847;;;30077:4;:9;;30085:1;30077:9;:22;;;;30090:4;:9;;30098:1;30090:9;30077:22;30073:144;;;30161:37;30172:4;:9;;30180:1;30172:9;:21;;30188:5;30172:21;;;30184:1;30172:21;30195:2;30161:10;:37::i;:::-;30154:44;;;;;;;;;;;;;;;30073:144;30239:4;:11;;30247:3;30239:11;30235:121;;30309:28;30318:5;30325:2;30329:7;;;;30309:8;:28::i;30235:121::-;30377:4;:11;;30385:3;30377:11;30373:121;;30447:28;30456:5;30463:2;30467:7;;;;;30447:8;:28::i;30373:121::-;30564:4;:11;;30572:3;30564:11;30560:80;;30606:15;:13;:15::i;30560:80::-;30743:4;30735;:12;;;;:27;;;;;30758:4;30751;:11;;;30735:27;30731:112;;;30793:31;30804:4;30810:2;30814;30818:5;30793:10;:31::i;30731:112::-;30917:6;:14;;30927:4;30917:14;:28;;;;-1:-1:-1;30935:10:105;;;;;30917:28;30913:93;;;30990:1;30965:5;:15;;;30981:5;30965:22;;;;;;;;;:::i;:::-;:26;;;;:22;;;;;;:26;30913:93;31052:9;:26;;31065:13;31052:26;31048:92;;31098:27;31107:9;31118:1;31121:3;31098:8;:27::i;:::-;31221:26;31230:5;31237:3;31242:4;31221:8;:26::i;:::-;31214:33;;;;;;;;;;;;;24925:6339;;;;;;;:::o;3087:1709::-;3634:4;3628:11;;3550:4;3353:31;3342:43;;3413:13;3353:31;3752:2;3452:13;;3342:43;3359:24;3353:31;3452:13;;;3342:43;;;;3359:24;3353:31;3452:13;;;3342:43;3359:24;3353:31;3452:13;;;3342:43;3359:24;3353:31;3452:13;;;3342:43;3359:24;3353:31;3452:13;;;3342:43;3359:24;3353:31;3452:13;;;3342:43;3359:24;3353:31;3452:13;;;3342:43;3359:24;3353:31;3452:13;;;3342:43;3359:24;3353:31;3452:13;;;3342:43;3128:12;;4337:13;;3452;;;3128:12;4417:84;4442:2;4439:1;4436:9;4417:84;;;3369:13;3359:24;;3353:31;3342:43;;3373:2;3413:13;;;;4497:1;3452:13;;;;4460:1;4453:9;4417:84;;;4421:14;4564:1;4560:2;4553:13;4659:5;4655:2;4651:14;4644:5;4639:27;4765:14;;;4748:32;;;3087:1709;-1:-1:-1;;3087:1709:105:o;20980:1831::-;21053:11;21164:14;21181:24;21193:11;21181;:24::i;:::-;21164:41;;21313:1;21306:5;21302:13;21299:33;;;21328:1;21325;21318:12;21299:33;21461:2;21449:15;;;21402:20;21891:5;21888:1;21884:13;21926:4;21962:1;21947:343;21972:2;21969:1;21966:9;21947:343;;;22095:2;22083:15;;;22032:20;22130:12;;;22144:1;22126:20;22167:42;;;;22235:1;22230:42;;;;22119:153;;22167:42;21625:1;21618:12;;;21658:2;21651:13;;;21703:2;21690:16;;22176:31;;22167:42;;22230;21625:1;21618:12;;;21658:2;21651:13;;;21703:2;21690:16;;22239:31;;22119:153;-1:-1:-1;;21990:1:105;21983:9;21947:343;;;21951:14;22400:4;22394:11;22379:26;;22486:7;22480:4;22477:17;22467:124;;22528:10;22525:1;22518:21;22570:2;22567:1;22560:13;22467:124;-1:-1:-1;;22718:2:105;22707:14;;;;22695:10;22691:31;22688:1;22684:39;22752:16;;;;22770:10;22748:33;;20980:1831;-1:-1:-1;;;20980:1831:105:o;18090:823::-;18159:12;18246:18;;:::i;:::-;18314:4;18305:13;;18366:5;:8;;;18377:1;18366:12;18350:28;;:5;:12;;;:28;;;18346:95;;18398:28;;;;;2114:2:256;18398:28:105;;;2096:21:256;2153:2;2133:18;;;2126:30;2192:20;2172:18;;;2165:48;2230:18;;18398:28:105;;;;;;;;18346:95;18530:8;;;;;18563:12;;;;;18552:23;;;;;;;18589:20;;;;;18530:8;18721:13;;;18717:90;;18782:6;18791:1;18782:10;18754:5;:15;;;18770:8;18754:25;;;;;;;;;:::i;:::-;:38;;;;:25;;;;;;:38;18717:90;18883:13;:11;:13::i;:::-;18876:20;18090:823;-1:-1:-1;;;;;18090:823:105:o;2645:334::-;2706:6;2765:18;;;;2774:8;;;;2765:18;;;;;;2764:25;;;;;2781:1;2828:2;:9;;;2822:16;;;;;2821:22;;2820:32;;;;;;;2882:9;;2881:15;2764:25;2939:21;;2959:1;2939:21;;;2950:6;2939:21;2924:11;;;;;:37;;-1:-1:-1;;;2645:334:105;;;;:::o;12951:2026::-;13048:12;13134:18;;:::i;:::-;13202:4;13193:13;;13234:17;13294:5;:8;;;13305:1;13294:12;13278:28;;:5;:12;;;:28;;;13274:97;;13326:30;;;;;2461:2:256;13326:30:105;;;2443:21:256;2500:2;2480:18;;;2473:30;2539:22;2519:18;;;2512:50;2579:18;;13326:30:105;2259:344:256;13274:97:105;13441:7;:12;;13452:1;13441:12;:28;;;;13457:7;:12;;13468:1;13457:12;13441:28;13437:947;;;13489:9;13501:5;:15;;;13517:6;13501:23;;;;;;;;;:::i;:::-;;;;;13489:35;;13565:2;13558:9;;:3;:9;;;:25;;;;;13571:7;:12;;13582:1;13571:12;13558:25;13557:58;;;;13596:2;13589:9;;:3;:9;;;;:25;;;;;13602:7;:12;;13613:1;13602:12;13589:25;13542:73;;13471:159;13437:947;;;13727:7;:12;;13738:1;13727:12;13723:661;;13788:1;13780:3;13774:15;;;;13759:30;;13723:661;;;13892:7;:12;;13903:1;13892:12;13888:496;;13952:1;13945:3;13939:14;;;13924:29;;13888:496;;;14073:7;:12;;14084:1;14073:12;14069:315;;14161:4;14155:2;14146:11;;;14145:20;14131:10;14188:8;;;14184:84;;14248:1;14241:3;14235:14;;;14220:29;;14184:84;14289:3;:8;;14296:1;14289:8;14285:85;;14350:1;14342:3;14336:15;;;;14321:30;;14285:85;14087:297;14069:315;14460:8;;;;;14538:12;;;;14527:23;;;;;14694:178;;;;14785:1;14759:22;14762:5;14770:6;14762:14;14778:2;14759;:22::i;:::-;:27;;;;;;;14745:42;;14754:1;14745:42;14730:57;:12;;;:57;14694:178;;;14841:12;;;;;14856:1;14841:16;14826:31;;;;14694:178;14947:13;:11;:13::i;:::-;14940:20;12951:2026;-1:-1:-1;;;;;;;;12951:2026:105:o;31310:7165::-;31397:6;31455:10;31463:2;31455:10;;;;;;31503:11;;31552:4;31543:13;;31539:6876;;;31683:1;31673:6;:11;;;;:27;;;;;31697:3;31688:6;:12;;;31673:27;31669:537;;;31728:6;:11;;31738:1;31728:11;31724:423;;-1:-1:-1;31748:4:105;31724:423;;;31792:6;:11;;31802:1;31792:11;31788:359;;-1:-1:-1;31812:4:105;31788:359;;;31857:6;:13;;31867:3;31857:13;31853:294;;-1:-1:-1;31879:4:105;31853:294;;;31923:6;:13;;31933:3;31923:13;31919:228;;-1:-1:-1;31945:4:105;31919:228;;;31990:6;:13;;32000:3;31990:13;31986:161;;-1:-1:-1;32012:4:105;31986:161;;;32056:6;:13;;32066:3;32056:13;32052:95;;-1:-1:-1;32078:4:105;32052:95;;;32121:6;:13;;32131:3;32121:13;32117:30;;-1:-1:-1;32143:4:105;32117:30;32186:1;32177:10;;31669:537;32267:6;:11;;32277:1;32267:11;32263:3554;;32331:4;32326:1;32318:9;;;32317:18;32368:4;32318:9;32361:11;;;32357:1319;;;32460:4;32452;:12;;;32448:1206;;32503:2;32496:9;;;;;;;32448:1206;32617:4;:12;;32625:4;32617:12;32613:1041;;32668:11;;;;;;;;-1:-1:-1;32661:18:105;;-1:-1:-1;;32661:18:105;32613:1041;32792:4;:12;;32800:4;32792:12;32788:866;;32843:11;;;;;;;;-1:-1:-1;32836:18:105;;-1:-1:-1;;32836:18:105;32788:866;32970:4;:12;;32978:4;32970:12;32966:688;;33021:27;33030:5;33024:11;;:2;:11;;;;33042:5;33037:2;:10;33021:2;:27::i;:::-;33014:34;;;;;;;32966:688;33170:4;:12;;33178:4;33170:12;33166:488;;-1:-1:-1;;;;33221:17:105;;;33233:4;33228:9;;33221:17;33214:24;;33166:488;33361:4;:12;;33369:4;33361:12;33357:297;;-1:-1:-1;;;;33412:17:105;;;33424:4;33419:9;;33412:17;33405:24;;33357:297;33555:4;:12;;33563:4;33555:12;33551:103;;33606:21;33615:2;33609:8;;:2;:8;;;;33624:2;33619;:7;33606:2;:21::i;33551:103::-;33836:4;:12;;33844:4;33836:12;:28;;;;33852:4;:12;;33860:4;33852:12;33836:28;33832:1151;;;33904:2;33899;:7;33892:14;;;;;;;33832:1151;33994:4;:12;;34002:4;33994:12;:28;;;;34010:4;:12;;34018:4;34010:12;33994:28;33990:993;;;34062:2;34057;:7;34050:14;;;;;;;33990:993;34144:4;:12;;34152:4;34144:12;34140:843;;34196:2;34191;:7;34184:14;;;;;;;34140:843;34277:4;:12;;34285:4;34277:12;34273:710;;34330:2;34325;:7;34317:16;;;;;;;34273:710;34413:4;:12;;34421:4;34413:12;34409:574;;34466:2;34461;:7;34453:16;;;;;;;34409:574;34549:4;:12;;34557:4;34549:12;34545:438;;-1:-1:-1;;;;34598:7:105;;;34596:10;34589:17;;34545:438;34709:4;:12;;34717:4;34709:12;34705:278;;34774:2;34756:21;;34762:2;34756:21;;;:29;;34784:1;34756:29;;;34780:1;34756:29;34749:36;;;;;;;;;34705:278;34898:4;:12;;34906:4;34898:12;34894:89;;34950:2;34945:7;;:2;:7;;;:15;;34959:1;34945:15;;34894:89;32280:2721;31539:6876;;32263:3554;35072:6;:13;;35082:3;35072:13;35068:749;;35122:2;35116;:8;;;;35109:15;;;;;;35068:749;35197:6;:14;;35207:4;35197:14;35193:624;;35266:4;:9;;35274:1;35266:9;35262:100;;-1:-1:-1;;;35317:21:105;;;35303:36;;35262:100;35414:4;:12;;35422:4;35414:12;:28;;;;35430:4;:12;;35438:4;35430:12;35414:28;35410:389;;;35474:4;:12;;35482:4;35474:12;35470:83;;35523:3;;;35470:83;35578:8;35616:127;35628:10;35623:15;;:20;35616:127;;35708:8;35675:3;35708:8;;;;;35675:3;35616:127;;;35775:1;-1:-1:-1;35768:8:105;;-1:-1:-1;;35768:8:105;35410:389;31539:6876;;;35850:4;35841:6;:13;;;35837:2578;;;35900:6;:14;;35910:4;35900:14;35896:1208;;35945:42;35963:2;35968:1;35963:6;35973:1;35962:12;35957:2;:17;35949:26;;:3;:26;;;;35979:4;35948:35;35985:1;35945:2;:42::i;:::-;35938:49;;;;;;35896:1208;36054:6;:14;;36064:4;36054:14;36050:1054;;36099:45;36117:2;36122:1;36117:6;36127:1;36116:12;36111:2;:17;36103:26;;:3;:26;;;;36133:6;36102:37;36141:2;36099;:45::i;36050:1054::-;36212:6;:14;;36222:4;36212:14;36208:896;;-1:-1:-1;;;36263:21:105;36282:1;36277;36272:6;;36271:12;36263:21;;36320:36;;;36391:5;36386:10;;36263:21;;;;;36385:18;36378:25;;36208:896;36470:6;:14;;36480:4;36470:14;36466:638;;36515:3;36508:10;;;;;;36466:638;36586:6;:14;;36596:4;36586:14;36582:522;;36646:2;36651:1;36646:6;36656:1;36645:12;36640:2;:17;36632:26;;:3;:26;;;;36662:4;36631:35;36624:42;;;;;;36582:522;36734:6;:14;;36744:4;36734:14;36730:374;;36794:2;36799:1;36794:6;36804:1;36793:12;36788:2;:17;36780:26;;:3;:26;;;;36810:6;36779:37;36772:44;;;;;;36730:374;36884:6;:14;;36894:4;36884:14;36880:224;;-1:-1:-1;;;36935:26:105;36959:1;36954;36949:6;;36948:12;36943:2;:17;36935:26;;36997:41;;;37073:5;37068:10;;36935:26;;;;;37067:18;37060:25;;35837:2578;37158:6;:14;;37168:4;37158:14;37154:1261;;-1:-1:-1;;;37211:4:105;37205:34;37237:1;37232;37227:6;;37226:12;37221:2;:17;37205:34;;37291:27;;;37271:48;;;37345:10;;37206:9;;;37205:34;;37344:18;37337:25;;37154:1261;37417:6;:14;;37427:4;37417:14;37413:1002;;-1:-1:-1;;;37470:6:105;37464:36;37498:1;37493;37488:6;;37487:12;37482:2;:17;37464:36;;37552:29;;;37532:50;;;37608:10;;37465:11;;;37464:36;;37607:18;37600:25;;37413:1002;37681:6;:14;;37691:4;37681:14;37677:738;;-1:-1:-1;;;37728:20:105;37746:1;37741;37736:6;;37735:12;37728:20;;37780:36;;;37848:5;37842:11;;37728:20;;;;;37841:19;37834:26;;37677:738;37915:6;:14;;37925:4;37915:14;37911:504;;37956:2;37949:9;;;;;;37911:504;38014:6;:14;;38024:4;38014:14;38010:405;;-1:-1:-1;;;38061:25:105;38084:1;38079;38074:6;;38073:12;38068:2;:17;38061:25;;38118:41;;;38191:5;38185:11;;38061:25;;;;;38184:19;38177:26;;38010:405;38258:6;:14;;38268:4;38258:14;38254:161;;38299:3;38292:10;;;;;;38254:161;38357:6;:14;;38367:4;38357:14;38353:62;;38398:2;38391:9;;;;;;38353:62;38429:29;;;;;2810:2:256;38429:29:105;;;2792:21:256;2849:2;2829:18;;;2822:30;2888:21;2868:18;;;2861:49;2927:18;;38429:29:105;2608:343:256;19194:782:105;19280:12;19367:18;;:::i;:::-;-1:-1:-1;19435:4:105;19542:2;19530:14;;;;19522:41;;;;;;;3158:2:256;19522:41:105;;;3140:21:256;3197:2;3177:18;;;3170:30;3236:16;3216:18;;;3209:44;3270:18;;19522:41:105;2956:338:256;19522:41:105;19659:14;;;;;;;:30;;;19677:12;19659:30;19655:102;;;19738:4;19709:5;:15;;;19725:9;19709:26;;;;;;;;;:::i;:::-;:33;;;;:26;;;;;;:33;19655:102;19812:12;;;;;19801:23;;;;:8;;;:23;19868:1;19853:16;;;19838:31;;;19946:13;:11;:13::i;4837:7728::-;4880:12;4966:18;;:::i;:::-;-1:-1:-1;5144:15:105;;:18;;;;5034:4;5304:18;;;;5348;;;;5392;;;;;5034:4;;5124:17;;;;5304:18;5348;5482;;;5496:4;5482:18;5478:6777;;5532:2;5561:4;5556:9;;:14;5552:144;;5672:4;5667:9;;5659:4;:18;5653:24;5552:144;5717:2;:7;;5723:1;5717:7;5713:161;;5753:10;;;;;5785:16;;;;;;;;5753:10;-1:-1:-1;5713:161:105;;;5853:2;5848:7;;5713:161;5502:386;5478:6777;;;5990:10;:18;;6004:4;5990:18;5986:6269;;1745:10;6028:14;;5986:6269;;;6126:10;:18;;6140:4;6126:18;6122:6133;;6169:1;6164:6;;6122:6133;;;6294:10;:18;;6308:4;6294:18;6290:5965;;6347:4;6332:12;;;:19;6369:26;;;:14;;;:26;6420:13;:11;:13::i;:::-;6413:20;;;;;;;;;4837:7728;:::o;6290:5965::-;6559:10;:18;;6573:4;6559:18;6555:5700;;6710:14;;;6706:2708;6555:5700;6706:2708;6880:22;;;;;6876:2538;;7005:10;7018:27;7026:2;7031:10;7026:15;7043:1;7018:7;:27::i;:::-;7129:17;;;;7005:40;;-1:-1:-1;7129:17:105;7107:19;7279:14;7298:1;7273:26;7269:131;;7341:36;7365:11;1277:21:106;1426:15;;;1467:8;1461:4;1454:22;1595:4;1582:18;;1602:19;1578:44;1624:11;1575:61;;1222:430;7341:36:105;7327:50;;7269:131;7486:20;;;;;7453:54;;;;;;;;3472:25:256;;;7453:54:105;3533:23:256;;;3513:18;;;3506:51;7422:11:105;;;;7453:19;:6;:19;;;;3445:18:256;;7453:54:105;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7421:86;;;;7734:1;7730:2;7726:10;7831:9;7828:1;7824:17;7913:6;7906:5;7903:17;7900:40;;;7933:5;7923:15;;7900:40;;8016:6;8012:2;8009:14;8006:34;;;8036:2;8026:12;;8006:34;8142:3;8137:1;8129:6;8125:14;8120:3;8116:24;8112:34;8105:41;;8242:3;8238:1;8226:9;8217:6;8214:1;8210:14;8206:30;8202:38;8198:48;8191:55;;8397:1;8393;8389;8377:9;8374:1;8370:17;8366:25;8362:33;8358:41;8524:1;8520;8516;8507:6;8495:9;8492:1;8488:17;8484:30;8480:38;8476:46;8472:54;8454:72;;8655:10;8651:15;8645:4;8641:26;8633:34;;8771:3;8763:4;8759:9;8754:3;8750:19;8747:28;8740:35;;;;8917:33;8926:2;8931:10;8926:15;8943:1;8946:3;8917:8;:33::i;:::-;8972:20;;;:38;;;;;;;;;-1:-1:-1;6876:2538:105;;-1:-1:-1;;;6876:2538:105;;9129:18;;;;;9125:289;;9299:2;9294:7;;6555:5700;;9125:289;9353:10;9348:15;;2053:3;9385:10;;9125:289;6555:5700;;;9543:10;:18;;9557:4;9543:18;9539:2716;;9697:15;;;1824:1;9697:15;;:34;;-1:-1:-1;9716:15:105;;;1859:1;9716:15;9697:34;:57;;;-1:-1:-1;9735:19:105;;;1936:1;9735:19;9697:57;9693:1593;;;9783:2;9778:7;;9539:2716;;9693:1593;9909:23;;;;;9905:1381;;9956:10;9969:27;9977:2;9982:10;9977:15;9994:1;9969:7;:27::i;:::-;10072:17;;;;9956:40;;-1:-1:-1;10315:1:105;10307:10;;10409:1;10405:17;10484:13;;;10481:32;;;10506:5;10500:11;;10481:32;10792:14;;;10598:1;10788:22;;;10784:32;;;;10681:26;10705:1;10590:10;;;10685:18;;;10681:26;10780:43;10586:20;;10888:12;11016:17;;;:23;11084:1;11061:20;;;:24;10594:2;-1:-1:-1;10594:2:105;6555:5700;;9539:2716;11488:10;:18;;11502:4;11488:18;11484:771;;11598:2;:7;;11604:1;11598:7;11594:647;;11691:14;;;;;:40;;-1:-1:-1;11709:22:105;;;1978:1;11709:22;11691:40;:62;;;-1:-1:-1;11735:18:105;;;1897:1;11735:18;11691:62;11687:404;;;11786:1;11781:6;;11594:647;;11687:404;11832:15;;;1824:1;11832:15;;:34;;-1:-1:-1;11851:15:105;;;1859:1;11851:15;11832:34;:61;;;-1:-1:-1;11870:23:105;;;2021:1;11870:23;11832:61;:84;;;-1:-1:-1;11897:19:105;;;1936:1;11897:19;11832:84;11828:263;;;11949:1;11944:6;;6555:5700;;11594:647;12142:10;12137:15;;2087:4;12174:11;;11594:647;12330:15;;;;;:23;;;;:18;;;;:23;;;;12367:15;;:23;;;:18;;;;:23;-1:-1:-1;12456:12:105;;;;12445:23;;;:8;;;:23;12512:1;12497:16;12482:31;;;;;12535:13;:11;:13::i;15318:2480::-;15412:12;15498:18;;:::i;:::-;-1:-1:-1;15566:4:105;15598:10;15706:13;;;15715:4;15706:13;15702:1705;;-1:-1:-1;15745:8:105;;;;15702:1705;;;15864:5;:13;;15873:4;15864:13;15860:1547;;15897:14;;;:8;;;:14;15860:1547;;;16027:5;:13;;16036:4;16027:13;16023:1384;;-1:-1:-1;16066:8:105;;;;16023:1384;;;16185:5;:13;;16194:4;16185:13;16181:1226;;16218:14;;;:8;;;:14;16181:1226;;;16359:5;:13;;16368:4;16359:13;16355:1052;;16486:9;16432:17;16412;;;16432;;;;16412:37;16493:2;16486:9;;;;;16468:8;;;:28;16514:22;:8;;;:22;16355:1052;;;16673:5;:13;;16682:4;16673:13;16669:738;;16740:11;16726;;;16740;;;16726:25;16795:2;16788:9;;;;;16770:8;;;:28;16816:22;:8;;;:22;16669:738;;;16997:5;:13;;17006:4;16997:13;16993:414;;17067:3;17048:23;;17054:3;17048:23;;;;;;;:::i;:::-;;17030:42;;:8;;;:42;17108:23;;;;;;;;;;;;;:::i;:::-;;17090:42;;:8;;;:42;16993:414;;;17301:5;:13;;17310:4;17301:13;17297:110;;17351:3;17345:9;;:3;:9;;;;;;;:::i;:::-;;17334:20;;;;:8;;;:20;17383:9;;;;;;;;;;;:::i;:::-;;17372:20;;:8;;;:20;17297:110;17500:14;;;;17496:85;;17563:3;17534:5;:15;;;17550:9;17534:26;;;;;;;;;:::i;:::-;:32;;;;:26;;;;;;:32;17496:85;17635:12;;;;;17624:23;;;;:8;;;:23;17691:1;17676:16;;;17661:31;;;17768:13;:11;:13::i;:::-;17761:20;15318:2480;-1:-1:-1;;;;;;;15318:2480:105:o;23147:1654::-;23323:14;23340:24;23352:11;23340;:24::i;:::-;23323:41;;23472:1;23465:5;23461:13;23458:33;;;23487:1;23484;23477:12;23458:33;23626:2;23820:15;;;23645:2;23634:14;;23622:10;23618:31;23615:1;23611:39;23776:16;;;23561:20;;23761:10;23750:22;;;23746:27;23736:38;23733:60;24262:5;24259:1;24255:13;24333:1;24318:343;24343:2;24340:1;24337:9;24318:343;;;24466:2;24454:15;;;24403:20;24501:12;;;24515:1;24497:20;24538:42;;;;24606:1;24601:42;;;;24490:153;;24538:42;21625:1;21618:12;;;21658:2;21651:13;;;21703:2;21690:16;;24547:31;;24538:42;;24601;21625:1;21618:12;;;21658:2;21651:13;;;21703:2;21690:16;;24610:31;;24490:153;-1:-1:-1;;24361:1:105;24354:9;24318:343;;;-1:-1:-1;;24760:4:105;24753:18;-1:-1:-1;;;;23147:1654:105:o;20180:586::-;20502:20;;;20526:7;20502:32;20495:3;:40;;;20608:14;;20663:17;;20657:24;;;20649:72;;;;;;;4209:2:256;20649:72:105;;;4191:21:256;4248:2;4228:18;;;4221:30;4287:34;4267:18;;;4260:62;4358:5;4338:18;;;4331:33;4381:19;;20649:72:105;4007:399:256;20649:72:105;20735:14;20180:586;;;:::o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;:::o;467:347:256:-;518:8;528:6;582:3;575:4;567:6;563:17;559:27;549:55;;600:1;597;590:12;549:55;-1:-1:-1;623:20:256;;666:18;655:30;;652:50;;;698:1;695;688:12;652:50;735:4;727:6;723:17;711:29;;787:3;780:4;771:6;763;759:19;755:30;752:39;749:59;;;804:1;801;794:12;749:59;467:347;;;;;:::o;819:717::-;909:6;917;925;933;986:2;974:9;965:7;961:23;957:32;954:52;;;1002:1;999;992:12;954:52;1042:9;1029:23;1071:18;1112:2;1104:6;1101:14;1098:34;;;1128:1;1125;1118:12;1098:34;1167:58;1217:7;1208:6;1197:9;1193:22;1167:58;:::i;:::-;1244:8;;-1:-1:-1;1141:84:256;-1:-1:-1;1332:2:256;1317:18;;1304:32;;-1:-1:-1;1348:16:256;;;1345:36;;;1377:1;1374;1367:12;1345:36;;1416:60;1468:7;1457:8;1446:9;1442:24;1416:60;:::i;:::-;819:717;;;;-1:-1:-1;1495:8:256;-1:-1:-1;;;;819:717:256:o;1723:184::-;1775:77;1772:1;1765:88;1872:4;1869:1;1862:15;1896:4;1893:1;1886:15;3568:245;3647:6;3655;3708:2;3696:9;3687:7;3683:23;3679:32;3676:52;;;3724:1;3721;3714:12;3676:52;-1:-1:-1;;3747:16:256;;3803:2;3788:18;;;3782:25;3747:16;;3782:25;;-1:-1:-1;3568:245:256:o;3818:184::-;3870:77;3867:1;3860:88;3967:4;3964:1;3957:15;3991:4;3988:1;3981:15"
func init() { func init() {
if err := json.Unmarshal([]byte(MIPSStorageLayoutJSON), MIPSStorageLayout); err != nil { if err := json.Unmarshal([]byte(MIPSStorageLayoutJSON), MIPSStorageLayout); err != nil {
......
...@@ -15,7 +15,7 @@ var PreimageOracleStorageLayout = new(solc.StorageLayout) ...@@ -15,7 +15,7 @@ var PreimageOracleStorageLayout = new(solc.StorageLayout)
var PreimageOracleDeployedBin = "0x608060405234801561001057600080fd5b506004361061007d5760003560e01c8063e03110e11161005b578063e03110e114610111578063e159261114610139578063fe4ac08e1461014e578063fef2b4ed146101c357600080fd5b806361238bde146100825780638542cf50146100c05780639a1f5e7f146100fe575b600080fd5b6100ad610090366004610551565b600160209081526000928352604080842090915290825290205481565b6040519081526020015b60405180910390f35b6100ee6100ce366004610551565b600260209081526000928352604080842090915290825290205460ff1681565b60405190151581526020016100b7565b6100ad61010c366004610573565b6101e3565b61012461011f366004610551565b6102b6565b604080519283526020830191909152016100b7565b61014c6101473660046105a5565b6103a7565b005b61014c61015c366004610573565b6000838152600260209081526040808320878452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081179091558684528252808320968352958152858220939093559283529082905291902055565b6100ad6101d1366004610621565b60006020819052908152604090205481565b60006101ee856104b0565b90506101fb836008610669565b8211806102085750602083115b1561023f576040517ffe25498700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000602081815260c085901b82526008959095528251828252600286526040808320858452875280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915584845287528083209483529386528382205581815293849052922055919050565b6000828152600260209081526040808320848452909152812054819060ff1661033f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f7072652d696d616765206d757374206578697374000000000000000000000000604482015260640160405180910390fd5b506000838152602081815260409091205461035b816008610669565b610366856020610669565b106103845783610377826008610669565b6103819190610681565b91505b506000938452600160209081526040808620948652939052919092205492909150565b604435600080600883018611156103c65763fe2549876000526004601cfd5b60c083901b6080526088838682378087017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80151908490207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f02000000000000000000000000000000000000000000000000000000000000001760008181526002602090815260408083208b8452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915584845282528083209a83529981528982209390935590815290819052959095209190915550505050565b7f01000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82161761054b81600090815233602052604090207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790565b92915050565b6000806040838503121561056457600080fd5b50508035926020909101359150565b6000806000806080858703121561058957600080fd5b5050823594602084013594506040840135936060013592509050565b6000806000604084860312156105ba57600080fd5b83359250602084013567ffffffffffffffff808211156105d957600080fd5b818601915086601f8301126105ed57600080fd5b8135818111156105fc57600080fd5b87602082850101111561060e57600080fd5b6020830194508093505050509250925092565b60006020828403121561063357600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000821982111561067c5761067c61063a565b500190565b6000828210156106935761069361063a565b50039056fea164736f6c634300080f000a" var PreimageOracleDeployedBin = "0x608060405234801561001057600080fd5b506004361061007d5760003560e01c8063e03110e11161005b578063e03110e114610111578063e159261114610139578063fe4ac08e1461014e578063fef2b4ed146101c357600080fd5b806361238bde146100825780638542cf50146100c05780639a1f5e7f146100fe575b600080fd5b6100ad610090366004610551565b600160209081526000928352604080842090915290825290205481565b6040519081526020015b60405180910390f35b6100ee6100ce366004610551565b600260209081526000928352604080842090915290825290205460ff1681565b60405190151581526020016100b7565b6100ad61010c366004610573565b6101e3565b61012461011f366004610551565b6102b6565b604080519283526020830191909152016100b7565b61014c6101473660046105a5565b6103a7565b005b61014c61015c366004610573565b6000838152600260209081526040808320878452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081179091558684528252808320968352958152858220939093559283529082905291902055565b6100ad6101d1366004610621565b60006020819052908152604090205481565b60006101ee856104b0565b90506101fb836008610669565b8211806102085750602083115b1561023f576040517ffe25498700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000602081815260c085901b82526008959095528251828252600286526040808320858452875280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915584845287528083209483529386528382205581815293849052922055919050565b6000828152600260209081526040808320848452909152812054819060ff1661033f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f7072652d696d616765206d757374206578697374000000000000000000000000604482015260640160405180910390fd5b506000838152602081815260409091205461035b816008610669565b610366856020610669565b106103845783610377826008610669565b6103819190610681565b91505b506000938452600160209081526040808620948652939052919092205492909150565b604435600080600883018611156103c65763fe2549876000526004601cfd5b60c083901b6080526088838682378087017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80151908490207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f02000000000000000000000000000000000000000000000000000000000000001760008181526002602090815260408083208b8452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915584845282528083209a83529981528982209390935590815290819052959095209190915550505050565b7f01000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82161761054b81600090815233602052604090207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790565b92915050565b6000806040838503121561056457600080fd5b50508035926020909101359150565b6000806000806080858703121561058957600080fd5b5050823594602084013594506040840135936060013592509050565b6000806000604084860312156105ba57600080fd5b83359250602084013567ffffffffffffffff808211156105d957600080fd5b818601915086601f8301126105ed57600080fd5b8135818111156105fc57600080fd5b87602082850101111561060e57600080fd5b6020830194508093505050509250925092565b60006020828403121561063357600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000821982111561067c5761067c61063a565b500190565b6000828210156106935761069361063a565b50039056fea164736f6c634300080f000a"
var PreimageOracleDeployedSourceMap = "306:4436:107:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;537:68;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;413:25:255;;;401:2;386:18;537:68:107;;;;;;;;680:66;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;614:14:255;;607:22;589:41;;577:2;562:18;680:66:107;449:187:255;1938:1165:107;;;;;;:::i;:::-;;:::i;789:536::-;;;;;;:::i;:::-;;:::i;:::-;;;;1205:25:255;;;1261:2;1246:18;;1239:34;;;;1178:18;789:536:107;1031:248:255;3145:1595:107;;;;;;:::i;:::-;;:::i;:::-;;1672:224;;;;;;:::i;:::-;1767:19;;;;:14;:19;;;;;;;;:31;;;;;;;;:38;;;;1801:4;1767:38;;;;;;1815:18;;;;;;;;:30;;;;;;;;;:37;;;;1862:20;;;;;;;;;;:27;1672:224;419:50;;;;;;:::i;:::-;;;;;;;;;;;;;;;1938:1165;2100:12;2205:36;2234:6;2205:28;:36::i;:::-;2198:43;-1:-1:-1;2335:9:107;:5;2343:1;2335:9;:::i;:::-;2321:11;:23;:37;;;;2356:2;2348:5;:10;2321:37;2317:90;;;2381:15;;;;;;;;;;;;;;2317:90;2476:12;2576:4;2569:18;;;2677:3;2673:15;;;2660:29;;2709:4;2702:19;;;;2811:18;;2901:20;;;:14;:20;;;;;;:33;;;;;;;;:40;;;;2937:4;2901:40;;;;;;2951:19;;;;;;;;:32;;;;;;;;;:39;3067:21;;;;;;;;;:29;2916:4;1938:1165;-1:-1:-1;1938:1165:107:o;789:536::-;865:12;914:20;;;:14;:20;;;;;;;;:29;;;;;;;;;865:12;;914:29;;906:62;;;;;;;3229:2:255;906:62:107;;;3211:21:255;3268:2;3248:18;;;3241:30;3307:22;3287:18;;;3280:50;3347:18;;906:62:107;;;;;;;;-1:-1:-1;1099:14:107;1116:21;;;1087:2;1116:21;;;;;;;;1167:10;1116:21;1176:1;1167:10;:::i;:::-;1151:12;:7;1161:2;1151:12;:::i;:::-;:26;1147:87;;1216:7;1203:10;:6;1212:1;1203:10;:::i;:::-;:20;;;;:::i;:::-;1193:30;;1147:87;-1:-1:-1;1290:19:107;;;;:13;:19;;;;;;;;:28;;;;;;;;;;;;789:536;;-1:-1:-1;789:536:107:o;3145:1595::-;3441:4;3428:18;3246:12;;3570:1;3560:12;;3544:29;;3541:210;;;3645:10;3642:1;3635:21;3735:1;3729:4;3722:15;3541:210;3994:3;3990:14;;;3894:4;3978:27;4025:11;3999:4;4144:16;4025:11;4126:41;4357:29;;;4361:11;4357:29;4351:36;4409:20;;;;4556:19;4549:27;4578:11;4546:44;4609:19;;;;4587:1;4609:19;;;;;;;;:32;;;;;;;;:39;;;;4644:4;4609:39;;;;;;4658:18;;;;;;;;:31;;;;;;;;;:38;;;;4706:20;;;;;;;;;;;:27;;;;-1:-1:-1;;;;3145:1595:107:o;492:353:106:-;752:11;777:19;765:32;;749:49;824:14;749:49;1277:21;1426:15;;;1467:8;1461:4;1454:22;1595:4;1582:18;;1602:19;1578:44;1624:11;1575:61;;1222:430;824:14;817:21;492:353;-1:-1:-1;;492:353:106:o;14:248:255:-;82:6;90;143:2;131:9;122:7;118:23;114:32;111:52;;;159:1;156;149:12;111:52;-1:-1:-1;;182:23:255;;;252:2;237:18;;;224:32;;-1:-1:-1;14:248:255:o;641:385::-;727:6;735;743;751;804:3;792:9;783:7;779:23;775:33;772:53;;;821:1;818;811:12;772:53;-1:-1:-1;;844:23:255;;;914:2;899:18;;886:32;;-1:-1:-1;965:2:255;950:18;;937:32;;1016:2;1001:18;988:32;;-1:-1:-1;641:385:255;-1:-1:-1;641:385:255:o;1284:659::-;1363:6;1371;1379;1432:2;1420:9;1411:7;1407:23;1403:32;1400:52;;;1448:1;1445;1438:12;1400:52;1484:9;1471:23;1461:33;;1545:2;1534:9;1530:18;1517:32;1568:18;1609:2;1601:6;1598:14;1595:34;;;1625:1;1622;1615:12;1595:34;1663:6;1652:9;1648:22;1638:32;;1708:7;1701:4;1697:2;1693:13;1689:27;1679:55;;1730:1;1727;1720:12;1679:55;1770:2;1757:16;1796:2;1788:6;1785:14;1782:34;;;1812:1;1809;1802:12;1782:34;1857:7;1852:2;1843:6;1839:2;1835:15;1831:24;1828:37;1825:57;;;1878:1;1875;1868:12;1825:57;1909:2;1905;1901:11;1891:21;;1931:6;1921:16;;;;;1284:659;;;;;:::o;2338:180::-;2397:6;2450:2;2438:9;2429:7;2425:23;2421:32;2418:52;;;2466:1;2463;2456:12;2418:52;-1:-1:-1;2489:23:255;;2338:180;-1:-1:-1;2338:180:255:o;2705:184::-;2757:77;2754:1;2747:88;2854:4;2851:1;2844:15;2878:4;2875:1;2868:15;2894:128;2934:3;2965:1;2961:6;2958:1;2955:13;2952:39;;;2971:18;;:::i;:::-;-1:-1:-1;3007:9:255;;2894:128::o;3376:125::-;3416:4;3444:1;3441;3438:8;3435:34;;;3449:18;;:::i;:::-;-1:-1:-1;3486:9:255;;3376:125::o" var PreimageOracleDeployedSourceMap = "306:4436:107:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;537:68;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;413:25:256;;;401:2;386:18;537:68:107;;;;;;;;680:66;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;614:14:256;;607:22;589:41;;577:2;562:18;680:66:107;449:187:256;1938:1165:107;;;;;;:::i;:::-;;:::i;789:536::-;;;;;;:::i;:::-;;:::i;:::-;;;;1205:25:256;;;1261:2;1246:18;;1239:34;;;;1178:18;789:536:107;1031:248:256;3145:1595:107;;;;;;:::i;:::-;;:::i;:::-;;1672:224;;;;;;:::i;:::-;1767:19;;;;:14;:19;;;;;;;;:31;;;;;;;;:38;;;;1801:4;1767:38;;;;;;1815:18;;;;;;;;:30;;;;;;;;;:37;;;;1862:20;;;;;;;;;;:27;1672:224;419:50;;;;;;:::i;:::-;;;;;;;;;;;;;;;1938:1165;2100:12;2205:36;2234:6;2205:28;:36::i;:::-;2198:43;-1:-1:-1;2335:9:107;:5;2343:1;2335:9;:::i;:::-;2321:11;:23;:37;;;;2356:2;2348:5;:10;2321:37;2317:90;;;2381:15;;;;;;;;;;;;;;2317:90;2476:12;2576:4;2569:18;;;2677:3;2673:15;;;2660:29;;2709:4;2702:19;;;;2811:18;;2901:20;;;:14;:20;;;;;;:33;;;;;;;;:40;;;;2937:4;2901:40;;;;;;2951:19;;;;;;;;:32;;;;;;;;;:39;3067:21;;;;;;;;;:29;2916:4;1938:1165;-1:-1:-1;1938:1165:107:o;789:536::-;865:12;914:20;;;:14;:20;;;;;;;;:29;;;;;;;;;865:12;;914:29;;906:62;;;;;;;3229:2:256;906:62:107;;;3211:21:256;3268:2;3248:18;;;3241:30;3307:22;3287:18;;;3280:50;3347:18;;906:62:107;;;;;;;;-1:-1:-1;1099:14:107;1116:21;;;1087:2;1116:21;;;;;;;;1167:10;1116:21;1176:1;1167:10;:::i;:::-;1151:12;:7;1161:2;1151:12;:::i;:::-;:26;1147:87;;1216:7;1203:10;:6;1212:1;1203:10;:::i;:::-;:20;;;;:::i;:::-;1193:30;;1147:87;-1:-1:-1;1290:19:107;;;;:13;:19;;;;;;;;:28;;;;;;;;;;;;789:536;;-1:-1:-1;789:536:107:o;3145:1595::-;3441:4;3428:18;3246:12;;3570:1;3560:12;;3544:29;;3541:210;;;3645:10;3642:1;3635:21;3735:1;3729:4;3722:15;3541:210;3994:3;3990:14;;;3894:4;3978:27;4025:11;3999:4;4144:16;4025:11;4126:41;4357:29;;;4361:11;4357:29;4351:36;4409:20;;;;4556:19;4549:27;4578:11;4546:44;4609:19;;;;4587:1;4609:19;;;;;;;;:32;;;;;;;;:39;;;;4644:4;4609:39;;;;;;4658:18;;;;;;;;:31;;;;;;;;;:38;;;;4706:20;;;;;;;;;;;:27;;;;-1:-1:-1;;;;3145:1595:107:o;492:353:106:-;752:11;777:19;765:32;;749:49;824:14;749:49;1277:21;1426:15;;;1467:8;1461:4;1454:22;1595:4;1582:18;;1602:19;1578:44;1624:11;1575:61;;1222:430;824:14;817:21;492:353;-1:-1:-1;;492:353:106:o;14:248:256:-;82:6;90;143:2;131:9;122:7;118:23;114:32;111:52;;;159:1;156;149:12;111:52;-1:-1:-1;;182:23:256;;;252:2;237:18;;;224:32;;-1:-1:-1;14:248:256:o;641:385::-;727:6;735;743;751;804:3;792:9;783:7;779:23;775:33;772:53;;;821:1;818;811:12;772:53;-1:-1:-1;;844:23:256;;;914:2;899:18;;886:32;;-1:-1:-1;965:2:256;950:18;;937:32;;1016:2;1001:18;988:32;;-1:-1:-1;641:385:256;-1:-1:-1;641:385:256:o;1284:659::-;1363:6;1371;1379;1432:2;1420:9;1411:7;1407:23;1403:32;1400:52;;;1448:1;1445;1438:12;1400:52;1484:9;1471:23;1461:33;;1545:2;1534:9;1530:18;1517:32;1568:18;1609:2;1601:6;1598:14;1595:34;;;1625:1;1622;1615:12;1595:34;1663:6;1652:9;1648:22;1638:32;;1708:7;1701:4;1697:2;1693:13;1689:27;1679:55;;1730:1;1727;1720:12;1679:55;1770:2;1757:16;1796:2;1788:6;1785:14;1782:34;;;1812:1;1809;1802:12;1782:34;1857:7;1852:2;1843:6;1839:2;1835:15;1831:24;1828:37;1825:57;;;1878:1;1875;1868:12;1825:57;1909:2;1905;1901:11;1891:21;;1931:6;1921:16;;;;;1284:659;;;;;:::o;2338:180::-;2397:6;2450:2;2438:9;2429:7;2425:23;2421:32;2418:52;;;2466:1;2463;2456:12;2418:52;-1:-1:-1;2489:23:256;;2338:180;-1:-1:-1;2338:180:256:o;2705:184::-;2757:77;2754:1;2747:88;2854:4;2851:1;2844:15;2878:4;2875:1;2868:15;2894:128;2934:3;2965:1;2961:6;2958:1;2955:13;2952:39;;;2971:18;;:::i;:::-;-1:-1:-1;3007:9:256;;2894:128::o;3376:125::-;3416:4;3444:1;3441;3438:8;3435:34;;;3449:18;;:::i;:::-;-1:-1:-1;3486:9:256;;3376:125::o"
func init() { func init() {
if err := json.Unmarshal([]byte(PreimageOracleStorageLayoutJSON), PreimageOracleStorageLayout); err != nil { if err := json.Unmarshal([]byte(PreimageOracleStorageLayoutJSON), PreimageOracleStorageLayout); err != nil {
......
package main
import (
"os"
"path/filepath"
"strings"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum/go-ethereum/log"
)
func main() {
log.Root().SetHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(isatty.IsTerminal(os.Stderr.Fd()))))
app := &cli.App{
Name: "check-deploy-config",
Usage: "Check that a deploy config is valid",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "path",
Required: true,
Usage: "File system path to the deploy config",
},
},
Action: entrypoint,
}
if err := app.Run(os.Args); err != nil {
log.Crit("error checking deploy config", "err", err)
}
}
func entrypoint(ctx *cli.Context) error {
path := ctx.String("path")
name := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
log.Info("Checking deploy config", "name", name, "path", path)
config, err := genesis.NewDeployConfig(path)
if err != nil {
return err
}
// Check the config, no need to call `CheckAddresses()`
if err := config.Check(); err != nil {
return err
}
log.Info("Valid deploy config")
return nil
}
package genesis package genesis
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
...@@ -31,6 +32,7 @@ var ( ...@@ -31,6 +32,7 @@ var (
) )
// DeployConfig represents the deployment configuration for an OP Stack chain. // DeployConfig represents the deployment configuration for an OP Stack chain.
// It is used to deploy the L1 contracts as well as create the L2 genesis state.
type DeployConfig struct { type DeployConfig struct {
// L1StartingBlockTag is used to fill in the storage of the L1Block info predeploy. The rollup // L1StartingBlockTag is used to fill in the storage of the L1Block info predeploy. The rollup
// config script uses this to fill the L1 genesis info for the rollup. The Output oracle deploy // config script uses this to fill the L1 genesis info for the rollup. The Output oracle deploy
...@@ -172,10 +174,10 @@ type DeployConfig struct { ...@@ -172,10 +174,10 @@ type DeployConfig struct {
EIP1559Elasticity uint64 `json:"eip1559Elasticity"` EIP1559Elasticity uint64 `json:"eip1559Elasticity"`
// EIP1559Denominator is the denominator of EIP1559 base fee market. // EIP1559Denominator is the denominator of EIP1559 base fee market.
EIP1559Denominator uint64 `json:"eip1559Denominator"` EIP1559Denominator uint64 `json:"eip1559Denominator"`
// FundDevAccounts configures whether or not to fund the dev accounts. Should only be used // SystemConfigStartBlock represents the block at which the op-node should start syncing
// during devnet deployments. // from. It is an override to set this value on legacy networks where it is not set by
FundDevAccounts bool `json:"fundDevAccounts"` // default. It can be removed once all networks have this value set in their storage.
SystemConfigStartBlock uint64 `json:"systemConfigStartBlock"`
// FaultGameAbsolutePrestate is the absolute prestate of Cannon. This is computed // FaultGameAbsolutePrestate is the absolute prestate of Cannon. This is computed
// by generating a proof from the 0th -> 1st instruction and grabbing the prestate from // by generating a proof from the 0th -> 1st instruction and grabbing the prestate from
// the output JSON. All honest challengers should agree on the setup state of the program. // the output JSON. All honest challengers should agree on the setup state of the program.
...@@ -193,6 +195,9 @@ type DeployConfig struct { ...@@ -193,6 +195,9 @@ type DeployConfig struct {
// game can run for before it is ready to be resolved. Each side receives half of this value // game can run for before it is ready to be resolved. Each side receives half of this value
// on their chess clock at the inception of the dispute. // on their chess clock at the inception of the dispute.
FaultGameMaxDuration uint64 `json:"faultGameMaxDuration"` FaultGameMaxDuration uint64 `json:"faultGameMaxDuration"`
// FundDevAccounts configures whether or not to fund the dev accounts. Should only be used
// during devnet deployments.
FundDevAccounts bool `json:"fundDevAccounts"`
} }
// Copy will deeply copy the DeployConfig. This does a JSON roundtrip to copy // Copy will deeply copy the DeployConfig. This does a JSON roundtrip to copy
...@@ -293,21 +298,6 @@ func (d *DeployConfig) Check() error { ...@@ -293,21 +298,6 @@ func (d *DeployConfig) Check() error {
if d.GasPriceOracleScalar == 0 { if d.GasPriceOracleScalar == 0 {
return fmt.Errorf("%w: GasPriceOracleScalar cannot be 0", ErrInvalidDeployConfig) return fmt.Errorf("%w: GasPriceOracleScalar cannot be 0", ErrInvalidDeployConfig)
} }
if d.L1StandardBridgeProxy == (common.Address{}) {
return fmt.Errorf("%w: L1StandardBridgeProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.L1CrossDomainMessengerProxy == (common.Address{}) {
return fmt.Errorf("%w: L1CrossDomainMessengerProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.L1ERC721BridgeProxy == (common.Address{}) {
return fmt.Errorf("%w: L1ERC721BridgeProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.SystemConfigProxy == (common.Address{}) {
return fmt.Errorf("%w: SystemConfigProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.OptimismPortalProxy == (common.Address{}) {
return fmt.Errorf("%w: OptimismPortalProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.EIP1559Denominator == 0 { if d.EIP1559Denominator == 0 {
return fmt.Errorf("%w: EIP1559Denominator cannot be 0", ErrInvalidDeployConfig) return fmt.Errorf("%w: EIP1559Denominator cannot be 0", ErrInvalidDeployConfig)
} }
...@@ -343,6 +333,29 @@ func (d *DeployConfig) Check() error { ...@@ -343,6 +333,29 @@ func (d *DeployConfig) Check() error {
return nil return nil
} }
// CheckAddresses will return an error if the addresses are not set.
// These values are required to create the L2 genesis state and are present in the deploy config
// even though the deploy config is required to deploy the contracts on L1. This creates a
// circular dependency that should be resolved in the future.
func (d *DeployConfig) CheckAddresses() error {
if d.L1StandardBridgeProxy == (common.Address{}) {
return fmt.Errorf("%w: L1StandardBridgeProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.L1CrossDomainMessengerProxy == (common.Address{}) {
return fmt.Errorf("%w: L1CrossDomainMessengerProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.L1ERC721BridgeProxy == (common.Address{}) {
return fmt.Errorf("%w: L1ERC721BridgeProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.SystemConfigProxy == (common.Address{}) {
return fmt.Errorf("%w: SystemConfigProxy cannot be address(0)", ErrInvalidDeployConfig)
}
if d.OptimismPortalProxy == (common.Address{}) {
return fmt.Errorf("%w: OptimismPortalProxy cannot be address(0)", ErrInvalidDeployConfig)
}
return nil
}
// SetDeployments will merge a Deployments into a DeployConfig. // SetDeployments will merge a Deployments into a DeployConfig.
func (d *DeployConfig) SetDeployments(deployments *L1Deployments) { func (d *DeployConfig) SetDeployments(deployments *L1Deployments) {
d.L1StandardBridgeProxy = deployments.L1StandardBridgeProxy d.L1StandardBridgeProxy = deployments.L1StandardBridgeProxy
...@@ -469,8 +482,11 @@ func NewDeployConfig(path string) (*DeployConfig, error) { ...@@ -469,8 +482,11 @@ func NewDeployConfig(path string) (*DeployConfig, error) {
return nil, fmt.Errorf("deploy config at %s not found: %w", path, err) return nil, fmt.Errorf("deploy config at %s not found: %w", path, err)
} }
dec := json.NewDecoder(bytes.NewReader(file))
dec.DisallowUnknownFields()
var config DeployConfig var config DeployConfig
if err := json.Unmarshal(file, &config); err != nil { if err := dec.Decode(&config); err != nil {
return nil, fmt.Errorf("cannot unmarshal deploy config: %w", err) return nil, fmt.Errorf("cannot unmarshal deploy config: %w", err)
} }
......
...@@ -23,9 +23,10 @@ func BuildL2Genesis(config *DeployConfig, l1StartBlock *types.Block) (*core.Gene ...@@ -23,9 +23,10 @@ func BuildL2Genesis(config *DeployConfig, l1StartBlock *types.Block) (*core.Gene
if config.FundDevAccounts { if config.FundDevAccounts {
log.Info("Funding developer accounts in L2 genesis") log.Info("Funding developer accounts in L2 genesis")
FundDevAccounts(db) FundDevAccounts(db)
SetPrecompileBalances(db)
} }
SetPrecompileBalances(db)
storage, err := NewL2StorageConfig(config, l1StartBlock) storage, err := NewL2StorageConfig(config, l1StartBlock)
if err != nil { if err != nil {
return nil, err return nil, err
......
...@@ -66,6 +66,12 @@ func testBuildL2Genesis(t *testing.T, config *genesis.DeployConfig) *core.Genesi ...@@ -66,6 +66,12 @@ func testBuildL2Genesis(t *testing.T, config *genesis.DeployConfig) *core.Genesi
} }
} }
// All of the precompile addresses should be funded with a single wei
for i := 0; i < genesis.PrecompileCount; i++ {
addr := common.BytesToAddress([]byte{byte(i)})
require.Equal(t, common.Big1, gen.Alloc[addr].Balance)
}
if writeFile { if writeFile {
file, _ := json.MarshalIndent(gen, "", " ") file, _ := json.MarshalIndent(gen, "", " ")
_ = os.WriteFile("genesis.json", file, 0644) _ = os.WriteFile("genesis.json", file, 0644)
...@@ -79,7 +85,7 @@ func TestBuildL2MainnetGenesis(t *testing.T) { ...@@ -79,7 +85,7 @@ func TestBuildL2MainnetGenesis(t *testing.T) {
config.EnableGovernance = true config.EnableGovernance = true
config.FundDevAccounts = false config.FundDevAccounts = false
gen := testBuildL2Genesis(t, config) gen := testBuildL2Genesis(t, config)
require.Equal(t, 2066, len(gen.Alloc)) require.Equal(t, 2322, len(gen.Alloc))
} }
func TestBuildL2MainnetNoGovernanceGenesis(t *testing.T) { func TestBuildL2MainnetNoGovernanceGenesis(t *testing.T) {
...@@ -88,5 +94,5 @@ func TestBuildL2MainnetNoGovernanceGenesis(t *testing.T) { ...@@ -88,5 +94,5 @@ func TestBuildL2MainnetNoGovernanceGenesis(t *testing.T) {
config.EnableGovernance = false config.EnableGovernance = false
config.FundDevAccounts = false config.FundDevAccounts = false
gen := testBuildL2Genesis(t, config) gen := testBuildL2Genesis(t, config)
require.Equal(t, 2066, len(gen.Alloc)) require.Equal(t, 2322, len(gen.Alloc))
} }
...@@ -13,6 +13,11 @@ import ( ...@@ -13,6 +13,11 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
// PrecompileCount represents the number of precompile addresses
// starting from `address(0)` to PrecompileCount that are funded
// with a single wei in the genesis state.
const PrecompileCount = 256
// FundDevAccounts will fund each of the development accounts. // FundDevAccounts will fund each of the development accounts.
func FundDevAccounts(db vm.StateDB) { func FundDevAccounts(db vm.StateDB) {
for _, account := range DevAccounts { for _, account := range DevAccounts {
...@@ -52,7 +57,7 @@ func setProxies(db vm.StateDB, proxyAdminAddr common.Address, namespace *big.Int ...@@ -52,7 +57,7 @@ func setProxies(db vm.StateDB, proxyAdminAddr common.Address, namespace *big.Int
// This is an optimization to make calling them cheaper. This should only // This is an optimization to make calling them cheaper. This should only
// be used for devnets. // be used for devnets.
func SetPrecompileBalances(db vm.StateDB) { func SetPrecompileBalances(db vm.StateDB) {
for i := 0; i < 256; i++ { for i := 0; i < PrecompileCount; i++ {
addr := common.BytesToAddress([]byte{byte(i)}) addr := common.BytesToAddress([]byte{byte(i)})
db.CreateAccount(addr) db.CreateAccount(addr)
db.AddBalance(addr, common.Big1) db.AddBalance(addr, common.Big1)
......
...@@ -66,5 +66,6 @@ ...@@ -66,5 +66,6 @@
"fundDevAccounts": true, "fundDevAccounts": true,
"faultGameAbsolutePrestate": "0x0000000000000000000000000000000000000000000000000000000000000000", "faultGameAbsolutePrestate": "0x0000000000000000000000000000000000000000000000000000000000000000",
"faultGameMaxDepth": 63, "faultGameMaxDepth": 63,
"faultGameMaxDuration": 604800 "faultGameMaxDuration": 604800,
"systemConfigStartBlock": 0
} }
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
var ( var (
l1EthRpc = "http://example.com:8545" l1EthRpc = "http://example.com:8545"
gameAddressValue = "0xaa00000000000000000000000000000000000000" gameFactoryAddressValue = "0xbb00000000000000000000000000000000000000"
cannonNetwork = chaincfg.AvailableNetworks()[0] cannonNetwork = chaincfg.AvailableNetworks()[0]
otherCannonNetwork = chaincfg.AvailableNetworks()[1] otherCannonNetwork = chaincfg.AvailableNetworks()[1]
cannonBin = "./bin/cannon" cannonBin = "./bin/cannon"
...@@ -44,14 +44,14 @@ func TestLogLevel(t *testing.T) { ...@@ -44,14 +44,14 @@ func TestLogLevel(t *testing.T) {
func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) { func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) {
cfg := configForArgs(t, addRequiredArgs(config.TraceTypeAlphabet)) cfg := configForArgs(t, addRequiredArgs(config.TraceTypeAlphabet))
defaultCfg := config.NewConfig(l1EthRpc, common.HexToAddress(gameAddressValue), config.TraceTypeAlphabet, true) defaultCfg := config.NewConfig(common.HexToAddress(gameFactoryAddressValue), l1EthRpc, config.TraceTypeAlphabet, true)
// Add in the extra CLI options required when using alphabet trace type // Add in the extra CLI options required when using alphabet trace type
defaultCfg.AlphabetTrace = alphabetTrace defaultCfg.AlphabetTrace = alphabetTrace
require.Equal(t, defaultCfg, cfg) require.Equal(t, defaultCfg, cfg)
} }
func TestDefaultConfigIsValid(t *testing.T) { func TestDefaultConfigIsValid(t *testing.T) {
cfg := config.NewConfig(l1EthRpc, common.HexToAddress(gameAddressValue), config.TraceTypeAlphabet, true) cfg := config.NewConfig(common.HexToAddress(gameFactoryAddressValue), l1EthRpc, config.TraceTypeAlphabet, true)
// Add in options that are required based on the specific trace type // Add in options that are required based on the specific trace type
// To avoid needing to specify unused options, these aren't included in the params for NewConfig // To avoid needing to specify unused options, these aren't included in the params for NewConfig
cfg.AlphabetTrace = alphabetTrace cfg.AlphabetTrace = alphabetTrace
...@@ -89,9 +89,26 @@ func TestTraceType(t *testing.T) { ...@@ -89,9 +89,26 @@ func TestTraceType(t *testing.T) {
}) })
} }
func TestGameAddress(t *testing.T) { func TestGameFactoryAddress(t *testing.T) {
t.Run("Required", func(t *testing.T) { t.Run("Required", func(t *testing.T) {
verifyArgsInvalid(t, "flag game-address is required", addRequiredArgsExcept(config.TraceTypeAlphabet, "--game-address")) verifyArgsInvalid(t, "flag game-factory-address is required", addRequiredArgsExcept(config.TraceTypeAlphabet, "--game-factory-address"))
})
t.Run("Valid", func(t *testing.T) {
addr := common.Address{0xbb, 0xcc, 0xdd}
cfg := configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--game-factory-address", "--game-factory-address="+addr.Hex()))
require.Equal(t, addr, cfg.GameFactoryAddress)
})
t.Run("Invalid", func(t *testing.T) {
verifyArgsInvalid(t, "invalid address: foo", addRequiredArgsExcept(config.TraceTypeAlphabet, "--game-factory-address", "--game-factory-address=foo"))
})
}
func TestGameAddress(t *testing.T) {
t.Run("Optional", func(t *testing.T) {
cfg := configForArgs(t, addRequiredArgsExcept(config.TraceTypeAlphabet, "--game-address"))
require.NoError(t, cfg.Check())
}) })
t.Run("Valid", func(t *testing.T) { t.Run("Valid", func(t *testing.T) {
...@@ -316,7 +333,7 @@ func requiredArgs(traceType config.TraceType) map[string]string { ...@@ -316,7 +333,7 @@ func requiredArgs(traceType config.TraceType) map[string]string {
args := map[string]string{ args := map[string]string{
"--agree-with-proposed-output": agreeWithProposedOutput, "--agree-with-proposed-output": agreeWithProposedOutput,
"--l1-eth-rpc": l1EthRpc, "--l1-eth-rpc": l1EthRpc,
"--game-address": gameAddressValue, "--game-factory-address": gameFactoryAddressValue,
"--trace-type": traceType.String(), "--trace-type": traceType.String(),
} }
switch traceType { switch traceType {
......
...@@ -18,7 +18,7 @@ var ( ...@@ -18,7 +18,7 @@ var (
ErrMissingCannonAbsolutePreState = errors.New("missing cannon absolute pre-state") ErrMissingCannonAbsolutePreState = errors.New("missing cannon absolute pre-state")
ErrMissingAlphabetTrace = errors.New("missing alphabet trace") ErrMissingAlphabetTrace = errors.New("missing alphabet trace")
ErrMissingL1EthRPC = errors.New("missing l1 eth rpc url") ErrMissingL1EthRPC = errors.New("missing l1 eth rpc url")
ErrMissingGameAddress = errors.New("missing game address") ErrMissingGameFactoryAddress = errors.New("missing game factory address")
ErrMissingCannonSnapshotFreq = errors.New("missing cannon snapshot freq") ErrMissingCannonSnapshotFreq = errors.New("missing cannon snapshot freq")
ErrMissingCannonRollupConfig = errors.New("missing cannon network or rollup config path") ErrMissingCannonRollupConfig = errors.New("missing cannon network or rollup config path")
ErrMissingCannonL2Genesis = errors.New("missing cannon network or l2 genesis path") ErrMissingCannonL2Genesis = errors.New("missing cannon network or l2 genesis path")
...@@ -32,10 +32,30 @@ type TraceType string ...@@ -32,10 +32,30 @@ type TraceType string
const ( const (
TraceTypeAlphabet TraceType = "alphabet" TraceTypeAlphabet TraceType = "alphabet"
TraceTypeCannon TraceType = "cannon" TraceTypeCannon TraceType = "cannon"
// Devnet game IDs
DevnetGameIDAlphabet = uint8(0)
DevnetGameIDCannon = uint8(1)
// Mainnet game IDs
MainnetGameIDFault = uint8(0)
) )
var TraceTypes = []TraceType{TraceTypeAlphabet, TraceTypeCannon} var TraceTypes = []TraceType{TraceTypeAlphabet, TraceTypeCannon}
// GameIdToString maps game IDs to their string representation on a per-network basis.
var GameIdToString = map[uint64]map[uint8]string{
// Mainnet
1: {
MainnetGameIDFault: "fault-cannon",
},
// Devnet
900: {
DevnetGameIDAlphabet: "fault-alphabet",
DevnetGameIDCannon: "fault-cannon",
},
}
func (t TraceType) String() string { func (t TraceType) String() string {
return string(t) return string(t)
} }
...@@ -65,6 +85,7 @@ const DefaultCannonSnapshotFreq = uint(1_000_000_000) ...@@ -65,6 +85,7 @@ const DefaultCannonSnapshotFreq = uint(1_000_000_000)
// It is used to initialize the challenger. // It is used to initialize the challenger.
type Config struct { type Config struct {
L1EthRpc string // L1 RPC Url L1EthRpc string // L1 RPC Url
GameFactoryAddress common.Address // Address of the dispute game factory
GameAddress common.Address // Address of the fault game GameAddress common.Address // Address of the fault game
AgreeWithProposedOutput bool // Temporary config if we agree or disagree with the posted output AgreeWithProposedOutput bool // Temporary config if we agree or disagree with the posted output
...@@ -88,14 +109,14 @@ type Config struct { ...@@ -88,14 +109,14 @@ type Config struct {
} }
func NewConfig( func NewConfig(
gameFactoryAddress common.Address,
l1EthRpc string, l1EthRpc string,
gameAddress common.Address,
traceType TraceType, traceType TraceType,
agreeWithProposedOutput bool, agreeWithProposedOutput bool,
) Config { ) Config {
return Config{ return Config{
L1EthRpc: l1EthRpc, L1EthRpc: l1EthRpc,
GameAddress: gameAddress, GameFactoryAddress: gameFactoryAddress,
AgreeWithProposedOutput: agreeWithProposedOutput, AgreeWithProposedOutput: agreeWithProposedOutput,
...@@ -111,8 +132,8 @@ func (c Config) Check() error { ...@@ -111,8 +132,8 @@ func (c Config) Check() error {
if c.L1EthRpc == "" { if c.L1EthRpc == "" {
return ErrMissingL1EthRPC return ErrMissingL1EthRPC
} }
if c.GameAddress == (common.Address{}) { if c.GameFactoryAddress == (common.Address{}) {
return ErrMissingGameAddress return ErrMissingGameFactoryAddress
} }
if c.TraceType == "" { if c.TraceType == "" {
return ErrMissingTraceType return ErrMissingTraceType
......
...@@ -10,7 +10,7 @@ import ( ...@@ -10,7 +10,7 @@ import (
var ( var (
validL1EthRpc = "http://localhost:8545" validL1EthRpc = "http://localhost:8545"
validGameAddress = common.HexToAddress("0x7bdd3b028C4796eF0EAf07d11394d0d9d8c24139") validGameFactoryAddress = common.Address{0x23}
validAlphabetTrace = "abcdefgh" validAlphabetTrace = "abcdefgh"
validCannonBin = "./bin/cannon" validCannonBin = "./bin/cannon"
validCannonOpProgramBin = "./bin/op-program" validCannonOpProgramBin = "./bin/op-program"
...@@ -22,7 +22,7 @@ var ( ...@@ -22,7 +22,7 @@ var (
) )
func validConfig(traceType TraceType) Config { func validConfig(traceType TraceType) Config {
cfg := NewConfig(validL1EthRpc, validGameAddress, traceType, agreeWithProposedOutput) cfg := NewConfig(validGameFactoryAddress, validL1EthRpc, traceType, agreeWithProposedOutput)
switch traceType { switch traceType {
case TraceTypeAlphabet: case TraceTypeAlphabet:
cfg.AlphabetTrace = validAlphabetTrace cfg.AlphabetTrace = validAlphabetTrace
...@@ -62,10 +62,16 @@ func TestL1EthRpcRequired(t *testing.T) { ...@@ -62,10 +62,16 @@ func TestL1EthRpcRequired(t *testing.T) {
require.ErrorIs(t, config.Check(), ErrMissingL1EthRPC) require.ErrorIs(t, config.Check(), ErrMissingL1EthRPC)
} }
func TestGameAddressRequired(t *testing.T) { func TestGameFactoryAddressRequired(t *testing.T) {
config := validConfig(TraceTypeCannon)
config.GameFactoryAddress = common.Address{}
require.ErrorIs(t, config.Check(), ErrMissingGameFactoryAddress)
}
func TestGameAddressNotRequired(t *testing.T) {
config := validConfig(TraceTypeCannon) config := validConfig(TraceTypeCannon)
config.GameAddress = common.Address{} config.GameAddress = common.Address{}
require.ErrorIs(t, config.Check(), ErrMissingGameAddress) require.NoError(t, config.Check())
} }
func TestAlphabetTraceRequired(t *testing.T) { func TestAlphabetTraceRequired(t *testing.T) {
......
...@@ -21,7 +21,7 @@ const execTestCannonPrestate = "/foo/pre.json" ...@@ -21,7 +21,7 @@ const execTestCannonPrestate = "/foo/pre.json"
func TestGenerateProof(t *testing.T) { func TestGenerateProof(t *testing.T) {
input := "starting.json" input := "starting.json"
cfg := config.NewConfig("http://localhost:8888", common.Address{0xaa}, config.TraceTypeCannon, true) cfg := config.NewConfig(common.Address{0xbb}, "http://localhost:8888", config.TraceTypeCannon, true)
cfg.CannonDatadir = t.TempDir() cfg.CannonDatadir = t.TempDir()
cfg.CannonAbsolutePreState = "pre.json" cfg.CannonAbsolutePreState = "pre.json"
cfg.CannonBin = "./bin/cannon" cfg.CannonBin = "./bin/cannon"
......
...@@ -19,14 +19,16 @@ var ( ...@@ -19,14 +19,16 @@ var (
type MinimalDisputeGameFactoryCaller interface { type MinimalDisputeGameFactoryCaller interface {
GameCount(opts *bind.CallOpts) (*big.Int, error) GameCount(opts *bind.CallOpts) (*big.Int, error)
GameAtIndex(opts *bind.CallOpts, _index *big.Int) (struct { GameAtIndex(opts *bind.CallOpts, _index *big.Int) (struct {
GameType uint8
Timestamp uint64
Proxy common.Address Proxy common.Address
Timestamp *big.Int
}, error) }, error)
} }
type FaultDisputeGame struct { type FaultDisputeGame struct {
GameType uint8
Timestamp uint64
Proxy common.Address Proxy common.Address
Timestamp *big.Int
} }
// GameLoader is a minimal interface for fetching on chain dispute games. // GameLoader is a minimal interface for fetching on chain dispute games.
......
...@@ -90,7 +90,7 @@ func generateMockGames(count uint64) []FaultDisputeGame { ...@@ -90,7 +90,7 @@ func generateMockGames(count uint64) []FaultDisputeGame {
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
games[i] = FaultDisputeGame{ games[i] = FaultDisputeGame{
Proxy: common.BigToAddress(big.NewInt(int64(i))), Proxy: common.BigToAddress(big.NewInt(int64(i))),
Timestamp: big.NewInt(int64(i)), Timestamp: i,
} }
} }
...@@ -151,22 +151,26 @@ func (m *mockMinimalDisputeGameFactoryCaller) GameCount(opts *bind.CallOpts) (*b ...@@ -151,22 +151,26 @@ func (m *mockMinimalDisputeGameFactoryCaller) GameCount(opts *bind.CallOpts) (*b
} }
func (m *mockMinimalDisputeGameFactoryCaller) GameAtIndex(opts *bind.CallOpts, _index *big.Int) (struct { func (m *mockMinimalDisputeGameFactoryCaller) GameAtIndex(opts *bind.CallOpts, _index *big.Int) (struct {
GameType uint8
Timestamp uint64
Proxy common.Address Proxy common.Address
Timestamp *big.Int
}, error) { }, error) {
index := _index.Uint64() index := _index.Uint64()
if m.indexErrors[index] { if m.indexErrors[index] {
return struct { return struct {
GameType uint8
Timestamp uint64
Proxy common.Address Proxy common.Address
Timestamp *big.Int
}{}, gameIndexErr }{}, gameIndexErr
} }
return struct { return struct {
GameType uint8
Timestamp uint64
Proxy common.Address Proxy common.Address
Timestamp *big.Int
}{ }{
Proxy: m.games[index].Proxy, GameType: m.games[index].GameType,
Timestamp: m.games[index].Timestamp, Timestamp: m.games[index].Timestamp,
Proxy: m.games[index].Proxy,
}, nil }, nil
} }
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
openum "github.com/ethereum-optimism/optimism/op-service/enum" openum "github.com/ethereum-optimism/optimism/op-service/enum"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
...@@ -29,7 +30,12 @@ var ( ...@@ -29,7 +30,12 @@ var (
Usage: "HTTP provider URL for L1.", Usage: "HTTP provider URL for L1.",
EnvVars: prefixEnvVars("L1_ETH_RPC"), EnvVars: prefixEnvVars("L1_ETH_RPC"),
} }
DGFAddressFlag = &cli.StringFlag{ FactoryAddressFlag = &cli.StringFlag{
Name: "game-factory-address",
Usage: "Address of the fault game factory contract.",
EnvVars: prefixEnvVars("GAME_FACTORY_ADDRESS"),
}
GameAddressFlag = &cli.StringFlag{
Name: "game-address", Name: "game-address",
Usage: "Address of the Fault Game contract.", Usage: "Address of the Fault Game contract.",
EnvVars: prefixEnvVars("GAME_ADDRESS"), EnvVars: prefixEnvVars("GAME_ADDRESS"),
...@@ -105,7 +111,7 @@ var ( ...@@ -105,7 +111,7 @@ var (
// requiredFlags are checked by [CheckRequired] // requiredFlags are checked by [CheckRequired]
var requiredFlags = []cli.Flag{ var requiredFlags = []cli.Flag{
L1EthRpcFlag, L1EthRpcFlag,
DGFAddressFlag, FactoryAddressFlag,
TraceTypeFlag, TraceTypeFlag,
AgreeWithProposedOutputFlag, AgreeWithProposedOutputFlag,
} }
...@@ -113,6 +119,7 @@ var requiredFlags = []cli.Flag{ ...@@ -113,6 +119,7 @@ var requiredFlags = []cli.Flag{
// optionalFlags is a list of unchecked cli flags // optionalFlags is a list of unchecked cli flags
var optionalFlags = []cli.Flag{ var optionalFlags = []cli.Flag{
AlphabetFlag, AlphabetFlag,
GameAddressFlag,
CannonNetworkFlag, CannonNetworkFlag,
CannonRollupConfigFlag, CannonRollupConfigFlag,
CannonL2GenesisFlag, CannonL2GenesisFlag,
...@@ -181,10 +188,17 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) { ...@@ -181,10 +188,17 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) {
if err := CheckRequired(ctx); err != nil { if err := CheckRequired(ctx); err != nil {
return nil, err return nil, err
} }
dgfAddress, err := opservice.ParseAddress(ctx.String(DGFAddressFlag.Name)) gameFactoryAddress, err := opservice.ParseAddress(ctx.String(FactoryAddressFlag.Name))
if err != nil { if err != nil {
return nil, err return nil, err
} }
var gameAddress common.Address
if ctx.IsSet(GameAddressFlag.Name) {
gameAddress, err = opservice.ParseAddress(ctx.String(GameAddressFlag.Name))
if err != nil {
return nil, err
}
}
txMgrConfig := txmgr.ReadCLIConfig(ctx) txMgrConfig := txmgr.ReadCLIConfig(ctx)
...@@ -194,7 +208,8 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) { ...@@ -194,7 +208,8 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) {
// Required Flags // Required Flags
L1EthRpc: ctx.String(L1EthRpcFlag.Name), L1EthRpc: ctx.String(L1EthRpcFlag.Name),
TraceType: traceTypeFlag, TraceType: traceTypeFlag,
GameAddress: dgfAddress, GameFactoryAddress: gameFactoryAddress,
GameAddress: gameAddress,
AlphabetTrace: ctx.String(AlphabetFlag.Name), AlphabetTrace: ctx.String(AlphabetFlag.Name),
CannonNetwork: ctx.String(CannonNetworkFlag.Name), CannonNetwork: ctx.String(CannonNetworkFlag.Name),
CannonRollupConfigPath: ctx.String(CannonRollupConfigFlag.Name), CannonRollupConfigPath: ctx.String(CannonRollupConfigFlag.Name),
......
...@@ -3,6 +3,7 @@ set -euo pipefail ...@@ -3,6 +3,7 @@ set -euo pipefail
SOURCE_DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd) SOURCE_DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
CHALLENGER_DIR=$(echo ${SOURCE_DIR%/*/*}) CHALLENGER_DIR=$(echo ${SOURCE_DIR%/*/*})
MONOREPO_DIR=$(echo ${SOURCE_DIR%/*/*/*})
# Check that the fault game address file exists # Check that the fault game address file exists
FAULT_GAME_ADDR_FILE="$CHALLENGER_DIR/.fault-game-address" FAULT_GAME_ADDR_FILE="$CHALLENGER_DIR/.fault-game-address"
...@@ -14,6 +15,7 @@ fi ...@@ -14,6 +15,7 @@ fi
# Charlie's Address: 0xF45B7537828CB2fffBC69996B054c2Aaf36DC778 # Charlie's Address: 0xF45B7537828CB2fffBC69996B054c2Aaf36DC778
CHARLIE_KEY="74feb147d72bfae943e6b4e483410933d9e447d5dc47d52432dcc2c1454dabb7" CHARLIE_KEY="74feb147d72bfae943e6b4e483410933d9e447d5dc47d52432dcc2c1454dabb7"
DISPUTE_GAME_PROXY=$(jq -r .DisputeGameFactoryProxy $MONOREPO_DIR/.devnet/addresses.json)
FAULT_GAME_ADDRESS=$(cat $FAULT_GAME_ADDR_FILE) FAULT_GAME_ADDRESS=$(cat $FAULT_GAME_ADDR_FILE)
echo "Fault dispute game address: $FAULT_GAME_ADDRESS" echo "Fault dispute game address: $FAULT_GAME_ADDRESS"
...@@ -21,6 +23,7 @@ $CHALLENGER_DIR/bin/op-challenger \ ...@@ -21,6 +23,7 @@ $CHALLENGER_DIR/bin/op-challenger \
--l1-eth-rpc http://localhost:8545 \ --l1-eth-rpc http://localhost:8545 \
--trace-type="alphabet" \ --trace-type="alphabet" \
--alphabet "abcdefgh" \ --alphabet "abcdefgh" \
--game-factory-address $DISPUTE_GAME_PROXY \
--game-address $FAULT_GAME_ADDRESS \ --game-address $FAULT_GAME_ADDRESS \
--private-key $CHARLIE_KEY \ --private-key $CHARLIE_KEY \
--num-confirmations 1 \ --num-confirmations 1 \
......
...@@ -3,6 +3,7 @@ set -euo pipefail ...@@ -3,6 +3,7 @@ set -euo pipefail
SOURCE_DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd) SOURCE_DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
CHALLENGER_DIR=$(echo ${SOURCE_DIR%/*/*}) CHALLENGER_DIR=$(echo ${SOURCE_DIR%/*/*})
MONOREPO_DIR=$(echo ${SOURCE_DIR%/*/*/*})
# Check that the fault game address file exists # Check that the fault game address file exists
FAULT_GAME_ADDR_FILE="$CHALLENGER_DIR/.fault-game-address" FAULT_GAME_ADDR_FILE="$CHALLENGER_DIR/.fault-game-address"
...@@ -14,6 +15,7 @@ fi ...@@ -14,6 +15,7 @@ fi
# Mallory's Address: 0x4641c704a6c743f73ee1f36C7568Fbf4b80681e4 # Mallory's Address: 0x4641c704a6c743f73ee1f36C7568Fbf4b80681e4
MALLORY_KEY="28d7045146193f5f4eeb151c4843544b1b0d30a7ac1680c845a416fac65a7715" MALLORY_KEY="28d7045146193f5f4eeb151c4843544b1b0d30a7ac1680c845a416fac65a7715"
DISPUTE_GAME_PROXY=$(jq -r .DisputeGameFactoryProxy $MONOREPO_DIR/.devnet/addresses.json)
FAULT_GAME_ADDRESS=$(cat $FAULT_GAME_ADDR_FILE) FAULT_GAME_ADDRESS=$(cat $FAULT_GAME_ADDR_FILE)
echo "Fault dispute game address: $FAULT_GAME_ADDRESS" echo "Fault dispute game address: $FAULT_GAME_ADDRESS"
...@@ -21,6 +23,7 @@ $CHALLENGER_DIR/bin/op-challenger \ ...@@ -21,6 +23,7 @@ $CHALLENGER_DIR/bin/op-challenger \
--l1-eth-rpc http://localhost:8545 \ --l1-eth-rpc http://localhost:8545 \
--trace-type="alphabet" \ --trace-type="alphabet" \
--alphabet "abcdexyz" \ --alphabet "abcdexyz" \
--game-factory-address $DISPUTE_GAME_PROXY \
--game-address $FAULT_GAME_ADDRESS \ --game-address $FAULT_GAME_ADDRESS \
--private-key $MALLORY_KEY \ --private-key $MALLORY_KEY \
--num-confirmations 1 \ --num-confirmations 1 \
......
...@@ -2,15 +2,22 @@ package challenger ...@@ -2,15 +2,22 @@ package challenger
import ( import (
"context" "context"
"crypto/ecdsa"
"encoding/json"
"errors" "errors"
"os" "os"
"path/filepath"
"testing" "testing"
"time" "time"
op_challenger "github.com/ethereum-optimism/optimism/op-challenger" op_challenger "github.com/ethereum-optimism/optimism/op-challenger"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/testlog" "github.com/ethereum-optimism/optimism/op-node/testlog"
"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/core"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -23,6 +30,67 @@ type Helper struct { ...@@ -23,6 +30,67 @@ type Helper struct {
type Option func(config2 *config.Config) type Option func(config2 *config.Config)
func WithFactoryAddress(addr common.Address) Option {
return func(c *config.Config) {
c.GameFactoryAddress = addr
}
}
func WithGameAddress(addr common.Address) Option {
return func(c *config.Config) {
c.GameAddress = addr
}
}
func WithPrivKey(key *ecdsa.PrivateKey) Option {
return func(c *config.Config) {
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(key)
}
}
func WithAgreeProposedOutput(agree bool) Option {
return func(c *config.Config) {
c.AgreeWithProposedOutput = agree
}
}
func WithAlphabet(alphabet string) Option {
return func(c *config.Config) {
c.TraceType = config.TraceTypeAlphabet
c.AlphabetTrace = alphabet
}
}
func WithCannon(
t *testing.T,
rollupCfg *rollup.Config,
l2Genesis *core.Genesis,
l2Endpoint string,
) Option {
return func(c *config.Config) {
require := require.New(t)
c.TraceType = config.TraceTypeCannon
c.CannonL2 = l2Endpoint
c.CannonDatadir = t.TempDir()
c.CannonBin = "../cannon/bin/cannon"
c.CannonServer = "../op-program/bin/op-program"
c.CannonAbsolutePreState = "../op-program/bin/prestate.json"
c.CannonSnapshotFreq = 10_000_000
genesisBytes, err := json.Marshal(l2Genesis)
require.NoError(err, "marshall l2 genesis config")
genesisFile := filepath.Join(c.CannonDatadir, "l2-genesis.json")
require.NoError(os.WriteFile(genesisFile, genesisBytes, 0644))
c.CannonL2GenesisPath = genesisFile
rollupBytes, err := json.Marshal(rollupCfg)
require.NoError(err, "marshall rollup config")
rollupFile := filepath.Join(c.CannonDatadir, "rollup.json")
require.NoError(os.WriteFile(rollupFile, rollupBytes, 0644))
c.CannonRollupConfigPath = rollupFile
}
}
func NewChallenger(t *testing.T, ctx context.Context, l1Endpoint string, name string, options ...Option) *Helper { func NewChallenger(t *testing.T, ctx context.Context, l1Endpoint string, name string, options ...Option) *Helper {
log := testlog.Logger(t, log.LvlInfo).New("role", name) log := testlog.Logger(t, log.LvlInfo).New("role", name)
log.Info("Creating challenger", "l1", l1Endpoint) log.Info("Creating challenger", "l1", l1Endpoint)
......
...@@ -15,6 +15,7 @@ type AlphabetGameHelper struct { ...@@ -15,6 +15,7 @@ type AlphabetGameHelper struct {
func (g *AlphabetGameHelper) StartChallenger(ctx context.Context, l1Endpoint string, name string, options ...challenger.Option) *challenger.Helper { func (g *AlphabetGameHelper) StartChallenger(ctx context.Context, l1Endpoint string, name string, options ...challenger.Option) *challenger.Helper {
opts := []challenger.Option{ opts := []challenger.Option{
func(c *config.Config) { func(c *config.Config) {
c.GameFactoryAddress = g.factoryAddr
c.GameAddress = g.addr c.GameAddress = g.addr
c.TraceType = config.TraceTypeAlphabet c.TraceType = config.TraceTypeAlphabet
// By default the challenger agrees with the root claim (thus disagrees with the proposed output) // By default the challenger agrees with the root claim (thus disagrees with the proposed output)
......
...@@ -2,21 +2,14 @@ package disputegame ...@@ -2,21 +2,14 @@ package disputegame
import ( import (
"context" "context"
"encoding/json"
"os"
"path/filepath"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault/cannon" "github.com/ethereum-optimism/optimism/op-challenger/fault/cannon"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/testlog" "github.com/ethereum-optimism/optimism/op-node/testlog"
"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/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
) )
type CannonGameHelper struct { type CannonGameHelper struct {
...@@ -24,7 +17,11 @@ type CannonGameHelper struct { ...@@ -24,7 +17,11 @@ type CannonGameHelper struct {
} }
func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Endpoint string, l2Endpoint string, name string, options ...challenger.Option) *challenger.Helper { func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Endpoint string, l2Endpoint string, name string, options ...challenger.Option) *challenger.Helper {
opts := []challenger.Option{createConfigOption(g.t, rollupCfg, l2Genesis, g.addr, l2Endpoint)} opts := []challenger.Option{
challenger.WithCannon(g.t, rollupCfg, l2Genesis, l2Endpoint),
challenger.WithFactoryAddress(g.factoryAddr),
challenger.WithGameAddress(g.addr),
}
opts = append(opts, options...) opts = append(opts, options...)
c := challenger.NewChallenger(g.t, ctx, l1Endpoint, name, opts...) c := challenger.NewChallenger(g.t, ctx, l1Endpoint, name, opts...)
g.t.Cleanup(func() { g.t.Cleanup(func() {
...@@ -34,7 +31,11 @@ func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollu ...@@ -34,7 +31,11 @@ func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollu
} }
func (g *CannonGameHelper) CreateHonestActor(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Client bind.ContractCaller, l1Endpoint string, l2Endpoint string, options ...challenger.Option) *HonestHelper { func (g *CannonGameHelper) CreateHonestActor(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Client bind.ContractCaller, l1Endpoint string, l2Endpoint string, options ...challenger.Option) *HonestHelper {
opts := []challenger.Option{createConfigOption(g.t, rollupCfg, l2Genesis, g.addr, l2Endpoint)} opts := []challenger.Option{
challenger.WithCannon(g.t, rollupCfg, l2Genesis, l2Endpoint),
challenger.WithFactoryAddress(g.factoryAddr),
challenger.WithGameAddress(g.addr),
}
opts = append(opts, options...) opts = append(opts, options...)
cfg := challenger.NewChallengerConfig(g.t, l1Endpoint, opts...) cfg := challenger.NewChallengerConfig(g.t, l1Endpoint, opts...)
provider, err := cannon.NewTraceProvider(ctx, testlog.Logger(g.t, log.LvlInfo).New("role", "CorrectTrace"), cfg, l1Client) provider, err := cannon.NewTraceProvider(ctx, testlog.Logger(g.t, log.LvlInfo).New("role", "CorrectTrace"), cfg, l1Client)
...@@ -47,30 +48,3 @@ func (g *CannonGameHelper) CreateHonestActor(ctx context.Context, rollupCfg *rol ...@@ -47,30 +48,3 @@ func (g *CannonGameHelper) CreateHonestActor(ctx context.Context, rollupCfg *rol
correctTrace: provider, correctTrace: provider,
} }
} }
func createConfigOption(t *testing.T, rollupCfg *rollup.Config, l2Genesis *core.Genesis, gameAddr common.Address, l2Endpoint string) challenger.Option {
return func(c *config.Config) {
require := require.New(t)
c.GameAddress = gameAddr
c.TraceType = config.TraceTypeCannon
c.AgreeWithProposedOutput = false
c.CannonL2 = l2Endpoint
c.CannonBin = "../cannon/bin/cannon"
c.CannonDatadir = t.TempDir()
c.CannonServer = "../op-program/bin/op-program"
c.CannonAbsolutePreState = "../op-program/bin/prestate.json"
c.CannonSnapshotFreq = 10_000_000
genesisBytes, err := json.Marshal(l2Genesis)
require.NoError(err, "marshall l2 genesis config")
genesisFile := filepath.Join(c.CannonDatadir, "l2-genesis.json")
require.NoError(os.WriteFile(genesisFile, genesisBytes, 0644))
c.CannonL2GenesisPath = genesisFile
rollupBytes, err := json.Marshal(rollupCfg)
require.NoError(err, "marshall rollup config")
rollupFile := filepath.Join(c.CannonDatadir, "rollup.json")
require.NoError(os.WriteFile(rollupFile, rollupBytes, 0644))
c.CannonRollupConfigPath = rollupFile
}
}
...@@ -22,6 +22,7 @@ type FaultGameHelper struct { ...@@ -22,6 +22,7 @@ type FaultGameHelper struct {
client *ethclient.Client client *ethclient.Client
opts *bind.TransactOpts opts *bind.TransactOpts
game *bindings.FaultDisputeGame game *bindings.FaultDisputeGame
factoryAddr common.Address
addr common.Address addr common.Address
} }
...@@ -59,7 +60,7 @@ func (g *FaultGameHelper) MaxDepth(ctx context.Context) int64 { ...@@ -59,7 +60,7 @@ func (g *FaultGameHelper) MaxDepth(ctx context.Context) int64 {
return depth.Int64() return depth.Int64()
} }
func (g *FaultGameHelper) WaitForClaim(ctx context.Context, predicate func(claim ContractClaim) bool) { func (g *FaultGameHelper) waitForClaim(ctx context.Context, errorMsg string, predicate func(claim ContractClaim) bool) {
ctx, cancel := context.WithTimeout(ctx, time.Minute) ctx, cancel := context.WithTimeout(ctx, time.Minute)
defer cancel() defer cancel()
err := wait.For(ctx, time.Second, func() (bool, error) { err := wait.For(ctx, time.Second, func() (bool, error) {
...@@ -79,7 +80,15 @@ func (g *FaultGameHelper) WaitForClaim(ctx context.Context, predicate func(claim ...@@ -79,7 +80,15 @@ func (g *FaultGameHelper) WaitForClaim(ctx context.Context, predicate func(claim
} }
return false, nil return false, nil
}) })
g.require.NoError(err) if err != nil { // Avoid waiting time capturing game data when there's no error
g.require.NoErrorf(err, "%v\n%v", errorMsg, g.gameData(ctx))
}
}
func (g *FaultGameHelper) GetClaimValue(ctx context.Context, claimIdx int64) common.Hash {
g.WaitForClaimCount(ctx, claimIdx+1)
claim := g.getClaim(ctx, claimIdx)
return claim.Claim
} }
// getClaim retrieves the claim data for a specific index. // getClaim retrieves the claim data for a specific index.
...@@ -94,7 +103,10 @@ func (g *FaultGameHelper) getClaim(ctx context.Context, claimIdx int64) Contract ...@@ -94,7 +103,10 @@ func (g *FaultGameHelper) getClaim(ctx context.Context, claimIdx int64) Contract
func (g *FaultGameHelper) WaitForClaimAtMaxDepth(ctx context.Context, countered bool) { func (g *FaultGameHelper) WaitForClaimAtMaxDepth(ctx context.Context, countered bool) {
maxDepth := g.MaxDepth(ctx) maxDepth := g.MaxDepth(ctx)
g.WaitForClaim(ctx, func(claim ContractClaim) bool { g.waitForClaim(
ctx,
fmt.Sprintf("Could not find claim depth %v with countered=%v", maxDepth, countered),
func(claim ContractClaim) bool {
pos := types.NewPositionFromGIndex(claim.Position.Uint64()) pos := types.NewPositionFromGIndex(claim.Position.Uint64())
return int64(pos.Depth()) == maxDepth && claim.Countered == countered return int64(pos.Depth()) == maxDepth && claim.Countered == countered
}) })
...@@ -140,7 +152,7 @@ func (g *FaultGameHelper) Defend(ctx context.Context, claimIdx int64, claim comm ...@@ -140,7 +152,7 @@ func (g *FaultGameHelper) Defend(ctx context.Context, claimIdx int64, claim comm
g.require.NoError(err, "Defend transaction was not OK") g.require.NoError(err, "Defend transaction was not OK")
} }
func (g *FaultGameHelper) LogGameData(ctx context.Context) { func (g *FaultGameHelper) gameData(ctx context.Context) string {
opts := &bind.CallOpts{Context: ctx} opts := &bind.CallOpts{Context: ctx}
maxDepth := int(g.MaxDepth(ctx)) maxDepth := int(g.MaxDepth(ctx))
claimCount, err := g.game.ClaimDataLen(opts) claimCount, err := g.game.ClaimDataLen(opts)
...@@ -156,5 +168,9 @@ func (g *FaultGameHelper) LogGameData(ctx context.Context) { ...@@ -156,5 +168,9 @@ func (g *FaultGameHelper) LogGameData(ctx context.Context) {
} }
status, err := g.game.Status(opts) status, err := g.game.Status(opts)
g.require.NoError(err, "Load game status") g.require.NoError(err, "Load game status")
g.t.Logf("Game %v (%v):\n%v\n", g.addr, Status(status), info) return fmt.Sprintf("Game %v (%v):\n%v\n", g.addr, Status(status), info)
}
func (g *FaultGameHelper) LogGameData(ctx context.Context) {
g.t.Log(g.gameData(ctx))
} }
...@@ -12,7 +12,6 @@ import ( ...@@ -12,7 +12,6 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault/alphabet" "github.com/ethereum-optimism/optimism/op-challenger/fault/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/fault/cannon" "github.com/ethereum-optimism/optimism/op-challenger/fault/cannon"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
...@@ -124,6 +123,7 @@ func (h *FactoryHelper) StartAlphabetGame(ctx context.Context, claimedAlphabet s ...@@ -124,6 +123,7 @@ func (h *FactoryHelper) StartAlphabetGame(ctx context.Context, claimedAlphabet s
client: h.client, client: h.client,
opts: h.opts, opts: h.opts,
game: game, game: game,
factoryAddr: h.factoryAddr,
addr: createdEvent.DisputeProxy, addr: createdEvent.DisputeProxy,
}, },
claimedAlphabet: claimedAlphabet, claimedAlphabet: claimedAlphabet,
...@@ -137,7 +137,10 @@ func (h *FactoryHelper) StartCannonGame(ctx context.Context, rootClaim common.Ha ...@@ -137,7 +137,10 @@ func (h *FactoryHelper) StartCannonGame(ctx context.Context, rootClaim common.Ha
func (h *FactoryHelper) StartCannonGameWithCorrectRoot(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Endpoint string, l2Endpoint string, options ...challenger.Option) (*CannonGameHelper, *HonestHelper) { func (h *FactoryHelper) StartCannonGameWithCorrectRoot(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Endpoint string, l2Endpoint string, options ...challenger.Option) (*CannonGameHelper, *HonestHelper) {
l2BlockNumber, l1Head := h.prepareCannonGame(ctx) l2BlockNumber, l1Head := h.prepareCannonGame(ctx)
challengerOpts := []challenger.Option{createConfigOption(h.t, rollupCfg, l2Genesis, common.Address{0xaa}, l2Endpoint)} challengerOpts := []challenger.Option{
challenger.WithCannon(h.t, rollupCfg, l2Genesis, l2Endpoint),
challenger.WithFactoryAddress(h.factoryAddr),
}
challengerOpts = append(challengerOpts, options...) challengerOpts = append(challengerOpts, options...)
cfg := challenger.NewChallengerConfig(h.t, l1Endpoint, challengerOpts...) cfg := challenger.NewChallengerConfig(h.t, l1Endpoint, challengerOpts...)
opts := &bind.CallOpts{Context: ctx} opts := &bind.CallOpts{Context: ctx}
...@@ -205,17 +208,15 @@ func (h *FactoryHelper) createCannonGame(ctx context.Context, l2BlockNumber uint ...@@ -205,17 +208,15 @@ func (h *FactoryHelper) createCannonGame(ctx context.Context, l2BlockNumber uint
client: h.client, client: h.client,
opts: h.opts, opts: h.opts,
game: game, game: game,
factoryAddr: h.factoryAddr,
addr: createdEvent.DisputeProxy, addr: createdEvent.DisputeProxy,
}, },
} }
} }
func (h *FactoryHelper) StartChallenger(ctx context.Context, l1Endpoint string, name string, options ...challenger.Option) *challenger.Helper { func (h *FactoryHelper) StartChallenger(ctx context.Context, l1Endpoint string, name string, options ...challenger.Option) *challenger.Helper {
opts := []challenger.Option{ opts := []challenger.Option{
func(c *config.Config) { challenger.WithFactoryAddress(h.factoryAddr),
// Uncomment when challenger actually supports setting the game factory address
//c.FactoryAddress = h.factoryAddr
c.TraceType = config.TraceTypeAlphabet
},
} }
opts = append(opts, options...) opts = append(opts, options...)
c := challenger.NewChallenger(h.t, ctx, l1Endpoint, name, opts...) c := challenger.NewChallenger(h.t, ctx, l1Endpoint, name, opts...)
......
...@@ -4,8 +4,7 @@ import ( ...@@ -4,8 +4,7 @@ import (
"context" "context"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/disputegame" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/disputegame"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -23,11 +22,11 @@ func TestCannonMultipleGames(t *testing.T) { ...@@ -23,11 +22,11 @@ func TestCannonMultipleGames(t *testing.T) {
gameFactory := disputegame.NewFactoryHelper(t, ctx, sys.cfg.L1Deployments, l1Client) gameFactory := disputegame.NewFactoryHelper(t, ctx, sys.cfg.L1Deployments, l1Client)
// Start a challenger with the correct alphabet trace // Start a challenger with the correct alphabet trace
gameFactory.StartChallenger(ctx, sys.NodeEndpoint("l1"), "TowerDefense", func(c *config.Config) { gameFactory.StartChallenger(ctx, sys.NodeEndpoint("l1"), "TowerDefense",
c.AgreeWithProposedOutput = true challenger.WithAlphabet("abcdefg"),
c.AlphabetTrace = "abcdefg" challenger.WithPrivKey(sys.cfg.Secrets.Alice),
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Alice) challenger.WithAgreeProposedOutput(true),
}) )
game1 := gameFactory.StartAlphabetGame(ctx, "abcxyz") game1 := gameFactory.StartAlphabetGame(ctx, "abcxyz")
// Wait for the challenger to respond to the first game // Wait for the challenger to respond to the first game
...@@ -44,6 +43,59 @@ func TestCannonMultipleGames(t *testing.T) { ...@@ -44,6 +43,59 @@ func TestCannonMultipleGames(t *testing.T) {
game1.WaitForClaimCount(ctx, 4) game1.WaitForClaimCount(ctx, 4)
} }
func TestMultipleCannonGames(t *testing.T) {
t.Skip("Cannon provider doesn't currently isolate different game traces")
InitParallel(t)
ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t)
t.Cleanup(sys.Close)
gameFactory := disputegame.NewFactoryHelper(t, ctx, sys.cfg.L1Deployments, l1Client)
// Start a challenger with the correct alphabet trace
gameFactory.StartChallenger(ctx, sys.NodeEndpoint("l1"), "TowerDefense",
challenger.WithCannon(t, sys.RollupConfig, sys.L2GenesisCfg, sys.NodeEndpoint("sequencer")),
challenger.WithPrivKey(sys.cfg.Secrets.Alice),
challenger.WithAgreeProposedOutput(true),
)
game1 := gameFactory.StartCannonGame(ctx, common.Hash{0xaa})
game2 := gameFactory.StartCannonGame(ctx, common.Hash{0xbb})
game1.WaitForClaimCount(ctx, 2)
game2.WaitForClaimCount(ctx, 2)
game1Claim := game1.GetClaimValue(ctx, 1)
game2Claim := game2.GetClaimValue(ctx, 1)
require.NotEqual(t, game1Claim, game2Claim, "games should have different cannon traces")
// Push both games down to the step function
maxDepth := game1.MaxDepth(ctx)
for claimCount := int64(1); claimCount <= maxDepth; {
// Challenger should respond to both games
claimCount++
game1.WaitForClaimCount(ctx, claimCount)
game2.WaitForClaimCount(ctx, claimCount)
// Progress both games
game1.Defend(ctx, claimCount-1, common.Hash{0xaa})
game2.Defend(ctx, claimCount-1, common.Hash{0xaa})
claimCount++
}
game1.WaitForClaimAtMaxDepth(ctx, true)
game2.WaitForClaimAtMaxDepth(ctx, true)
gameDuration := game1.GameDuration(ctx)
sys.TimeTravelClock.AdvanceTime(gameDuration)
require.NoError(t, wait.ForNextBlock(ctx, l1Client))
game1.WaitForGameStatus(ctx, disputegame.StatusChallengerWins)
game2.WaitForGameStatus(ctx, disputegame.StatusChallengerWins)
game1.LogGameData(ctx)
game2.LogGameData(ctx)
}
func TestResolveDisputeGame(t *testing.T) { func TestResolveDisputeGame(t *testing.T) {
InitParallel(t) InitParallel(t)
...@@ -59,11 +111,11 @@ func TestResolveDisputeGame(t *testing.T) { ...@@ -59,11 +111,11 @@ func TestResolveDisputeGame(t *testing.T) {
game.WaitForGameStatus(ctx, disputegame.StatusInProgress) game.WaitForGameStatus(ctx, disputegame.StatusInProgress)
game.StartChallenger(ctx, sys.NodeEndpoint("l1"), "HonestAlice", func(c *config.Config) { game.StartChallenger(ctx, sys.NodeEndpoint("l1"), "HonestAlice",
c.AgreeWithProposedOutput = true // Agree with the proposed output, so disagree with the root claim challenger.WithAgreeProposedOutput(true),
c.AlphabetTrace = "abcdefg" challenger.WithAlphabet("abcdefg"),
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Alice) challenger.WithPrivKey(sys.cfg.Secrets.Alice),
}) )
game.WaitForClaimCount(ctx, 2) game.WaitForClaimCount(ctx, 2)
...@@ -155,15 +207,17 @@ func TestChallengerCompleteDisputeGame(t *testing.T) { ...@@ -155,15 +207,17 @@ func TestChallengerCompleteDisputeGame(t *testing.T) {
require.NotNil(t, game) require.NotNil(t, game)
gameDuration := game.GameDuration(ctx) gameDuration := game.GameDuration(ctx)
game.StartChallenger(ctx, sys.NodeEndpoint("l1"), "Defender", func(c *config.Config) { game.StartChallenger(ctx, sys.NodeEndpoint("l1"), "Defender",
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Mallory) challenger.WithAgreeProposedOutput(false),
}) challenger.WithPrivKey(sys.cfg.Secrets.Mallory),
)
game.StartChallenger(ctx, sys.NodeEndpoint("l1"), "Challenger", func(c *config.Config) { game.StartChallenger(ctx, sys.NodeEndpoint("l1"), "Challenger",
c.AgreeWithProposedOutput = true // Agree with the proposed output, so disagree with the root claim // Agree with the proposed output, so disagree with the root claim
c.AlphabetTrace = test.otherAlphabet challenger.WithAgreeProposedOutput(true),
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Alice) challenger.WithAlphabet(test.otherAlphabet),
}) challenger.WithPrivKey(sys.cfg.Secrets.Alice),
)
// Wait for a claim at the maximum depth that has been countered to indicate we're ready to resolve the game // Wait for a claim at the maximum depth that has been countered to indicate we're ready to resolve the game
game.WaitForClaimAtMaxDepth(ctx, test.expectStep) game.WaitForClaimAtMaxDepth(ctx, test.expectStep)
...@@ -201,10 +255,11 @@ func TestCannonDisputeGame(t *testing.T) { ...@@ -201,10 +255,11 @@ func TestCannonDisputeGame(t *testing.T) {
require.NotNil(t, game) require.NotNil(t, game)
game.LogGameData(ctx) game.LogGameData(ctx)
game.StartChallenger(ctx, sys.RollupConfig, sys.L2GenesisCfg, sys.NodeEndpoint("l1"), sys.NodeEndpoint("sequencer"), "Challenger", func(c *config.Config) { game.StartChallenger(ctx, sys.RollupConfig, sys.L2GenesisCfg, sys.NodeEndpoint("l1"), sys.NodeEndpoint("sequencer"), "Challenger",
c.AgreeWithProposedOutput = true // Agree with the proposed output, so disagree with the root claim // Agree with the proposed output, so disagree with the root claim
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Alice) challenger.WithAgreeProposedOutput(true),
}) challenger.WithPrivKey(sys.cfg.Secrets.Alice),
)
maxDepth := game.MaxDepth(ctx) maxDepth := game.MaxDepth(ctx)
for claimCount := int64(1); claimCount < maxDepth; { for claimCount := int64(1); claimCount < maxDepth; {
...@@ -251,14 +306,15 @@ func TestCannonDefendStep(t *testing.T) { ...@@ -251,14 +306,15 @@ func TestCannonDefendStep(t *testing.T) {
l1Endpoint := sys.NodeEndpoint("l1") l1Endpoint := sys.NodeEndpoint("l1")
l2Endpoint := sys.NodeEndpoint("sequencer") l2Endpoint := sys.NodeEndpoint("sequencer")
game.StartChallenger(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Endpoint, l2Endpoint, "Challenger", func(c *config.Config) { game.StartChallenger(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Endpoint, l2Endpoint, "Challenger",
c.AgreeWithProposedOutput = true // Agree with the proposed output, so disagree with the root claim // Agree with the proposed output, so disagree with the root claim
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Alice) challenger.WithAgreeProposedOutput(true),
}) challenger.WithPrivKey(sys.cfg.Secrets.Alice),
)
correctTrace := game.CreateHonestActor(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Client, l1Endpoint, l2Endpoint, func(c *config.Config) { correctTrace := game.CreateHonestActor(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Client, l1Endpoint, l2Endpoint,
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Mallory) challenger.WithPrivKey(sys.cfg.Secrets.Mallory),
}) )
maxDepth := game.MaxDepth(ctx) maxDepth := game.MaxDepth(ctx)
for claimCount := int64(1); claimCount < maxDepth; { for claimCount := int64(1); claimCount < maxDepth; {
...@@ -302,16 +358,17 @@ func TestCannonChallengeWithCorrectRoot(t *testing.T) { ...@@ -302,16 +358,17 @@ func TestCannonChallengeWithCorrectRoot(t *testing.T) {
l2Endpoint := sys.NodeEndpoint("sequencer") l2Endpoint := sys.NodeEndpoint("sequencer")
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys.cfg.L1Deployments, l1Client) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys.cfg.L1Deployments, l1Client)
game, correctTrace := disputeGameFactory.StartCannonGameWithCorrectRoot(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Endpoint, l2Endpoint, func(c *config.Config) { game, correctTrace := disputeGameFactory.StartCannonGameWithCorrectRoot(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Endpoint, l2Endpoint,
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Mallory) challenger.WithPrivKey(sys.cfg.Secrets.Mallory),
}) )
require.NotNil(t, game) require.NotNil(t, game)
game.LogGameData(ctx) game.LogGameData(ctx)
game.StartChallenger(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Endpoint, l2Endpoint, "Challenger", func(c *config.Config) { game.StartChallenger(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Endpoint, l2Endpoint, "Challenger",
c.AgreeWithProposedOutput = true // Agree with the proposed output, so disagree with the root claim // Agree with the proposed output, so disagree with the root claim
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Alice) challenger.WithAgreeProposedOutput(true),
}) challenger.WithPrivKey(sys.cfg.Secrets.Alice),
)
maxDepth := game.MaxDepth(ctx) maxDepth := game.MaxDepth(ctx)
for claimCount := int64(1); claimCount < maxDepth; { for claimCount := int64(1); claimCount < maxDepth; {
......
...@@ -34,9 +34,9 @@ import ( ...@@ -34,9 +34,9 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup/driver" "github.com/ethereum-optimism/optimism/op-node/rollup/driver"
"github.com/ethereum-optimism/optimism/op-node/sources" "github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/testlog" "github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" oppprof "github.com/ethereum-optimism/optimism/op-service/pprof"
"github.com/ethereum-optimism/optimism/op-service/retry"
) )
func TestL2OutputSubmitter(t *testing.T) { func TestL2OutputSubmitter(t *testing.T) {
...@@ -533,7 +533,7 @@ func TestSystemMockP2P(t *testing.T) { ...@@ -533,7 +533,7 @@ func TestSystemMockP2P(t *testing.T) {
// poll to see if the verifier node is connected & meshed on gossip. // poll to see if the verifier node is connected & meshed on gossip.
// Without this verifier, we shouldn't start sending blocks around, or we'll miss them and fail the test. // Without this verifier, we shouldn't start sending blocks around, or we'll miss them and fail the test.
backOffStrategy := backoff.Exponential() backOffStrategy := retry.Exponential()
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
if check() { if check() {
break break
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
"regexp" "regexp"
"time" "time"
"github.com/ethereum-optimism/optimism/op-service/backoff" "github.com/ethereum-optimism/optimism/op-service/retry"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
...@@ -103,8 +103,8 @@ func NewRPC(ctx context.Context, lgr log.Logger, addr string, opts ...RPCOption) ...@@ -103,8 +103,8 @@ func NewRPC(ctx context.Context, lgr log.Logger, addr string, opts ...RPCOption)
// Dials a JSON-RPC endpoint repeatedly, with a backoff, until a client connection is established. Auth is optional. // Dials a JSON-RPC endpoint repeatedly, with a backoff, until a client connection is established. Auth is optional.
func dialRPCClientWithBackoff(ctx context.Context, log log.Logger, addr string, attempts int, opts ...rpc.ClientOption) (*rpc.Client, error) { func dialRPCClientWithBackoff(ctx context.Context, log log.Logger, addr string, attempts int, opts ...rpc.ClientOption) (*rpc.Client, error) {
bOff := backoff.Exponential() bOff := retry.Exponential()
return backoff.Do(ctx, attempts, bOff, func() (*rpc.Client, error) { return retry.Do(ctx, attempts, bOff, func() (*rpc.Client, error) {
if !IsURLAvailable(addr) { if !IsURLAvailable(addr) {
log.Warn("failed to dial address, but may connect later", "addr", addr) log.Warn("failed to dial address, but may connect later", "addr", addr)
return nil, fmt.Errorf("address unavailable (%s)", addr) return nil, fmt.Errorf("address unavailable (%s)", addr)
......
...@@ -63,7 +63,6 @@ jq '.batches|del(.[]|.Transactions)' $CHANNEL_FILE ...@@ -63,7 +63,6 @@ jq '.batches|del(.[]|.Transactions)' $CHANNEL_FILE
## Roadmap ## Roadmap
- Parallel transaction fetching (CLI-3563)
- Pull the batches out of channels & store that information inside the ChannelWithMetadata (CLI-3565) - Pull the batches out of channels & store that information inside the ChannelWithMetadata (CLI-3565)
- Transaction Bytes used - Transaction Bytes used
- Total uncompressed (different from tx bytes) + compressed bytes - Total uncompressed (different from tx bytes) + compressed bytes
......
...@@ -8,12 +8,14 @@ import ( ...@@ -8,12 +8,14 @@ import (
"math/big" "math/big"
"os" "os"
"path" "path"
"sync/atomic"
"time" "time"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"golang.org/x/sync/errgroup"
) )
type TransactionWithMetadata struct { type TransactionWithMetadata struct {
...@@ -37,40 +39,59 @@ type Config struct { ...@@ -37,40 +39,59 @@ type Config struct {
BatchInbox common.Address BatchInbox common.Address
BatchSenders map[common.Address]struct{} BatchSenders map[common.Address]struct{}
OutDirectory string OutDirectory string
ConcurrentRequests uint64
} }
// Batches fetches & stores all transactions sent to the batch inbox address in // Batches fetches & stores all transactions sent to the batch inbox address in
// the given block range (inclusive to exclusive). // the given block range (inclusive to exclusive).
// The transactions & metadata are written to the out directory. // The transactions & metadata are written to the out directory.
func Batches(client *ethclient.Client, config Config) (totalValid, totalInvalid int) { func Batches(client *ethclient.Client, config Config) (totalValid, totalInvalid uint64) {
if err := os.MkdirAll(config.OutDirectory, 0750); err != nil { if err := os.MkdirAll(config.OutDirectory, 0750); err != nil {
log.Fatal(err) log.Fatal(err)
} }
number := new(big.Int).SetUint64(config.Start)
signer := types.LatestSignerForChainID(config.ChainID) signer := types.LatestSignerForChainID(config.ChainID)
concurrentRequests := int(config.ConcurrentRequests)
g, ctx := errgroup.WithContext(context.Background())
g.SetLimit(concurrentRequests)
for i := config.Start; i < config.End; i++ { for i := config.Start; i < config.End; i++ {
valid, invalid := fetchBatchesPerBlock(client, number, signer, config) if err := ctx.Err(); err != nil {
totalValid += valid break
totalInvalid += invalid }
number = number.Add(number, common.Big1) number := i
g.Go(func() error {
valid, invalid, err := fetchBatchesPerBlock(ctx, client, number, signer, config)
if err != nil {
return fmt.Errorf("error occurred while fetching block %d: %w", number, err)
}
atomic.AddUint64(&totalValid, valid)
atomic.AddUint64(&totalInvalid, invalid)
return nil
})
}
if err := g.Wait(); err != nil {
log.Fatal(err)
} }
return return
} }
// fetchBatchesPerBlock gets a block & the parses all of the transactions in the block. // fetchBatchesPerBlock gets a block & the parses all of the transactions in the block.
func fetchBatchesPerBlock(client *ethclient.Client, number *big.Int, signer types.Signer, config Config) (validBatchCount, invalidBatchCount int) { func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number uint64, signer types.Signer, config Config) (uint64, uint64, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) validBatchCount := uint64(0)
invalidBatchCount := uint64(0)
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel() defer cancel()
block, err := client.BlockByNumber(ctx, number) block, err := client.BlockByNumber(ctx, new(big.Int).SetUint64(number))
if err != nil { if err != nil {
log.Fatal(err) return 0, 0, err
} }
fmt.Println("Fetched block: ", number) fmt.Println("Fetched block: ", number)
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
if tx.To() != nil && *tx.To() == config.BatchInbox { if tx.To() != nil && *tx.To() == config.BatchInbox {
sender, err := signer.Sender(tx) sender, err := signer.Sender(tx)
if err != nil { if err != nil {
log.Fatal(err) return 0, 0, err
} }
validSender := true validSender := true
if _, ok := config.BatchSenders[sender]; !ok { if _, ok := config.BatchSenders[sender]; !ok {
...@@ -111,14 +132,14 @@ func fetchBatchesPerBlock(client *ethclient.Client, number *big.Int, signer type ...@@ -111,14 +132,14 @@ func fetchBatchesPerBlock(client *ethclient.Client, number *big.Int, signer type
filename := path.Join(config.OutDirectory, fmt.Sprintf("%s.json", tx.Hash().String())) filename := path.Join(config.OutDirectory, fmt.Sprintf("%s.json", tx.Hash().String()))
file, err := os.Create(filename) file, err := os.Create(filename)
if err != nil { if err != nil {
log.Fatal(err) return 0, 0, err
} }
defer file.Close() defer file.Close()
enc := json.NewEncoder(file) enc := json.NewEncoder(file)
if err := enc.Encode(txm); err != nil { if err := enc.Encode(txm); err != nil {
log.Fatal(err) return 0, 0, err
} }
} }
} }
return return validBatchCount, invalidBatchCount, nil
} }
...@@ -55,6 +55,11 @@ func main() { ...@@ -55,6 +55,11 @@ func main() {
Usage: "L1 RPC URL", Usage: "L1 RPC URL",
EnvVars: []string{"L1_RPC"}, EnvVars: []string{"L1_RPC"},
}, },
&cli.IntFlag{
Name: "concurrent-requests",
Value: 10,
Usage: "Concurrency level when fetching L1",
},
}, },
Action: func(cliCtx *cli.Context) error { Action: func(cliCtx *cli.Context) error {
client, err := ethclient.Dial(cliCtx.String("l1")) client, err := ethclient.Dial(cliCtx.String("l1"))
...@@ -76,6 +81,7 @@ func main() { ...@@ -76,6 +81,7 @@ func main() {
}, },
BatchInbox: common.HexToAddress(cliCtx.String("inbox")), BatchInbox: common.HexToAddress(cliCtx.String("inbox")),
OutDirectory: cliCtx.String("out"), OutDirectory: cliCtx.String("out"),
ConcurrentRequests: uint64(cliCtx.Int("concurrent-requests")),
} }
totalValid, totalInvalid := fetch.Batches(client, config) totalValid, totalInvalid := fetch.Batches(client, config)
fmt.Printf("Fetched batches in range [%v,%v). Found %v valid & %v invalid batches\n", config.Start, config.End, totalValid, totalInvalid) fmt.Printf("Fetched batches in range [%v,%v). Found %v valid & %v invalid batches\n", config.Start, config.End, totalValid, totalInvalid)
......
echo $1
jq '.frames[] | {timestamp, inclusion_block}' $1
jq '.batches[]|.Timestamp' $1
...@@ -63,11 +63,15 @@ var Subcommands = cli.Commands{ ...@@ -63,11 +63,15 @@ var Subcommands = cli.Commands{
config.SetDeployments(deployments) config.SetDeployments(deployments)
} }
// Do the check after setting the deployments
if err := config.Check(); err != nil { if err := config.Check(); err != nil {
return fmt.Errorf("deploy config at %s invalid: %w", deployConfig, err) return fmt.Errorf("deploy config at %s invalid: %w", deployConfig, err)
} }
// Check the addresses after setting the deployments
if err := config.CheckAddresses(); err != nil {
return fmt.Errorf("deploy config at %s invalid: %w", deployConfig, err)
}
var dump *state.Dump var dump *state.Dump
if l1Allocs := ctx.String("l1-allocs"); l1Allocs != "" { if l1Allocs := ctx.String("l1-allocs"); l1Allocs != "" {
dump, err = genesis.NewStateDump(l1Allocs) dump, err = genesis.NewStateDump(l1Allocs)
......
...@@ -15,8 +15,8 @@ import ( ...@@ -15,8 +15,8 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/retry"
) )
// Deprecated: use eth.SyncStatus instead. // Deprecated: use eth.SyncStatus instead.
...@@ -178,7 +178,7 @@ func (s *Driver) eventLoop() { ...@@ -178,7 +178,7 @@ func (s *Driver) eventLoop() {
var delayedStepReq <-chan time.Time var delayedStepReq <-chan time.Time
// keep track of consecutive failed attempts, to adjust the backoff time accordingly // keep track of consecutive failed attempts, to adjust the backoff time accordingly
bOffStrategy := backoff.Exponential() bOffStrategy := retry.Exponential()
stepAttempts := 0 stepAttempts := 0
// step requests a derivation step to be taken. Won't deadlock if the channel is full. // step requests a derivation step to be taken. Won't deadlock if the channel is full.
......
...@@ -11,8 +11,8 @@ import ( ...@@ -11,8 +11,8 @@ import (
"github.com/ethereum-optimism/optimism/op-node/client" "github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/sources/caching" "github.com/ethereum-optimism/optimism/op-node/sources/caching"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/retry"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peer"
...@@ -130,10 +130,10 @@ func (s *SyncClient) eventLoop() { ...@@ -130,10 +130,10 @@ func (s *SyncClient) eventLoop() {
defer s.wg.Done() defer s.wg.Done()
s.log.Info("Starting sync client event loop") s.log.Info("Starting sync client event loop")
backoffStrategy := &backoff.ExponentialStrategy{ backoffStrategy := &retry.ExponentialStrategy{
Min: 1000, Min: 1000 * time.Millisecond,
Max: 20_000, Max: 20_000 * time.Millisecond,
MaxJitter: 250, MaxJitter: 250 * time.Millisecond,
} }
for { for {
...@@ -142,7 +142,7 @@ func (s *SyncClient) eventLoop() { ...@@ -142,7 +142,7 @@ func (s *SyncClient) eventLoop() {
s.log.Debug("Shutting down RPC sync worker") s.log.Debug("Shutting down RPC sync worker")
return return
case reqNum := <-s.requests: case reqNum := <-s.requests:
_, err := backoff.Do(s.resCtx, 5, backoffStrategy, func() (interface{}, error) { _, err := retry.Do(s.resCtx, 5, backoffStrategy, func() (interface{}, error) {
// Limit the maximum time for fetching payloads // Limit the maximum time for fetching payloads
ctx, cancel := context.WithTimeout(s.resCtx, time.Second*10) ctx, cancel := context.WithTimeout(s.resCtx, time.Second*10)
defer cancel() defer cancel()
......
...@@ -4,8 +4,8 @@ import ( ...@@ -4,8 +4,8 @@ import (
"context" "context"
"math" "math"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/retry"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"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"
...@@ -16,19 +16,19 @@ const maxAttempts = math.MaxInt // Succeed or die trying ...@@ -16,19 +16,19 @@ const maxAttempts = math.MaxInt // Succeed or die trying
type RetryingL1Source struct { type RetryingL1Source struct {
logger log.Logger logger log.Logger
source L1Source source L1Source
strategy backoff.Strategy strategy retry.Strategy
} }
func NewRetryingL1Source(logger log.Logger, source L1Source) *RetryingL1Source { func NewRetryingL1Source(logger log.Logger, source L1Source) *RetryingL1Source {
return &RetryingL1Source{ return &RetryingL1Source{
logger: logger, logger: logger,
source: source, source: source,
strategy: backoff.Exponential(), strategy: retry.Exponential(),
} }
} }
func (s *RetryingL1Source) InfoByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, error) { func (s *RetryingL1Source) InfoByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, error) {
return backoff.Do(ctx, maxAttempts, s.strategy, func() (eth.BlockInfo, error) { return retry.Do(ctx, maxAttempts, s.strategy, func() (eth.BlockInfo, error) {
res, err := s.source.InfoByHash(ctx, blockHash) res, err := s.source.InfoByHash(ctx, blockHash)
if err != nil { if err != nil {
s.logger.Warn("Failed to retrieve info", "hash", blockHash, "err", err) s.logger.Warn("Failed to retrieve info", "hash", blockHash, "err", err)
...@@ -38,7 +38,7 @@ func (s *RetryingL1Source) InfoByHash(ctx context.Context, blockHash common.Hash ...@@ -38,7 +38,7 @@ func (s *RetryingL1Source) InfoByHash(ctx context.Context, blockHash common.Hash
} }
func (s *RetryingL1Source) InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error) { func (s *RetryingL1Source) InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error) {
return backoff.Do2(ctx, maxAttempts, s.strategy, func() (eth.BlockInfo, types.Transactions, error) { return retry.Do2(ctx, maxAttempts, s.strategy, func() (eth.BlockInfo, types.Transactions, error) {
i, t, err := s.source.InfoAndTxsByHash(ctx, blockHash) i, t, err := s.source.InfoAndTxsByHash(ctx, blockHash)
if err != nil { if err != nil {
s.logger.Warn("Failed to retrieve l1 info and txs", "hash", blockHash, "err", err) s.logger.Warn("Failed to retrieve l1 info and txs", "hash", blockHash, "err", err)
...@@ -48,7 +48,7 @@ func (s *RetryingL1Source) InfoAndTxsByHash(ctx context.Context, blockHash commo ...@@ -48,7 +48,7 @@ func (s *RetryingL1Source) InfoAndTxsByHash(ctx context.Context, blockHash commo
} }
func (s *RetryingL1Source) FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error) { func (s *RetryingL1Source) FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error) {
return backoff.Do2(ctx, maxAttempts, s.strategy, func() (eth.BlockInfo, types.Receipts, error) { return retry.Do2(ctx, maxAttempts, s.strategy, func() (eth.BlockInfo, types.Receipts, error) {
i, r, err := s.source.FetchReceipts(ctx, blockHash) i, r, err := s.source.FetchReceipts(ctx, blockHash)
if err != nil { if err != nil {
s.logger.Warn("Failed to fetch receipts", "hash", blockHash, "err", err) s.logger.Warn("Failed to fetch receipts", "hash", blockHash, "err", err)
...@@ -62,11 +62,11 @@ var _ L1Source = (*RetryingL1Source)(nil) ...@@ -62,11 +62,11 @@ var _ L1Source = (*RetryingL1Source)(nil)
type RetryingL2Source struct { type RetryingL2Source struct {
logger log.Logger logger log.Logger
source L2Source source L2Source
strategy backoff.Strategy strategy retry.Strategy
} }
func (s *RetryingL2Source) InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error) { func (s *RetryingL2Source) InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error) {
return backoff.Do2(ctx, maxAttempts, s.strategy, func() (eth.BlockInfo, types.Transactions, error) { return retry.Do2(ctx, maxAttempts, s.strategy, func() (eth.BlockInfo, types.Transactions, error) {
i, t, err := s.source.InfoAndTxsByHash(ctx, blockHash) i, t, err := s.source.InfoAndTxsByHash(ctx, blockHash)
if err != nil { if err != nil {
s.logger.Warn("Failed to retrieve l2 info and txs", "hash", blockHash, "err", err) s.logger.Warn("Failed to retrieve l2 info and txs", "hash", blockHash, "err", err)
...@@ -76,7 +76,7 @@ func (s *RetryingL2Source) InfoAndTxsByHash(ctx context.Context, blockHash commo ...@@ -76,7 +76,7 @@ func (s *RetryingL2Source) InfoAndTxsByHash(ctx context.Context, blockHash commo
} }
func (s *RetryingL2Source) NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) { func (s *RetryingL2Source) NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) {
return backoff.Do(ctx, maxAttempts, s.strategy, func() ([]byte, error) { return retry.Do(ctx, maxAttempts, s.strategy, func() ([]byte, error) {
n, err := s.source.NodeByHash(ctx, hash) n, err := s.source.NodeByHash(ctx, hash)
if err != nil { if err != nil {
s.logger.Warn("Failed to retrieve node", "hash", hash, "err", err) s.logger.Warn("Failed to retrieve node", "hash", hash, "err", err)
...@@ -86,7 +86,7 @@ func (s *RetryingL2Source) NodeByHash(ctx context.Context, hash common.Hash) ([] ...@@ -86,7 +86,7 @@ func (s *RetryingL2Source) NodeByHash(ctx context.Context, hash common.Hash) ([]
} }
func (s *RetryingL2Source) CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) { func (s *RetryingL2Source) CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) {
return backoff.Do(ctx, maxAttempts, s.strategy, func() ([]byte, error) { return retry.Do(ctx, maxAttempts, s.strategy, func() ([]byte, error) {
c, err := s.source.CodeByHash(ctx, hash) c, err := s.source.CodeByHash(ctx, hash)
if err != nil { if err != nil {
s.logger.Warn("Failed to retrieve code", "hash", hash, "err", err) s.logger.Warn("Failed to retrieve code", "hash", hash, "err", err)
...@@ -96,7 +96,7 @@ func (s *RetryingL2Source) CodeByHash(ctx context.Context, hash common.Hash) ([] ...@@ -96,7 +96,7 @@ func (s *RetryingL2Source) CodeByHash(ctx context.Context, hash common.Hash) ([]
} }
func (s *RetryingL2Source) OutputByRoot(ctx context.Context, root common.Hash) (eth.Output, error) { func (s *RetryingL2Source) OutputByRoot(ctx context.Context, root common.Hash) (eth.Output, error) {
return backoff.Do(ctx, maxAttempts, s.strategy, func() (eth.Output, error) { return retry.Do(ctx, maxAttempts, s.strategy, func() (eth.Output, error) {
o, err := s.source.OutputByRoot(ctx, root) o, err := s.source.OutputByRoot(ctx, root)
if err != nil { if err != nil {
s.logger.Warn("Failed to fetch l2 output", "root", root, "err", err) s.logger.Warn("Failed to fetch l2 output", "root", root, "err", err)
...@@ -110,7 +110,7 @@ func NewRetryingL2Source(logger log.Logger, source L2Source) *RetryingL2Source { ...@@ -110,7 +110,7 @@ func NewRetryingL2Source(logger log.Logger, source L2Source) *RetryingL2Source {
return &RetryingL2Source{ return &RetryingL2Source{
logger: logger, logger: logger,
source: source, source: source,
strategy: backoff.Exponential(), strategy: retry.Exponential(),
} }
} }
......
...@@ -7,8 +7,8 @@ import ( ...@@ -7,8 +7,8 @@ import (
"github.com/ethereum-optimism/optimism/op-node/testlog" "github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testutils" "github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/retry"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"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"
...@@ -104,8 +104,8 @@ func createL1Source(t *testing.T) (*RetryingL1Source, *testutils.MockL1Source) { ...@@ -104,8 +104,8 @@ func createL1Source(t *testing.T) (*RetryingL1Source, *testutils.MockL1Source) {
logger := testlog.Logger(t, log.LvlDebug) logger := testlog.Logger(t, log.LvlDebug)
mock := &testutils.MockL1Source{} mock := &testutils.MockL1Source{}
source := NewRetryingL1Source(logger, mock) source := NewRetryingL1Source(logger, mock)
// Avoid sleeping in tests by using a fixed backoff strategy with no delay // Avoid sleeping in tests by using a fixed retry strategy with no delay
source.strategy = backoff.Fixed(0) source.strategy = retry.Fixed(0)
return source, mock return source, mock
} }
...@@ -217,8 +217,8 @@ func createL2Source(t *testing.T) (*RetryingL2Source, *MockL2Source) { ...@@ -217,8 +217,8 @@ func createL2Source(t *testing.T) (*RetryingL2Source, *MockL2Source) {
logger := testlog.Logger(t, log.LvlDebug) logger := testlog.Logger(t, log.LvlDebug)
mock := &MockL2Source{} mock := &MockL2Source{}
source := NewRetryingL2Source(logger, mock) source := NewRetryingL2Source(logger, mock)
// Avoid sleeping in tests by using a fixed backoff strategy with no delay // Avoid sleeping in tests by using a fixed retry strategy with no delay
source.strategy = backoff.Fixed(0) source.strategy = retry.Fixed(0)
return source, mock return source, mock
} }
......
...@@ -7,7 +7,7 @@ import ( ...@@ -7,7 +7,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/client" "github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/sources" "github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-service/backoff" "github.com/ethereum-optimism/optimism/op-service/retry"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
...@@ -49,8 +49,8 @@ func DialRollupClientWithTimeout(timeout time.Duration, log log.Logger, url stri ...@@ -49,8 +49,8 @@ func DialRollupClientWithTimeout(timeout time.Duration, log log.Logger, url stri
// Dials a JSON-RPC endpoint repeatedly, with a backoff, until a client connection is established. Auth is optional. // Dials a JSON-RPC endpoint repeatedly, with a backoff, until a client connection is established. Auth is optional.
func dialRPCClientWithBackoff(ctx context.Context, log log.Logger, addr string) (*rpc.Client, error) { func dialRPCClientWithBackoff(ctx context.Context, log log.Logger, addr string) (*rpc.Client, error) {
bOff := backoff.Fixed(defaultRetryTime) bOff := retry.Fixed(defaultRetryTime)
return backoff.Do(ctx, defaultRetryCount, bOff, func() (*rpc.Client, error) { return retry.Do(ctx, defaultRetryCount, bOff, func() (*rpc.Client, error) {
if !client.IsURLAvailable(addr) { if !client.IsURLAvailable(addr) {
log.Warn("failed to dial address, but may connect later", "addr", addr) log.Warn("failed to dial address, but may connect later", "addr", addr)
return nil, fmt.Errorf("address unavailable (%s)", addr) return nil, fmt.Errorf("address unavailable (%s)", addr)
......
package backoff package retry
import ( import (
"context" "context"
......
package backoff package retry
import ( import (
"context" "context"
......
package backoff package retry
import ( import (
"math" "math"
...@@ -16,35 +16,34 @@ type Strategy interface { ...@@ -16,35 +16,34 @@ type Strategy interface {
// ExponentialStrategy performs exponential backoff. The exponential backoff // ExponentialStrategy performs exponential backoff. The exponential backoff
// function is min(e.Min + (2^attempt * 1000) + randBetween(0, e.MaxJitter), e.Max) // function is min(e.Min + (2^attempt * 1000) + randBetween(0, e.MaxJitter), e.Max)
type ExponentialStrategy struct { type ExponentialStrategy struct {
// Min is the minimum amount of time to wait between attempts in ms. // Min is the minimum amount of time to wait between attempts.
Min float64 Min time.Duration
// Max is the maximum amount of time to wait between attempts in ms. // Max is the maximum amount of time to wait between attempts.
Max float64 Max time.Duration
// MaxJitter is the maximum amount of random jitter to insert between // MaxJitter is the maximum amount of random jitter to insert between attempts.
// attempts in ms. MaxJitter time.Duration
MaxJitter int
} }
func (e *ExponentialStrategy) Duration(attempt int) time.Duration { func (e *ExponentialStrategy) Duration(attempt int) time.Duration {
var jitter int var jitter time.Duration
if e.MaxJitter > 0 { if e.MaxJitter > 0 {
jitter = rand.Intn(e.MaxJitter) jitter = time.Duration(rand.Int63n(e.MaxJitter.Nanoseconds()))
} }
dur := e.Min + (math.Pow(2, float64(attempt)) * 1000) dur := e.Min + time.Duration(int(math.Pow(2, float64(attempt))*1000))*time.Millisecond
dur += float64(jitter) dur += jitter
if dur > e.Max { if dur > e.Max {
return time.Millisecond * time.Duration(e.Max) return e.Max
} }
return time.Millisecond * time.Duration(dur) return dur
} }
func Exponential() Strategy { func Exponential() Strategy {
return &ExponentialStrategy{ return &ExponentialStrategy{
Max: 10000, Max: time.Duration(10000 * time.Millisecond),
MaxJitter: 250, MaxJitter: time.Duration(250 * time.Millisecond),
} }
} }
......
package backoff package retry
import ( import (
"testing" "testing"
...@@ -9,13 +9,13 @@ import ( ...@@ -9,13 +9,13 @@ import (
func TestExponential(t *testing.T) { func TestExponential(t *testing.T) {
strategy := &ExponentialStrategy{ strategy := &ExponentialStrategy{
Min: 3000, Min: 3000 * time.Millisecond,
Max: 10000, Max: 10000 * time.Millisecond,
MaxJitter: 0, MaxJitter: 0,
} }
durations := []int{4, 5, 7, 10, 10} durations := []time.Duration{4, 5, 7, 10, 10}
for i, dur := range durations { for i, dur := range durations {
require.Equal(t, time.Millisecond*time.Duration(dur*1000), strategy.Duration(i)) require.Equal(t, dur*time.Second, strategy.Duration(i))
} }
} }
...@@ -17,7 +17,7 @@ import ( ...@@ -17,7 +17,7 @@ import (
"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-service/backoff" "github.com/ethereum-optimism/optimism/op-service/retry"
"github.com/ethereum-optimism/optimism/op-service/txmgr/metrics" "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics"
) )
...@@ -176,7 +176,7 @@ func (m *SimpleTxManager) send(ctx context.Context, candidate TxCandidate) (*typ ...@@ -176,7 +176,7 @@ func (m *SimpleTxManager) send(ctx context.Context, candidate TxCandidate) (*typ
ctx, cancel = context.WithTimeout(ctx, m.cfg.TxSendTimeout) ctx, cancel = context.WithTimeout(ctx, m.cfg.TxSendTimeout)
defer cancel() defer cancel()
} }
tx, err := backoff.Do(ctx, 30, backoff.Fixed(2*time.Second), func() (*types.Transaction, error) { tx, err := retry.Do(ctx, 30, retry.Fixed(2*time.Second), func() (*types.Transaction, error) {
tx, err := m.craftTx(ctx, candidate) tx, err := m.craftTx(ctx, candidate)
if err != nil { if err != nil {
m.l.Warn("Failed to create a transaction, will retry", "err", err) m.l.Warn("Failed to create a transaction, will retry", "err", err)
......
...@@ -34,31 +34,31 @@ ...@@ -34,31 +34,31 @@
"url": "https://github.com/ethereum-optimism/optimism.git" "url": "https://github.com/ethereum-optimism/optimism.git"
}, },
"dependencies": { "dependencies": {
"@eth-optimism/core-utils": "0.12.3", "@eth-optimism/core-utils": "workspace:*",
"@sentry/node": "^6.3.1", "@sentry/node": "^7.64.0",
"bcfg": "^0.1.7", "bcfg": "^0.2.1",
"body-parser": "^1.20.0", "body-parser": "^1.20.2",
"commander": "^9.0.0", "commander": "^11.0.0",
"dotenv": "^16.0.0", "dotenv": "^16.3.1",
"envalid": "^7.2.2", "envalid": "^7.3.1",
"ethers": "^5.7.0", "ethers": "^5.7.2",
"express": "^4.17.1", "express": "^4.18.2",
"express-prom-bundle": "^6.4.1", "express-prom-bundle": "^6.6.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"pino": "^6.11.3", "pino": "^8.15.0",
"pino-multi-stream": "^5.3.0", "pino-multi-stream": "^6.0.0",
"pino-sentry": "^0.14.0", "pino-sentry": "^0.14.0",
"prom-client": "^13.1.0" "prom-client": "^14.2.0"
}, },
"devDependencies": { "devDependencies": {
"@ethersproject/abstract-provider": "^5.7.0", "@ethersproject/abstract-provider": "^5.7.0",
"@ethersproject/abstract-signer": "^5.7.0", "@ethersproject/abstract-signer": "^5.7.0",
"@types/express": "^4.17.13", "@types/express": "^4.17.17",
"@types/morgan": "^1.9.3", "@types/morgan": "^1.9.4",
"@types/pino": "^6.3.6", "@types/pino": "^7.0.5",
"@types/pino-multi-stream": "^5.1.1", "@types/pino-multi-stream": "^5.1.3",
"chai": "^4.3.4", "chai": "^4.3.7",
"supertest": "^6.1.4" "supertest": "^6.3.3"
} }
} }
...@@ -11,7 +11,7 @@ export const logLevels = [ ...@@ -11,7 +11,7 @@ export const logLevels = [
'error', 'error',
'fatal', 'fatal',
] as const ] as const
export type LogLevel = typeof logLevels[number] export type LogLevel = (typeof logLevels)[number]
export interface LoggerOptions { export interface LoggerOptions {
name: string name: string
......
{ {
"extends": "../../tsconfig.json", "extends": "../../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"rootDir": "./src", "rootDir": "./src",
"outDir": "./dist" "outDir": "./dist",
"skipLibCheck": true
}, },
"include": [ "include": [
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenName": "Optimism", "governanceTokenName": "Optimism",
"governanceTokenOwner": "0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76", "governanceTokenOwner": "0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76",
"l2GenesisBlockGasLimit": "0x17D7840", "l2GenesisBlockGasLimit": "0x2faf080",
"l2GenesisBlockBaseFeePerGas": "0x3b9aca00", "l2GenesisBlockBaseFeePerGas": "0x3b9aca00",
"eip1559Denominator": 50, "eip1559Denominator": 50,
"eip1559Elasticity": 10, "eip1559Elasticity": 10,
......
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
"l1StartingBlockTag": "earliest", "l1StartingBlockTag": "earliest",
"l1ChainID": 900, "l1ChainID": 900,
"l2ChainID": 901, "l2ChainID": 901,
"l2BlockTime": 2, "l2BlockTime": 1,
"l1BlockTime": 2,
"maxSequencerDrift": 300, "maxSequencerDrift": 300,
"sequencerWindowSize": 15, "sequencerWindowSize": 15,
"channelTimeout": 40, "channelTimeout": 40,
...@@ -35,7 +36,6 @@ ...@@ -35,7 +36,6 @@
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "governanceTokenOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
"finalizationPeriodSeconds": 2, "finalizationPeriodSeconds": 2,
"numDeployConfirmations": 1,
"eip1559Denominator": 50, "eip1559Denominator": 50,
"eip1559Elasticity": 10, "eip1559Elasticity": 10,
"l2GenesisRegolithTimeOffset": "0x0", "l2GenesisRegolithTimeOffset": "0x0",
......
{ {
"finalSystemOwner": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF", "finalSystemOwner": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF",
"portalGuardian": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF", "portalGuardian": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF",
"controller": "0x2d30335B0b807bBa1682C487BaAFD2Ad6da5D675",
"l1StartingBlockTag": "0x19c7e6b18fe156e45f4cfef707294fd8f079fa9c30a7b7cd6ec1ce3682ec6a2e", "l1StartingBlockTag": "0x19c7e6b18fe156e45f4cfef707294fd8f079fa9c30a7b7cd6ec1ce3682ec6a2e",
"l1ChainID": 5, "l1ChainID": 5,
"l2ChainID": 998, "l2ChainID": 998,
"l2BlockTime": 2, "l2BlockTime": 2,
"l1BlockTime": 12,
"maxSequencerDrift": 1200, "maxSequencerDrift": 1200,
"sequencerWindowSize": 3600, "sequencerWindowSize": 3600,
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
"l2OutputOracleStartingBlockNumber": 1, "l2OutputOracleStartingBlockNumber": 1,
"l2OutputOracleProposer": "0xE06d39D4B8DC05E562353F060DED346AC4acC077", "l2OutputOracleProposer": "0xE06d39D4B8DC05E562353F060DED346AC4acC077",
"l2OutputOracleChallenger": "0xE06d39D4B8DC05E562353F060DED346AC4acC077", "l2OutputOracleChallenger": "0xE06d39D4B8DC05E562353F060DED346AC4acC077",
"finalizationPeriodSeconds": 2, "finalizationPeriodSeconds": 12,
"proxyAdminOwner": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF", "proxyAdminOwner": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF",
"baseFeeVaultRecipient": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF", "baseFeeVaultRecipient": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF",
...@@ -46,5 +46,7 @@ ...@@ -46,5 +46,7 @@
"gasPriceOracleOverhead": 2100, "gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000, "gasPriceOracleScalar": 1000000,
"eip1559Denominator": 50, "eip1559Denominator": 50,
"eip1559Elasticity": 10 "eip1559Elasticity": 10,
"systemConfigStartBlock": 8364212
} }
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
"l1ChainID": 1, "l1ChainID": 1,
"l2ChainID": 10, "l2ChainID": 10,
"l2BlockTime": 2, "l2BlockTime": 2,
"l1BlockTime": 12,
"maxSequencerDrift": 600, "maxSequencerDrift": 600,
"sequencerWindowSize": 3600, "sequencerWindowSize": 3600,
"channelTimeout": 300, "channelTimeout": 300,
...@@ -32,7 +33,6 @@ ...@@ -32,7 +33,6 @@
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenOwner": "0x5C4e7Ba1E219E47948e6e3F55019A647bA501005", "governanceTokenOwner": "0x5C4e7Ba1E219E47948e6e3F55019A647bA501005",
"l2GenesisBlockGasLimit": "0x1c9c380", "l2GenesisBlockGasLimit": "0x1c9c380",
"l2GenesisBlockCoinbase": "0x4200000000000000000000000000000000000011",
"l2GenesisBlockBaseFeePerGas": "0x3b9aca00", "l2GenesisBlockBaseFeePerGas": "0x3b9aca00",
"gasPriceOracleOverhead": 188, "gasPriceOracleOverhead": 188,
"gasPriceOracleScalar": 684000, "gasPriceOracleScalar": 684000,
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
"gas-snapshot": "pnpm build:differential && pnpm build:fuzz && forge snapshot --no-match-test 'testDiff|testFuzz|invariant|generateArtifact'", "gas-snapshot": "pnpm build:differential && pnpm build:fuzz && forge snapshot --no-match-test 'testDiff|testFuzz|invariant|generateArtifact'",
"storage-snapshot": "./scripts/storage-snapshot.sh", "storage-snapshot": "./scripts/storage-snapshot.sh",
"semver-lock": "forge script scripts/SemverLock.s.sol", "semver-lock": "forge script scripts/SemverLock.s.sol",
"validate-deploy-configs": "./scripts/validate-deploy-configs.sh", "validate-deploy-configs": "./scripts/check-deploy-configs.sh",
"validate-spacers": "pnpm build && npx tsx scripts/validate-spacers.ts", "validate-spacers": "pnpm build && npx tsx scripts/validate-spacers.ts",
"slither": "./scripts/slither.sh", "slither": "./scripts/slither.sh",
"slither:triage": "TRIAGE_MODE=1 ./scripts/slither.sh", "slither:triage": "TRIAGE_MODE=1 ./scripts/slither.sh",
......
#!/usr/bin/env bash
# This script is used to check for valid deploy configs.
# It should check all configs and return a non-zero exit code if any of them are invalid.
# getting-started.json isn't valid JSON so its skipped.
code=$?
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
CONTRACTS_BASE=$(dirname $SCRIPT_DIR)
MONOREPO_BASE=$(dirname $(dirname $CONTRACTS_BASE))
for config in $CONTRACTS_BASE/deploy-config/*.json; do
if grep -q "getting-started" <<< "$config"; then
echo "Skipping getting-started.json"
continue
fi
go run $MONOREPO_BASE/op-chain-ops/cmd/check-deploy-config/main.go --path $config
[ $? -eq 0 ] || code=$?
done
exit $code
...@@ -61,8 +61,6 @@ abstract contract SafeBuilder is EnhancedScript, GlobalConstants { ...@@ -61,8 +61,6 @@ abstract contract SafeBuilder is EnhancedScript, GlobalConstants {
proxyAdmin = 0x01d3670863c3F4b24D7b107900f0b75d4BbC6e0d; proxyAdmin = 0x01d3670863c3F4b24D7b107900f0b75d4BbC6e0d;
} }
console.log("ChainID: %s", block.chainid); console.log("ChainID: %s", block.chainid);
console.log("Using Safe: %s", safe);
console.log("Using ProxyAdmin: %s", proxyAdmin);
return run(safe, proxyAdmin); return run(safe, proxyAdmin);
} }
......
...@@ -97,11 +97,15 @@ contract Multichain is SafeBuilder { ...@@ -97,11 +97,15 @@ contract Multichain is SafeBuilder {
// Set the network in storage // Set the network in storage
NETWORK = vm.envOr("NETWORK", GOERLI_PROD); NETWORK = vm.envOr("NETWORK", GOERLI_PROD);
// TODO: hack
PROXY_ADMIN = ProxyAdmin(vm.envOr("PROXY_ADMIN", 0x01d3670863c3F4b24D7b107900f0b75d4BbC6e0d));
// For simple comparisons of dynamic types // For simple comparisons of dynamic types
bytes32 network = keccak256(bytes(NETWORK)); bytes32 network = keccak256(bytes(NETWORK));
string memory deployConfigPath; string memory deployConfigPath;
if (network == goerli) { if (network == goerli) {
console.log("Using goerli-prod");
deployConfigPath = string.concat(vm.projectRoot(), "/deploy-config/goerli.json"); deployConfigPath = string.concat(vm.projectRoot(), "/deploy-config/goerli.json");
proxies = ContractSet({ proxies = ContractSet({
L1CrossDomainMessenger: 0x5086d1eEF304eb5284A0f6720f79403b4e9bE294, L1CrossDomainMessenger: 0x5086d1eEF304eb5284A0f6720f79403b4e9bE294,
...@@ -113,6 +117,7 @@ contract Multichain is SafeBuilder { ...@@ -113,6 +117,7 @@ contract Multichain is SafeBuilder {
L1ERC721Bridge: 0x8DD330DdE8D9898d43b4dc840Da27A07dF91b3c9 L1ERC721Bridge: 0x8DD330DdE8D9898d43b4dc840Da27A07dF91b3c9
}); });
} else if (network == chaosnet) { } else if (network == chaosnet) {
console.log("Using chaosnet");
deployConfigPath = string.concat(vm.projectRoot(), "/deploy-config/chaosnet.json"); deployConfigPath = string.concat(vm.projectRoot(), "/deploy-config/chaosnet.json");
proxies = ContractSet({ proxies = ContractSet({
L1CrossDomainMessenger: 0xfc428D28D197fFf99A5EbAc6be8B761FEd8718Da, L1CrossDomainMessenger: 0xfc428D28D197fFf99A5EbAc6be8B761FEd8718Da,
...@@ -124,6 +129,7 @@ contract Multichain is SafeBuilder { ...@@ -124,6 +129,7 @@ contract Multichain is SafeBuilder {
L1ERC721Bridge: 0x058BBf091232afE99BC2481F809254cD15e64Df5 L1ERC721Bridge: 0x058BBf091232afE99BC2481F809254cD15e64Df5
}); });
} else if (network == devnet) { } else if (network == devnet) {
console.log("Using devnet");
deployConfigPath = string.concat(vm.projectRoot(), "/deploy-config/internal-devnet.json"); deployConfigPath = string.concat(vm.projectRoot(), "/deploy-config/internal-devnet.json");
proxies = ContractSet({ proxies = ContractSet({
L1CrossDomainMessenger: 0x71A046D793C71af209960DCb8bD5388d2c5D2a78, L1CrossDomainMessenger: 0x71A046D793C71af209960DCb8bD5388d2c5D2a78,
......
...@@ -8,6 +8,8 @@ import { Semver } from "src/universal/Semver.sol"; ...@@ -8,6 +8,8 @@ import { Semver } from "src/universal/Semver.sol";
import { IDisputeGame } from "./interfaces/IDisputeGame.sol"; import { IDisputeGame } from "./interfaces/IDisputeGame.sol";
import { IDisputeGameFactory } from "./interfaces/IDisputeGameFactory.sol"; import { IDisputeGameFactory } from "./interfaces/IDisputeGameFactory.sol";
import { LibGameId } from "src/dispute/lib/LibGameId.sol";
import "src/libraries/DisputeTypes.sol"; import "src/libraries/DisputeTypes.sol";
import "src/libraries/DisputeErrors.sol"; import "src/libraries/DisputeErrors.sol";
...@@ -35,7 +37,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -35,7 +37,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
GameId[] internal _disputeGameList; GameId[] internal _disputeGameList;
/// @notice constructs a new DisputeGameFactory contract. /// @notice constructs a new DisputeGameFactory contract.
constructor() OwnableUpgradeable() Semver(0, 0, 4) { constructor() OwnableUpgradeable() Semver(0, 0, 5) {
initialize(address(0)); initialize(address(0));
} }
...@@ -59,61 +61,67 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -59,61 +61,67 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
) )
external external
view view
returns (IDisputeGame proxy_, uint256 timestamp_) returns (IDisputeGame proxy_, Timestamp timestamp_)
{ {
Hash uuid = getGameUUID(_gameType, _rootClaim, _extraData); Hash uuid = getGameUUID(_gameType, _rootClaim, _extraData);
GameId slot = _disputeGames[uuid]; (, timestamp_, proxy_) = _disputeGames[uuid].unpack();
(address addr, uint256 timestamp) = _unpackSlot(slot);
proxy_ = IDisputeGame(addr);
timestamp_ = timestamp;
} }
/// @inheritdoc IDisputeGameFactory /// @inheritdoc IDisputeGameFactory
function gameAtIndex(uint256 _index) external view returns (IDisputeGame proxy_, uint256 timestamp_) { function gameAtIndex(uint256 _index)
GameId slot = _disputeGameList[_index]; external
(address addr, uint256 timestamp) = _unpackSlot(slot); view
proxy_ = IDisputeGame(addr); returns (GameType gameType_, Timestamp timestamp_, IDisputeGame proxy_)
timestamp_ = timestamp; {
(gameType_, timestamp_, proxy_) = _disputeGameList[_index].unpack();
} }
/// @inheritdoc IDisputeGameFactory /// @inheritdoc IDisputeGameFactory
function create( function create(
GameType gameType, GameType _gameType,
Claim rootClaim, Claim _rootClaim,
bytes calldata extraData bytes calldata _extraData
) )
external external
returns (IDisputeGame proxy) returns (IDisputeGame proxy_)
{ {
// Grab the implementation contract for the given `GameType`. // Grab the implementation contract for the given `GameType`.
IDisputeGame impl = gameImpls[gameType]; IDisputeGame impl = gameImpls[_gameType];
// If there is no implementation to clone for the given `GameType`, revert. // If there is no implementation to clone for the given `GameType`, revert.
if (address(impl) == address(0)) revert NoImplementation(gameType); if (address(impl) == address(0)) revert NoImplementation(_gameType);
// Clone the implementation contract and initialize it with the given parameters. // Clone the implementation contract and initialize it with the given parameters.
proxy = IDisputeGame(address(impl).clone(abi.encodePacked(rootClaim, extraData))); proxy_ = IDisputeGame(address(impl).clone(abi.encodePacked(_rootClaim, _extraData)));
proxy.initialize(); proxy_.initialize();
// Compute the unique identifier for the dispute game. // Compute the unique identifier for the dispute game.
Hash uuid = getGameUUID(gameType, rootClaim, extraData); Hash uuid = getGameUUID(_gameType, _rootClaim, _extraData);
// If a dispute game with the same UUID already exists, revert. // If a dispute game with the same UUID already exists, revert.
if (GameId.unwrap(_disputeGames[uuid]) != bytes32(0)) revert GameAlreadyExists(uuid); if (GameId.unwrap(_disputeGames[uuid]) != bytes32(0)) revert GameAlreadyExists(uuid);
GameId slot = _packSlot(address(proxy), block.timestamp); GameId id = LibGameId.pack(_gameType, Timestamp.wrap(uint64(block.timestamp)), proxy_);
// Store the dispute game in the mapping & emit the `DisputeGameCreated` event. // Store the dispute game id in the mapping & emit the `DisputeGameCreated` event.
_disputeGames[uuid] = slot; _disputeGames[uuid] = id;
_disputeGameList.push(slot); _disputeGameList.push(id);
emit DisputeGameCreated(address(proxy), gameType, rootClaim); emit DisputeGameCreated(address(proxy_), _gameType, _rootClaim);
} }
/// @inheritdoc IDisputeGameFactory /// @inheritdoc IDisputeGameFactory
function getGameUUID(GameType gameType, Claim rootClaim, bytes memory extraData) public pure returns (Hash _uuid) { function getGameUUID(
GameType _gameType,
Claim _rootClaim,
bytes memory _extraData
)
public
pure
returns (Hash uuid_)
{
assembly { assembly {
// Grab the offsets of the other memory locations we will need to temporarily overwrite. // Grab the offsets of the other memory locations we will need to temporarily overwrite.
let gameTypeOffset := sub(extraData, 0x60) let gameTypeOffset := sub(_extraData, 0x60)
let rootClaimOffset := add(gameTypeOffset, 0x20) let rootClaimOffset := add(gameTypeOffset, 0x20)
let pointerOffset := add(rootClaimOffset, 0x20) let pointerOffset := add(rootClaimOffset, 0x20)
...@@ -124,16 +132,16 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -124,16 +132,16 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
let tempC := mload(pointerOffset) let tempC := mload(pointerOffset)
// Overwrite the memory with the data we want to hash // Overwrite the memory with the data we want to hash
mstore(gameTypeOffset, gameType) mstore(gameTypeOffset, _gameType)
mstore(rootClaimOffset, rootClaim) mstore(rootClaimOffset, _rootClaim)
mstore(pointerOffset, 0x60) mstore(pointerOffset, 0x60)
// Compute the length of the memory to hash // Compute the length of the memory to hash
// `0x60 + 0x20 + extraData.length` rounded to the *next* multiple of 32. // `0x60 + 0x20 + extraData.length` rounded to the *next* multiple of 32.
let hashLen := and(add(mload(extraData), 0x9F), not(0x1F)) let hashLen := and(add(mload(_extraData), 0x9F), not(0x1F))
// Hash the memory to produce the UUID digest // Hash the memory to produce the UUID digest
_uuid := keccak256(gameTypeOffset, hashLen) uuid_ := keccak256(gameTypeOffset, hashLen)
// Restore the memory prior to `extraData` // Restore the memory prior to `extraData`
mstore(gameTypeOffset, tempA) mstore(gameTypeOffset, tempA)
...@@ -143,24 +151,8 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver { ...@@ -143,24 +151,8 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
} }
/// @inheritdoc IDisputeGameFactory /// @inheritdoc IDisputeGameFactory
function setImplementation(GameType gameType, IDisputeGame impl) external onlyOwner { function setImplementation(GameType _gameType, IDisputeGame _impl) external onlyOwner {
gameImpls[gameType] = impl; gameImpls[_gameType] = _impl;
emit ImplementationSet(address(impl), gameType); emit ImplementationSet(address(_impl), _gameType);
}
/// @dev Packs an address and a uint256 into a single bytes32 slot. This
/// is only safe for up to uint96 values.
function _packSlot(address _addr, uint256 _num) internal pure returns (GameId slot_) {
assembly {
slot_ := or(shl(0xa0, _num), _addr)
}
}
/// @dev Unpacks an address and a uint256 from a single bytes32 slot.
function _unpackSlot(GameId _slot) internal pure returns (address addr_, uint256 num_) {
assembly {
addr_ := and(_slot, 0xffffffffffffffffffffffffffffffffffffffff)
num_ := shr(0xa0, _slot)
}
} }
} }
...@@ -20,74 +20,78 @@ interface IDisputeGameFactory { ...@@ -20,74 +20,78 @@ interface IDisputeGameFactory {
event ImplementationSet(address indexed impl, GameType indexed gameType); event ImplementationSet(address indexed impl, GameType indexed gameType);
/// @notice The total number of dispute games created by this factory. /// @notice The total number of dispute games created by this factory.
/// @return _gameCount The total number of dispute games created by this factory. /// @return gameCount_ The total number of dispute games created by this factory.
function gameCount() external view returns (uint256 _gameCount); function gameCount() external view returns (uint256 gameCount_);
/// @notice `games` queries an internal mapping that maps the hash of /// @notice `games` queries an internal mapping that maps the hash of
/// `gameType ++ rootClaim ++ extraData` to the deployed `DisputeGame` clone. /// `gameType ++ rootClaim ++ extraData` to the deployed `DisputeGame` clone.
/// @dev `++` equates to concatenation. /// @dev `++` equates to concatenation.
/// @param gameType The type of the DisputeGame - used to decide the proxy implementation /// @param _gameType The type of the DisputeGame - used to decide the proxy implementation
/// @param rootClaim The root claim of the DisputeGame. /// @param _rootClaim The root claim of the DisputeGame.
/// @param extraData Any extra data that should be provided to the created dispute game. /// @param _extraData Any extra data that should be provided to the created dispute game.
/// @return _proxy The clone of the `DisputeGame` created with the given parameters. /// @return proxy_ The clone of the `DisputeGame` created with the given parameters.
/// Returns `address(0)` if nonexistent. /// Returns `address(0)` if nonexistent.
/// @return _timestamp The timestamp of the creation of the dispute game. /// @return timestamp_ The timestamp of the creation of the dispute game.
function games( function games(
GameType gameType, GameType _gameType,
Claim rootClaim, Claim _rootClaim,
bytes calldata extraData bytes calldata _extraData
) )
external external
view view
returns (IDisputeGame _proxy, uint256 _timestamp); returns (IDisputeGame proxy_, Timestamp timestamp_);
/// @notice `gameAtIndex` returns the dispute game contract address and its creation timestamp /// @notice `gameAtIndex` returns the dispute game contract address and its creation timestamp
/// at the given index. Each created dispute game increments the underlying index. /// at the given index. Each created dispute game increments the underlying index.
/// @param _index The index of the dispute game. /// @param _index The index of the dispute game.
/// @return _proxy The clone of the `DisputeGame` created with the given parameters. /// @return gameType_ The type of the DisputeGame - used to decide the proxy implementation.
/// @return timestamp_ The timestamp of the creation of the dispute game.
/// @return proxy_ The clone of the `DisputeGame` created with the given parameters.
/// Returns `address(0)` if nonexistent. /// Returns `address(0)` if nonexistent.
/// @return _timestamp The timestamp of the creation of the dispute game. function gameAtIndex(uint256 _index)
function gameAtIndex(uint256 _index) external view returns (IDisputeGame _proxy, uint256 _timestamp); external
view
returns (GameType gameType_, Timestamp timestamp_, IDisputeGame proxy_);
/// @notice `gameImpls` is a mapping that maps `GameType`s to their respective /// @notice `gameImpls` is a mapping that maps `GameType`s to their respective
/// `IDisputeGame` implementations. /// `IDisputeGame` implementations.
/// @param gameType The type of the dispute game. /// @param _gameType The type of the dispute game.
/// @return _impl The address of the implementation of the game type. /// @return impl_ The address of the implementation of the game type.
/// Will be cloned on creation of a new dispute game with the given `gameType`. /// Will be cloned on creation of a new dispute game with the given `gameType`.
function gameImpls(GameType gameType) external view returns (IDisputeGame _impl); function gameImpls(GameType _gameType) external view returns (IDisputeGame impl_);
/// @notice Creates a new DisputeGame proxy contract. /// @notice Creates a new DisputeGame proxy contract.
/// @param gameType The type of the DisputeGame - used to decide the proxy implementation. /// @param _gameType The type of the DisputeGame - used to decide the proxy implementation.
/// @param rootClaim The root claim of the DisputeGame. /// @param _rootClaim The root claim of the DisputeGame.
/// @param extraData Any extra data that should be provided to the created dispute game. /// @param _extraData Any extra data that should be provided to the created dispute game.
/// @return proxy The address of the created DisputeGame proxy. /// @return proxy_ The address of the created DisputeGame proxy.
function create( function create(
GameType gameType, GameType _gameType,
Claim rootClaim, Claim _rootClaim,
bytes calldata extraData bytes calldata _extraData
) )
external external
returns (IDisputeGame proxy); returns (IDisputeGame proxy_);
/// @notice Sets the implementation contract for a specific `GameType`. /// @notice Sets the implementation contract for a specific `GameType`.
/// @dev May only be called by the `owner`. /// @dev May only be called by the `owner`.
/// @param gameType The type of the DisputeGame. /// @param _gameType The type of the DisputeGame.
/// @param impl The implementation contract for the given `GameType`. /// @param _impl The implementation contract for the given `GameType`.
function setImplementation(GameType gameType, IDisputeGame impl) external; function setImplementation(GameType _gameType, IDisputeGame _impl) external;
/// @notice Returns a unique identifier for the given dispute game parameters. /// @notice Returns a unique identifier for the given dispute game parameters.
/// @dev Hashes the concatenation of `gameType . rootClaim . extraData` /// @dev Hashes the concatenation of `gameType . rootClaim . extraData`
/// without expanding memory. /// without expanding memory.
/// @param gameType The type of the DisputeGame. /// @param _gameType The type of the DisputeGame.
/// @param rootClaim The root claim of the DisputeGame. /// @param _rootClaim The root claim of the DisputeGame.
/// @param extraData Any extra data that should be provided to the created dispute game. /// @param _extraData Any extra data that should be provided to the created dispute game.
/// @return _uuid The unique identifier for the given dispute game parameters. /// @return uuid_ The unique identifier for the given dispute game parameters.
function getGameUUID( function getGameUUID(
GameType gameType, GameType _gameType,
Claim rootClaim, Claim _rootClaim,
bytes memory extraData bytes memory _extraData
) )
external external
pure pure
returns (Hash _uuid); returns (Hash uuid_);
} }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "src/libraries/DisputeTypes.sol";
import "src/dispute/interfaces/IDisputeGame.sol";
/// @title LibGameId
/// @notice Utility functions for packing and unpacking GameIds.
library LibGameId {
/// @notice Packs values into a 32 byte GameId type.
/// @param _gameType The game type.
/// @param _timestamp The timestamp of the game's creation.
/// @param _gameProxy The game proxy address.
/// @return gameId_ The packed GameId.
function pack(
GameType _gameType,
Timestamp _timestamp,
IDisputeGame _gameProxy
)
internal
pure
returns (GameId gameId_)
{
assembly {
gameId_ := or(or(shl(248, _gameType), shl(184, _timestamp)), _gameProxy)
}
}
/// @notice Unpacks values from a 32 byte GameId type.
/// @param _gameId The packed GameId.
/// @return gameType_ The game type.
/// @return timestamp_ The timestamp of the game's creation.
/// @return gameProxy_ The game proxy address.
function unpack(GameId _gameId)
internal
pure
returns (GameType gameType_, Timestamp timestamp_, IDisputeGame gameProxy_)
{
assembly {
gameType_ := shr(248, _gameId)
timestamp_ := shr(184, and(_gameId, not(shl(248, 0xff))))
gameProxy_ := and(_gameId, 0xffffffffffffffffffffffffffffffffffffffff)
}
}
}
...@@ -4,10 +4,12 @@ pragma solidity ^0.8.15; ...@@ -4,10 +4,12 @@ pragma solidity ^0.8.15;
import { LibHashing } from "../dispute/lib/LibHashing.sol"; import { LibHashing } from "../dispute/lib/LibHashing.sol";
import { LibPosition } from "../dispute/lib/LibPosition.sol"; import { LibPosition } from "../dispute/lib/LibPosition.sol";
import { LibClock } from "../dispute/lib/LibClock.sol"; import { LibClock } from "../dispute/lib/LibClock.sol";
import { LibGameId } from "../dispute/lib/LibGameId.sol";
using LibHashing for Claim global; using LibHashing for Claim global;
using LibPosition for Position global; using LibPosition for Position global;
using LibClock for Clock global; using LibClock for Clock global;
using LibGameId for GameId global;
/// @notice A custom type for a generic hash. /// @notice A custom type for a generic hash.
type Hash is bytes32; type Hash is bytes32;
...@@ -29,14 +31,15 @@ type Timestamp is uint64; ...@@ -29,14 +31,15 @@ type Timestamp is uint64;
/// @dev Unit: seconds /// @dev Unit: seconds
type Duration is uint64; type Duration is uint64;
/// @notice A `GameId` represents a packed 12 byte timestamp and a 20 byte address. /// @notice A `GameId` represents a packed 1 byte game ID, an 11 byte timestamp, and a 20 byte address.
/// @dev The packed layout of this type is as follows: /// @dev The packed layout of this type is as follows:
/// ┌────────────┬────────────────┐ /// ┌──────────────────────┐
/// │ Bits │ Value │ /// │ Bits │ Value │
/// ├────────────┼────────────────┤ /// ├───────────┼───────────┤
/// │ [0, 96) │ Timestamp │ /// │ [0, 8) │ Game Type │
/// │ [8, 96) │ Timestamp │
/// │ [96, 256) │ Address │ /// │ [96, 256) │ Address │
/// └────────────┴────────────────┘ /// └──────────────────────┘
type GameId is bytes32; type GameId is bytes32;
/// @notice A `Clock` represents a packed `Duration` and `Timestamp` /// @notice A `Clock` represents a packed `Duration` and `Timestamp`
......
...@@ -51,16 +51,16 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init { ...@@ -51,16 +51,16 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init {
emit DisputeGameCreated(address(0), gt, rootClaim); emit DisputeGameCreated(address(0), gt, rootClaim);
IDisputeGame proxy = factory.create(gt, rootClaim, extraData); IDisputeGame proxy = factory.create(gt, rootClaim, extraData);
(IDisputeGame game, uint256 timestamp) = factory.games(gt, rootClaim, extraData); (IDisputeGame game, Timestamp timestamp) = factory.games(gt, rootClaim, extraData);
// Ensure that the dispute game was assigned to the `disputeGames` mapping. // Ensure that the dispute game was assigned to the `disputeGames` mapping.
assertEq(address(game), address(proxy)); assertEq(address(game), address(proxy));
assertEq(timestamp, block.timestamp); assertEq(Timestamp.unwrap(timestamp), block.timestamp);
assertEq(factory.gameCount(), 1); assertEq(factory.gameCount(), 1);
(IDisputeGame game2, uint256 timestamp2) = factory.gameAtIndex(0); (, Timestamp timestamp2, IDisputeGame game2) = factory.gameAtIndex(0);
assertEq(address(game2), address(proxy)); assertEq(address(game2), address(proxy));
assertEq(timestamp2, block.timestamp); assertEq(Timestamp.unwrap(timestamp2), block.timestamp);
} }
/// @dev Tests that the `create` function reverts when there is no implementation /// @dev Tests that the `create` function reverts when there is no implementation
...@@ -88,10 +88,10 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init { ...@@ -88,10 +88,10 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init {
emit DisputeGameCreated(address(0), gt, rootClaim); emit DisputeGameCreated(address(0), gt, rootClaim);
IDisputeGame proxy = factory.create(gt, rootClaim, extraData); IDisputeGame proxy = factory.create(gt, rootClaim, extraData);
(IDisputeGame game, uint256 timestamp) = factory.games(gt, rootClaim, extraData); (IDisputeGame game, Timestamp timestamp) = factory.games(gt, rootClaim, extraData);
// Ensure that the dispute game was assigned to the `disputeGames` mapping. // Ensure that the dispute game was assigned to the `disputeGames` mapping.
assertEq(address(game), address(proxy)); assertEq(address(game), address(proxy));
assertEq(timestamp, block.timestamp); assertEq(Timestamp.unwrap(timestamp), block.timestamp);
// Ensure that the `create` function reverts when called with parameters that would result in the same UUID. // Ensure that the `create` function reverts when called with parameters that would result in the same UUID.
vm.expectRevert( vm.expectRevert(
...@@ -161,37 +161,6 @@ contract DisputeGameFactory_TransferOwnership_Test is DisputeGameFactory_Init { ...@@ -161,37 +161,6 @@ contract DisputeGameFactory_TransferOwnership_Test is DisputeGameFactory_Init {
} }
} }
/// @title PackingTester
/// @notice Exposes the internal packing functions so that they can be fuzzed
/// in a roundtrip manner.
contract PackingTester is DisputeGameFactory {
function packSlot(address _addr, uint256 _num) external pure returns (GameId) {
return _packSlot(_addr, _num);
}
function unpackSlot(GameId _slot) external pure returns (address, uint256) {
return _unpackSlot(_slot);
}
}
/// @title DisputeGameFactory_PackSlot_Test
/// @notice Fuzzes the PackingTester contract
contract DisputeGameFactory_PackSlot_Test is Test {
PackingTester tester;
function setUp() public {
tester = new PackingTester();
}
/// @dev Tests that the `packSlot` and `unpackSlot` functions roundtrip correctly.
function testFuzz_packSlot_succeeds(address _addr, uint96 _num) public {
GameId slot = tester.packSlot(_addr, uint256(_num));
(address addr, uint256 num) = tester.unpackSlot(slot);
assertEq(addr, _addr);
assertEq(num, _num);
}
}
/// @dev A fake clone used for testing the `DisputeGameFactory` contract's `create` function. /// @dev A fake clone used for testing the `DisputeGameFactory` contract's `create` function.
contract FakeClone { contract FakeClone {
function initialize() external { function initialize() external {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { Test } from "forge-std/Test.sol";
import { LibGameId } from "src/dispute/lib/LibGameId.sol";
import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
import "src/libraries/DisputeTypes.sol";
contract LibGameId_Test is Test {
/// @dev Tests that a round trip of packing and unpacking a GameId maintains the same values.
function testFuzz_gameId_roundTrip_succeeds(
GameType _gameType,
Timestamp _timestamp,
IDisputeGame _gameProxy
)
public
{
GameId gameId = LibGameId.pack(_gameType, _timestamp, _gameProxy);
(GameType gameType_, Timestamp timestamp_, IDisputeGame gameProxy_) = LibGameId.unpack(gameId);
assertEq(GameType.unwrap(gameType_), GameType.unwrap(_gameType));
assertEq(Timestamp.unwrap(timestamp_), Timestamp.unwrap(_timestamp));
assertEq(address(gameProxy_), address(_gameProxy));
}
}
...@@ -12,6 +12,7 @@ ignores: [ ...@@ -12,6 +12,7 @@ ignores: [
"chai", "chai",
"ts-node", "ts-node",
"typedoc", "typedoc",
"typescript",
"ethereum-waffle", "ethereum-waffle",
"nyc" "nyc"
] ]
...@@ -41,30 +41,31 @@ ...@@ -41,30 +41,31 @@
"@ethersproject/transactions": "^5.7.0", "@ethersproject/transactions": "^5.7.0",
"@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1", "@nomiclabs/hardhat-waffle": "^2.0.1",
"@types/chai": "^4.3.5",
"@types/chai-as-promised": "^7.1.5",
"@types/mocha": "^10.0.1",
"@types/node": "^20.5.0",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"ethereum-waffle": "^4.0.10", "ethereum-waffle": "^4.0.10",
"ethers": "^5.7.0", "ethers": "^5.7.2",
"hardhat": "^2.9.6", "hardhat": "^2.9.6",
"hardhat-deploy": "^0.11.4", "hardhat-deploy": "^0.11.4",
"isomorphic-fetch": "^3.0.0", "isomorphic-fetch": "^3.0.0",
"mocha": "^10.0.0", "mocha": "^10.2.0",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typedoc": "^0.22.13", "typedoc": "^0.24.8",
"viem": "^0.3.30", "typescript": "^5.1.6",
"vitest": "^0.28.3", "viem": "^1.6.0",
"zod": "^3.11.6" "vitest": "^0.34.2",
"zod": "^3.22.1"
}, },
"dependencies": { "dependencies": {
"@eth-optimism/contracts": "0.6.0", "@eth-optimism/contracts": "0.6.0",
"@eth-optimism/contracts-bedrock": "0.16.0", "@eth-optimism/contracts-bedrock": "workspace:*",
"@eth-optimism/core-utils": "0.12.3", "@eth-optimism/core-utils": "workspace:*",
"@types/chai": "^4.2.18",
"@types/chai-as-promised": "^7.1.4",
"@types/mocha": "^10.0.1",
"@types/node": "^20.5.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"merkletreejs": "^0.2.27", "merkletreejs": "^0.3.10",
"rlp": "^2.2.7" "rlp": "^2.2.7"
}, },
"peerDependencies": { "peerDependencies": {
......
# @eth-optimism/web3.js-plugin # @eth-optimism/web3.js-plugin
## 0.1.2
### Patch Changes
- [#6873](https://github.com/ethereum-optimism/optimism/pull/6873) [`fdab6caa7`](https://github.com/ethereum-optimism/optimism/commit/fdab6caa7e6684b08882d2a766ccd727068c2b2f) Thanks [@spacesailor24](https://github.com/spacesailor24)! - Update code exmaples in README
## 0.1.1 ## 0.1.1
### Patch Changes ### Patch Changes
......
{ {
"name": "@eth-optimism/web3.js-plugin", "name": "@eth-optimism/web3.js-plugin",
"version": "0.1.1", "version": "0.1.2",
"description": "A Web3.js plugin for doing OP-Chain gas estimation", "description": "A Web3.js plugin for doing OP-Chain gas estimation",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
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