Commit 0a338da9 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into bindings.bridging.base.contracts

parents c118ba2f 6bbb5974
---
'@eth-optimism/chain-mon': minor
'@eth-optimism/core-utils': patch
---
Added a new service wallet-mon to identify unexpected transfers from key accounts
...@@ -33,4 +33,4 @@ ...@@ -33,4 +33,4 @@
"eslint.format.enable": true, "eslint.format.enable": true,
"editorconfig.generateAuto": false, "editorconfig.generateAuto": false,
"files.trimTrailingWhitespace": true "files.trimTrailingWhitespace": true
} }
\ No newline at end of file
...@@ -74,11 +74,11 @@ nuke: clean devnet-clean ...@@ -74,11 +74,11 @@ nuke: clean devnet-clean
.PHONY: nuke .PHONY: nuke
devnet-up: devnet-up:
@bash ./ops-bedrock/devnet-up.sh PYTHONPATH=./bedrock-devnet python3 ./bedrock-devnet/main.py --monorepo-dir=.
.PHONY: devnet-up .PHONY: devnet-up
devnet-up-deploy: devnet-up-deploy:
PYTHONPATH=./bedrock-devnet python3 ./bedrock-devnet/main.py --monorepo-dir=. PYTHONPATH=./bedrock-devnet python3 ./bedrock-devnet/main.py --monorepo-dir=. --deploy
.PHONY: devnet-up-deploy .PHONY: devnet-up-deploy
devnet-down: devnet-down:
......
...@@ -4,74 +4,142 @@ import os ...@@ -4,74 +4,142 @@ import os
import subprocess import subprocess
import json import json
import socket import socket
import calendar
import datetime
import time import time
import shutil import shutil
import devnet.log_setup import devnet.log_setup
from devnet.genesis import GENESIS_TMPL from devnet.genesis import GENESIS_TMPL
pjoin = os.path.join
parser = argparse.ArgumentParser(description='Bedrock devnet launcher') parser = argparse.ArgumentParser(description='Bedrock devnet launcher')
parser.add_argument('--monorepo-dir', help='Directory of the monorepo', default=os.getcwd()) parser.add_argument('--monorepo-dir', help='Directory of the monorepo', default=os.getcwd())
parser.add_argument('--deploy', help='Whether the contracts should be predeployed or deployed', type=bool, action=argparse.BooleanOptionalAction)
log = logging.getLogger() log = logging.getLogger()
class Bunch:
def __init__(self, **kwds):
self.__dict__.update(kwds)
def main(): def main():
args = parser.parse_args() args = parser.parse_args()
pjoin = os.path.join
monorepo_dir = os.path.abspath(args.monorepo_dir) monorepo_dir = os.path.abspath(args.monorepo_dir)
devnet_dir = pjoin(monorepo_dir, '.devnet') devnet_dir = pjoin(monorepo_dir, '.devnet')
ops_bedrock_dir = pjoin(monorepo_dir, 'ops-bedrock')
contracts_bedrock_dir = pjoin(monorepo_dir, 'packages', 'contracts-bedrock') contracts_bedrock_dir = pjoin(monorepo_dir, 'packages', 'contracts-bedrock')
deployment_dir = pjoin(contracts_bedrock_dir, 'deployments', 'devnetL1') deployment_dir = pjoin(contracts_bedrock_dir, 'deployments', 'devnetL1')
op_node_dir = pjoin(args.monorepo_dir, 'op-node') op_node_dir = pjoin(args.monorepo_dir, 'op-node')
genesis_l1_path = pjoin(devnet_dir, 'genesis-l1.json') ops_bedrock_dir=pjoin(monorepo_dir, 'ops-bedrock')
genesis_l2_path = pjoin(devnet_dir, 'genesis-l2.json')
addresses_json_path = pjoin(devnet_dir, 'addresses.json') paths = Bunch(
sdk_addresses_json_path = pjoin(devnet_dir, 'sdk-addresses.json') mono_repo_dir=monorepo_dir,
rollup_config_path = pjoin(devnet_dir, 'rollup.json') devnet_dir=devnet_dir,
contracts_bedrock_dir=contracts_bedrock_dir,
deployment_dir=deployment_dir,
deploy_config_dir=pjoin(contracts_bedrock_dir, 'deploy-config'),
op_node_dir=op_node_dir,
ops_bedrock_dir=ops_bedrock_dir,
genesis_l1_path=pjoin(devnet_dir, 'genesis-l1.json'),
genesis_l2_path=pjoin(devnet_dir, 'genesis-l2.json'),
addresses_json_path=pjoin(devnet_dir, 'addresses.json'),
sdk_addresses_json_path=pjoin(devnet_dir, 'sdk-addresses.json'),
rollup_config_path=pjoin(devnet_dir, 'rollup.json')
)
os.makedirs(devnet_dir, exist_ok=True) os.makedirs(devnet_dir, exist_ok=True)
if os.path.exists(genesis_l1_path): if args.deploy:
log.info('L2 genesis already generated.') log.info('Devnet with upcoming smart contract deployments')
devnet_deploy(paths)
else:
log.info('Devnet with smart contracts pre-deployed')
devnet_prestate(paths)
# Bring up the devnet where the L1 contracts are in the genesis state
def devnet_prestate(paths):
date = datetime.datetime.utcnow()
utc_time = hex(calendar.timegm(date.utctimetuple()))
done_file = pjoin(paths.devnet_dir, 'done')
if os.path.exists(done_file):
log.info('Genesis files already exist')
else:
log.info('Creating genesis files')
deploy_config_path = pjoin(paths.deploy_config_dir, 'devnetL1.json')
# read the json file
deploy_config = read_json(deploy_config_path)
deploy_config['l1GenesisBlockTimestamp'] = utc_time
temp_deploy_config = pjoin(paths.devnet_dir, 'deploy-config.json')
write_json(temp_deploy_config, deploy_config)
outfile_l1 = paths.genesis_l1_path
outfile_l2 = paths.genesis_l2_path
outfile_rollup = paths.rollup_config_path
run_command(['go', 'run', 'cmd/main.go', 'genesis', 'devnet', '--deploy-config', temp_deploy_config, '--outfile.l1', outfile_l1, '--outfile.l2', outfile_l2, '--outfile.rollup', outfile_rollup], cwd=paths.op_node_dir)
write_json(done_file, {})
log.info('Bringing up L1.')
run_command(['docker-compose', 'up', '-d', 'l1'], cwd=paths.ops_bedrock_dir, env={
'PWD': paths.ops_bedrock_dir
})
wait_up(8545)
log.info('Bringing up L2.')
run_command(['docker-compose', 'up', '-d', 'l2'], cwd=paths.ops_bedrock_dir, env={
'PWD': paths.ops_bedrock_dir
})
wait_up(9545)
log.info('Bringing up the services.')
run_command(['docker-compose', 'up', '-d', 'op-proposer', 'op-batcher'], cwd=paths.ops_bedrock_dir, env={
'PWD': paths.ops_bedrock_dir,
'L2OO_ADDRESS': '0x6900000000000000000000000000000000000000'
})
# Bring up the devnet where the contracts are deployed to L1
def devnet_deploy(paths):
if os.path.exists(paths.genesis_l1_path):
log.info('L1 genesis already generated.')
else: else:
log.info('Generating L1 genesis.') log.info('Generating L1 genesis.')
write_json(genesis_l1_path, GENESIS_TMPL) write_json(paths.genesis_l1_path, GENESIS_TMPL)
log.info('Starting L1.') log.info('Starting L1.')
run_command(['docker-compose', 'up', '-d', 'l1'], cwd=ops_bedrock_dir, env={ run_command(['docker-compose', 'up', '-d', 'l1'], cwd=paths.ops_bedrock_dir, env={
'PWD': ops_bedrock_dir 'PWD': paths.ops_bedrock_dir
}) })
wait_up(8545) wait_up(8545)
log.info('Generating network config.') log.info('Generating network config.')
devnet_cfg_orig = pjoin(contracts_bedrock_dir, 'deploy-config', 'devnetL1.json') devnet_cfg_orig = pjoin(paths.contracts_bedrock_dir, 'deploy-config', 'devnetL1.json')
devnet_cfg_backup = pjoin(devnet_dir, 'devnetL1.json.bak') devnet_cfg_backup = pjoin(paths.devnet_dir, 'devnetL1.json.bak')
shutil.copy(devnet_cfg_orig, devnet_cfg_backup) shutil.copy(devnet_cfg_orig, devnet_cfg_backup)
deploy_config = read_json(devnet_cfg_orig) deploy_config = read_json(devnet_cfg_orig)
deploy_config['l1GenesisBlockTimestamp'] = GENESIS_TMPL['timestamp'] deploy_config['l1GenesisBlockTimestamp'] = GENESIS_TMPL['timestamp']
deploy_config['l1StartingBlockTag'] = 'earliest' deploy_config['l1StartingBlockTag'] = 'earliest'
write_json(devnet_cfg_orig, deploy_config) write_json(devnet_cfg_orig, deploy_config)
if os.path.exists(addresses_json_path): if os.path.exists(paths.addresses_json_path):
log.info('Contracts already deployed.') log.info('Contracts already deployed.')
addresses = read_json(addresses_json_path) addresses = read_json(paths.addresses_json_path)
else: else:
log.info('Deploying contracts.') log.info('Deploying contracts.')
run_command(['yarn', 'hardhat', '--network', 'devnetL1', 'deploy', '--tags', 'l1'], env={ run_command(['yarn', 'hardhat', '--network', 'devnetL1', 'deploy', '--tags', 'l1'], env={
'CHAIN_ID': '900', 'CHAIN_ID': '900',
'L1_RPC': 'http://localhost:8545', 'L1_RPC': 'http://localhost:8545',
'PRIVATE_KEY_DEPLOYER': 'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' 'PRIVATE_KEY_DEPLOYER': 'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
}, cwd=contracts_bedrock_dir) }, cwd=paths.contracts_bedrock_dir)
contracts = os.listdir(deployment_dir) contracts = os.listdir(paths.deployment_dir)
addresses = {} addresses = {}
for c in contracts: for c in contracts:
if not c.endswith('.json'): if not c.endswith('.json'):
continue continue
data = read_json(pjoin(deployment_dir, c)) data = read_json(pjoin(paths.deployment_dir, c))
addresses[c.replace('.json', '')] = data['address'] addresses[c.replace('.json', '')] = data['address']
sdk_addresses = {} sdk_addresses = {}
sdk_addresses.update({ sdk_addresses.update({
...@@ -84,10 +152,10 @@ def main(): ...@@ -84,10 +152,10 @@ def main():
sdk_addresses['L1StandardBridge'] = addresses['Proxy__OVM_L1StandardBridge'] sdk_addresses['L1StandardBridge'] = addresses['Proxy__OVM_L1StandardBridge']
sdk_addresses['OptimismPortal'] = addresses['OptimismPortalProxy'] sdk_addresses['OptimismPortal'] = addresses['OptimismPortalProxy']
sdk_addresses['L2OutputOracle'] = addresses['L2OutputOracleProxy'] sdk_addresses['L2OutputOracle'] = addresses['L2OutputOracleProxy']
write_json(addresses_json_path, addresses) write_json(paths.addresses_json_path, addresses)
write_json(sdk_addresses_json_path, sdk_addresses) write_json(paths.sdk_addresses_json_path, sdk_addresses)
if os.path.exists(genesis_l2_path): if os.path.exists(paths.genesis_l2_path):
log.info('L2 genesis and rollup configs already generated.') log.info('L2 genesis and rollup configs already generated.')
else: else:
log.info('Generating L2 genesis and rollup configs.') log.info('Generating L2 genesis and rollup configs.')
...@@ -95,25 +163,25 @@ def main(): ...@@ -95,25 +163,25 @@ def main():
'go', 'run', 'cmd/main.go', 'genesis', 'l2', 'go', 'run', 'cmd/main.go', 'genesis', 'l2',
'--l1-rpc', 'http://localhost:8545', '--l1-rpc', 'http://localhost:8545',
'--deploy-config', devnet_cfg_orig, '--deploy-config', devnet_cfg_orig,
'--deployment-dir', deployment_dir, '--deployment-dir', paths.deployment_dir,
'--outfile.l2', pjoin(devnet_dir, 'genesis-l2.json'), '--outfile.l2', pjoin(paths.devnet_dir, 'genesis-l2.json'),
'--outfile.rollup', pjoin(devnet_dir, 'rollup.json') '--outfile.rollup', pjoin(paths.devnet_dir, 'rollup.json')
], cwd=op_node_dir) ], cwd=paths.op_node_dir)
rollup_config = read_json(rollup_config_path) rollup_config = read_json(paths.rollup_config_path)
if os.path.exists(devnet_cfg_backup): if os.path.exists(devnet_cfg_backup):
shutil.move(devnet_cfg_backup, devnet_cfg_orig) shutil.move(devnet_cfg_backup, devnet_cfg_orig)
log.info('Bringing up L2.') log.info('Bringing up L2.')
run_command(['docker-compose', 'up', '-d', 'l2'], cwd=ops_bedrock_dir, env={ run_command(['docker-compose', 'up', '-d', 'l2'], cwd=paths.ops_bedrock_dir, env={
'PWD': ops_bedrock_dir 'PWD': paths.ops_bedrock_dir
}) })
wait_up(9545) wait_up(9545)
log.info('Bringing up everything else.') log.info('Bringing up everything else.')
run_command(['docker-compose', 'up', '-d', 'op-node', 'op-proposer', 'op-batcher'], cwd=ops_bedrock_dir, env={ run_command(['docker-compose', 'up', '-d', 'op-node', 'op-proposer', 'op-batcher'], cwd=paths.ops_bedrock_dir, env={
'PWD': ops_bedrock_dir, 'PWD': paths.ops_bedrock_dir,
'L2OO_ADDRESS': addresses['L2OutputOracleProxy'], 'L2OO_ADDRESS': addresses['L2OutputOracleProxy'],
'SEQUENCER_BATCH_INBOX_ADDRESS': rollup_config['batch_inbox_address'] 'SEQUENCER_BATCH_INBOX_ADDRESS': rollup_config['batch_inbox_address']
}) })
......
...@@ -19,7 +19,7 @@ require ( ...@@ -19,7 +19,7 @@ require (
github.com/ipfs/go-datastore v0.6.0 github.com/ipfs/go-datastore v0.6.0
github.com/ipfs/go-ds-leveldb v0.5.0 github.com/ipfs/go-ds-leveldb v0.5.0
github.com/libp2p/go-libp2p v0.25.1 github.com/libp2p/go-libp2p v0.25.1
github.com/libp2p/go-libp2p-pubsub v0.9.0 github.com/libp2p/go-libp2p-pubsub v0.9.3
github.com/libp2p/go-libp2p-testing v0.12.0 github.com/libp2p/go-libp2p-testing v0.12.0
github.com/mattn/go-isatty v0.0.17 github.com/mattn/go-isatty v0.0.17
github.com/multiformats/go-base32 v0.1.0 github.com/multiformats/go-base32 v0.1.0
...@@ -62,7 +62,6 @@ require ( ...@@ -62,7 +62,6 @@ require (
github.com/docker/go-units v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/edsrzf/mmap-go v1.1.0 // indirect
github.com/elastic/gosigar v0.14.2 // indirect github.com/elastic/gosigar v0.14.2 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fjl/memsize v0.0.1 // indirect github.com/fjl/memsize v0.0.1 // indirect
github.com/flynn/noise v1.0.0 // indirect github.com/flynn/noise v1.0.0 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect github.com/francoispqt/gojay v1.2.13 // indirect
...@@ -90,7 +89,6 @@ require ( ...@@ -90,7 +89,6 @@ require (
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c // indirect github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c // indirect
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // indirect github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // indirect
github.com/ipfs/go-cid v0.3.2 // indirect github.com/ipfs/go-cid v0.3.2 // indirect
github.com/ipfs/go-log v1.0.5 // indirect
github.com/ipfs/go-log/v2 v2.5.1 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
......
...@@ -135,8 +135,6 @@ github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZi ...@@ -135,8 +135,6 @@ github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZi
github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=
github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4=
github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
...@@ -332,9 +330,6 @@ github.com/ipfs/go-ds-badger v0.3.0 h1:xREL3V0EH9S219kFFueOYJJTcjgNSZ2HY1iSvN7U1 ...@@ -332,9 +330,6 @@ github.com/ipfs/go-ds-badger v0.3.0 h1:xREL3V0EH9S219kFFueOYJJTcjgNSZ2HY1iSvN7U1
github.com/ipfs/go-ds-leveldb v0.5.0 h1:s++MEBbD3ZKc9/8/njrn4flZLnCuY9I79v94gBUNumo= github.com/ipfs/go-ds-leveldb v0.5.0 h1:s++MEBbD3ZKc9/8/njrn4flZLnCuY9I79v94gBUNumo=
github.com/ipfs/go-ds-leveldb v0.5.0/go.mod h1:d3XG9RUDzQ6V4SHi8+Xgj9j1XuEk1z82lquxrVbml/Q= github.com/ipfs/go-ds-leveldb v0.5.0/go.mod h1:d3XG9RUDzQ6V4SHi8+Xgj9j1XuEk1z82lquxrVbml/Q=
github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
...@@ -409,8 +404,8 @@ github.com/libp2p/go-libp2p v0.25.1 h1:YK+YDCHpYyTvitKWVxa5PfElgIpOONU01X5UcLEwJ ...@@ -409,8 +404,8 @@ github.com/libp2p/go-libp2p v0.25.1 h1:YK+YDCHpYyTvitKWVxa5PfElgIpOONU01X5UcLEwJ
github.com/libp2p/go-libp2p v0.25.1/go.mod h1:xnK9/1d9+jeQCVvi/f1g12KqtVi/jP/SijtKV1hML3g= github.com/libp2p/go-libp2p v0.25.1/go.mod h1:xnK9/1d9+jeQCVvi/f1g12KqtVi/jP/SijtKV1hML3g=
github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw=
github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI=
github.com/libp2p/go-libp2p-pubsub v0.9.0 h1:mcLb4WzwhUG4OKb0rp1/bYMd/DYhvMyzJheQH3LMd1s= github.com/libp2p/go-libp2p-pubsub v0.9.3 h1:ihcz9oIBMaCK9kcx+yHWm3mLAFBMAUsM4ux42aikDxo=
github.com/libp2p/go-libp2p-pubsub v0.9.0/go.mod h1:OEsj0Cc/BpkqikXRTrVspWU/Hx7bMZwHP+6vNMd+c7I= github.com/libp2p/go-libp2p-pubsub v0.9.3/go.mod h1:RYA7aM9jIic5VV47WXu4GkcRxRhrdElWf8xtyli+Dzc=
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg=
github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY= github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY=
...@@ -723,7 +718,6 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i ...@@ -723,7 +718,6 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
......
...@@ -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\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"Hash\",\"name\":\"uuid\",\"type\":\"bytes32\"}],\"name\":\"GameAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"enumGameType\",\"name\":\"gameType\",\"type\":\"uint8\"}],\"name\":\"NoImplementation\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"disputeProxy\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"enumGameType\",\"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\":\"enumGameType\",\"name\":\"gameType\",\"type\":\"uint8\"}],\"name\":\"ImplementationSet\",\"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\":\"enumGameType\",\"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\":\"enumGameType\",\"name\":\"\",\"type\":\"uint8\"}],\"name\":\"gameImpls\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enumGameType\",\"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\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enumGameType\",\"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\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enumGameType\",\"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\"}]", 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\":\"\",\"type\":\"uint256\"}],\"name\":\"disputeGameList\",\"outputs\":[{\"internalType\":\"contractIDisputeGame\",\"name\":\"\",\"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\"}],\"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\":\"pure\",\"type\":\"function\"}]",
Bin: "0x608060405234801561001057600080fd5b50604051610d8b380380610d8b83398101604081905261002f91610171565b61003833610047565b61004181610097565b506101a1565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61009f610115565b6001600160a01b0381166101095760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61011281610047565b50565b6000546001600160a01b0316331461016f5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610100565b565b60006020828403121561018357600080fd5b81516001600160a01b038116811461019a57600080fd5b9392505050565b610bdb806101b06000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80638da5cb5b1161005b5780638da5cb5b14610194578063c49d5271146101b2578063dfa162d3146101c5578063f2fde38b146101fb57600080fd5b806326daafbe1461008d5780633142e55e1461013f57806345583b7a14610177578063715018a61461018c575b600080fd5b61012c61009b36600461093c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0810180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0830180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08086018051988652968352606087529451609f0190941683209190925291905291905290565b6040519081526020015b60405180910390f35b61015261014d366004610a25565b61020e565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610136565b61018a610185366004610ace565b6104ba565b005b61018a61058d565b60005473ffffffffffffffffffffffffffffffffffffffff16610152565b6101526101c0366004610a25565b6105a1565b6101526101d3366004610b05565b60016020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b61018a610209366004610b27565b610618565b6000806001600087600281111561022757610227610b44565b600281111561023857610238610b44565b815260208101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff169050806102a357856040517f44265d6f00000000000000000000000000000000000000000000000000000000815260040161029a9190610b73565b60405180910390fd5b6103068585856040516020016102bb93929190610bb4565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905273ffffffffffffffffffffffffffffffffffffffff8316906106cf565b91508173ffffffffffffffffffffffffffffffffffffffff16638129fc1c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561035057600080fd5b505af1158015610364573d6000803e3d6000fd5b5050505060006103ab878787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061009b92505050565b60008181526002602052604090205490915073ffffffffffffffffffffffffffffffffffffffff161561040d576040517f014f6fe50000000000000000000000000000000000000000000000000000000081526004810182905260240161029a565b600081815260026020819052604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff86161790558690889081111561046f5761046f610b44565b60405173ffffffffffffffffffffffffffffffffffffffff8616907ffad0599ff449d8d9685eadecca8cb9e00924c5fd8367c1c09469824939e1ffec90600090a45050949350505050565b6104c2610803565b80600160008460028111156104d9576104d9610b44565b60028111156104ea576104ea610b44565b815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600281111561054857610548610b44565b60405173ffffffffffffffffffffffffffffffffffffffff8316907f623713f72f6e427a8044bb8b3bd6834357cf285decbaa21bcc73c1d0632c4d8490600090a35050565b610595610803565b61059f6000610884565b565b6000600260006105e8878787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061009b92505050565b815260208101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff1695945050505050565b610620610803565b73ffffffffffffffffffffffffffffffffffffffff81166106c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161029a565b6106cc81610884565b50565b60006002825101603f8101600a81036040518360581b8260e81b177f6100003d81600a3d39f3363d3d373d3d3d3d610000806035363936013d7300001781528660601b601e8201527f5af43d3d93803e603357fd5bf300000000000000000000000000000000000000603282015285519150603f8101602087015b6020841061078757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909301926020918201910161074a565b517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602085900360031b1b16815260f085901b9083015282816000f09450846107f4577febfef1880000000000000000000000000000000000000000000000000000000060005260206000fd5b90910160405250909392505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461059f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161029a565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80356003811061090857600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561095157600080fd5b61095a846108f9565b925060208401359150604084013567ffffffffffffffff8082111561097e57600080fd5b818601915086601f83011261099257600080fd5b8135818111156109a4576109a461090d565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156109ea576109ea61090d565b81604052828152896020848701011115610a0357600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60008060008060608587031215610a3b57600080fd5b610a44856108f9565b935060208501359250604085013567ffffffffffffffff80821115610a6857600080fd5b818701915087601f830112610a7c57600080fd5b813581811115610a8b57600080fd5b886020828501011115610a9d57600080fd5b95989497505060200194505050565b73ffffffffffffffffffffffffffffffffffffffff811681146106cc57600080fd5b60008060408385031215610ae157600080fd5b610aea836108f9565b91506020830135610afa81610aac565b809150509250929050565b600060208284031215610b1757600080fd5b610b20826108f9565b9392505050565b600060208284031215610b3957600080fd5b8135610b2081610aac565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6020810160038310610bae577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91905290565b83815281836020830137600091016020019081529291505056fea164736f6c634300080f000a", Bin: "0x60806040523480156200001157600080fd5b506200001e600062000024565b62000292565b600054610100900460ff1615808015620000455750600054600160ff909116105b8062000075575062000062306200016260201b620008f81760201c565b15801562000075575060005460ff166001145b620000de5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562000102576000805461ff0019166101001790555b6200010c62000171565b6200011782620001d9565b80156200015e576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6001600160a01b03163b151590565b600054610100900460ff16620001cd5760405162461bcd60e51b815260206004820152602b60248201526000805160206200121c83398151915260448201526a6e697469616c697a696e6760a81b6064820152608401620000d5565b620001d76200022b565b565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16620002875760405162461bcd60e51b815260206004820152602b60248201526000805160206200121c83398151915260448201526a6e697469616c697a696e6760a81b6064820152608401620000d5565b620001d733620001d9565b610f7a80620002a26000396000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c8063763014a611610081578063c4d66de81161005b578063c4d66de81461026b578063dfa162d31461027e578063f2fde38b146102b457600080fd5b8063763014a6146102275780638da5cb5b1461023a578063c49d52711461025857600080fd5b80634d1975b4116100b25780634d1975b4146101d857806354fd4d50146101e0578063715018a61461021f57600080fd5b806326daafbe146100d95780633142e55e1461018b57806345583b7a146101c3575b600080fd5b6101786100e7366004610cbf565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0810180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0830180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08086018051988652968352606087529451609f0190941683209190925291905291905290565b6040519081526020015b60405180910390f35b61019e610199366004610da8565b6102c7565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610182565b6101d66101d1366004610e51565b61055c565b005b606754610178565b604080518082018252600581527f302e302e31000000000000000000000000000000000000000000000000000000602082015290516101829190610e88565b6101d66105e3565b61019e610235366004610efb565b6105f7565b60335473ffffffffffffffffffffffffffffffffffffffff1661019e565b61019e610266366004610da8565b61062e565b6101d6610279366004610f14565b6106a5565b61019e61028c366004610f38565b60656020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6101d66102c2366004610f14565b610841565b60ff841660009081526065602052604081205473ffffffffffffffffffffffffffffffffffffffff1680610331576040517f44265d6f00000000000000000000000000000000000000000000000000000000815260ff871660048201526024015b60405180910390fd5b61039485858560405160200161034993929190610f53565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905273ffffffffffffffffffffffffffffffffffffffff831690610914565b91508173ffffffffffffffffffffffffffffffffffffffff16638129fc1c6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156103de57600080fd5b505af11580156103f2573d6000803e3d6000fd5b505050506000610439878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506100e792505050565b60008181526066602052604090205490915073ffffffffffffffffffffffffffffffffffffffff161561049b576040517f014f6fe500000000000000000000000000000000000000000000000000000000815260048101829052602401610328565b600081815260666020526040808220805473ffffffffffffffffffffffffffffffffffffffff87167fffffffffffffffffffffffff00000000000000000000000000000000000000009182168117909255606780546001810182559085527f9787eeb91fe3101235e4a76063c7023ecb40f923f97916639c598592fa30d6ae018054909116821790559051889260ff8b1692917ffad0599ff449d8d9685eadecca8cb9e00924c5fd8367c1c09469824939e1ffec9190a45050949350505050565b610564610a48565b60ff821660008181526065602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8616908117909155905190917f623713f72f6e427a8044bb8b3bd6834357cf285decbaa21bcc73c1d0632c4d8491a35050565b6105eb610a48565b6105f56000610ac9565b565b6067818154811061060757600080fd5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905081565b600060666000610675878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506100e792505050565b815260208101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff1695945050505050565b600054610100900460ff16158080156106c55750600054600160ff909116105b806106df5750303b1580156106df575060005460ff166001145b61076b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610328565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156107c957600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6107d1610b40565b6107da82610ac9565b801561083d57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b610849610a48565b73ffffffffffffffffffffffffffffffffffffffff81166108ec576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610328565b6108f581610ac9565b50565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b60006002825101603f8101600a81036040518360581b8260e81b177f6100003d81600a3d39f3363d3d373d3d3d3d610000806035363936013d7300001781528660601b601e8201527f5af43d3d93803e603357fd5bf300000000000000000000000000000000000000603282015285519150603f8101602087015b602084106109cc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909301926020918201910161098f565b517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602085900360031b1b16815260f085901b9083015282816000f0945084610a39577febfef1880000000000000000000000000000000000000000000000000000000060005260206000fd5b90910160405250909392505050565b60335473ffffffffffffffffffffffffffffffffffffffff1633146105f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610328565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16610bd7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610328565b6105f5600054610100900460ff16610c71576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610328565b6105f533610ac9565b803560ff81168114610c8b57600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080600060608486031215610cd457600080fd5b610cdd84610c7a565b925060208401359150604084013567ffffffffffffffff80821115610d0157600080fd5b818601915086601f830112610d1557600080fd5b813581811115610d2757610d27610c90565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715610d6d57610d6d610c90565b81604052828152896020848701011115610d8657600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60008060008060608587031215610dbe57600080fd5b610dc785610c7a565b935060208501359250604085013567ffffffffffffffff80821115610deb57600080fd5b818701915087601f830112610dff57600080fd5b813581811115610e0e57600080fd5b886020828501011115610e2057600080fd5b95989497505060200194505050565b73ffffffffffffffffffffffffffffffffffffffff811681146108f557600080fd5b60008060408385031215610e6457600080fd5b610e6d83610c7a565b91506020830135610e7d81610e2f565b809150509250929050565b600060208083528351808285015260005b81811015610eb557858101830151858201604001528201610e99565b81811115610ec7576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b600060208284031215610f0d57600080fd5b5035919050565b600060208284031215610f2657600080fd5b8135610f3181610e2f565b9392505050565b600060208284031215610f4a57600080fd5b610f3182610c7a565b83815281836020830137600091016020019081529291505056fea164736f6c634300080f000a496e697469616c697a61626c653a20636f6e7472616374206973206e6f742069",
} }
// DisputeGameFactoryABI is the input ABI used to generate the binding from. // DisputeGameFactoryABI is the input ABI used to generate the binding from.
...@@ -43,7 +43,7 @@ var DisputeGameFactoryABI = DisputeGameFactoryMetaData.ABI ...@@ -43,7 +43,7 @@ var DisputeGameFactoryABI = DisputeGameFactoryMetaData.ABI
var DisputeGameFactoryBin = DisputeGameFactoryMetaData.Bin var DisputeGameFactoryBin = DisputeGameFactoryMetaData.Bin
// DeployDisputeGameFactory deploys a new Ethereum contract, binding an instance of DisputeGameFactory to it. // DeployDisputeGameFactory deploys a new Ethereum contract, binding an instance of DisputeGameFactory to it.
func DeployDisputeGameFactory(auth *bind.TransactOpts, backend bind.ContractBackend, _owner common.Address) (common.Address, *types.Transaction, *DisputeGameFactory, error) { func DeployDisputeGameFactory(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *DisputeGameFactory, error) {
parsed, err := DisputeGameFactoryMetaData.GetAbi() parsed, err := DisputeGameFactoryMetaData.GetAbi()
if err != nil { if err != nil {
return common.Address{}, nil, nil, err return common.Address{}, nil, nil, err
...@@ -52,7 +52,7 @@ func DeployDisputeGameFactory(auth *bind.TransactOpts, backend bind.ContractBack ...@@ -52,7 +52,7 @@ func DeployDisputeGameFactory(auth *bind.TransactOpts, backend bind.ContractBack
return common.Address{}, nil, nil, errors.New("GetABI returned nil") return common.Address{}, nil, nil, errors.New("GetABI returned nil")
} }
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(DisputeGameFactoryBin), backend, _owner) address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(DisputeGameFactoryBin), backend)
if err != nil { if err != nil {
return common.Address{}, nil, nil, err return common.Address{}, nil, nil, err
} }
...@@ -201,6 +201,68 @@ func (_DisputeGameFactory *DisputeGameFactoryTransactorRaw) Transact(opts *bind. ...@@ -201,6 +201,68 @@ func (_DisputeGameFactory *DisputeGameFactoryTransactorRaw) Transact(opts *bind.
return _DisputeGameFactory.Contract.contract.Transact(opts, method, params...) return _DisputeGameFactory.Contract.contract.Transact(opts, method, params...)
} }
// DisputeGameList is a free data retrieval call binding the contract method 0x763014a6.
//
// Solidity: function disputeGameList(uint256 ) view returns(address)
func (_DisputeGameFactory *DisputeGameFactoryCaller) DisputeGameList(opts *bind.CallOpts, arg0 *big.Int) (common.Address, error) {
var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "disputeGameList", arg0)
if err != nil {
return *new(common.Address), err
}
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
return out0, err
}
// DisputeGameList is a free data retrieval call binding the contract method 0x763014a6.
//
// Solidity: function disputeGameList(uint256 ) view returns(address)
func (_DisputeGameFactory *DisputeGameFactorySession) DisputeGameList(arg0 *big.Int) (common.Address, error) {
return _DisputeGameFactory.Contract.DisputeGameList(&_DisputeGameFactory.CallOpts, arg0)
}
// DisputeGameList is a free data retrieval call binding the contract method 0x763014a6.
//
// Solidity: function disputeGameList(uint256 ) view returns(address)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) DisputeGameList(arg0 *big.Int) (common.Address, error) {
return _DisputeGameFactory.Contract.DisputeGameList(&_DisputeGameFactory.CallOpts, arg0)
}
// GameCount is a free data retrieval call binding the contract method 0x4d1975b4.
//
// Solidity: function gameCount() view returns(uint256 _gameCount)
func (_DisputeGameFactory *DisputeGameFactoryCaller) GameCount(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "gameCount")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// GameCount is a free data retrieval call binding the contract method 0x4d1975b4.
//
// Solidity: function gameCount() view returns(uint256 _gameCount)
func (_DisputeGameFactory *DisputeGameFactorySession) GameCount() (*big.Int, error) {
return _DisputeGameFactory.Contract.GameCount(&_DisputeGameFactory.CallOpts)
}
// GameCount is a free data retrieval call binding the contract method 0x4d1975b4.
//
// Solidity: function gameCount() view returns(uint256 _gameCount)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) GameCount() (*big.Int, error) {
return _DisputeGameFactory.Contract.GameCount(&_DisputeGameFactory.CallOpts)
}
// GameImpls is a free data retrieval call binding the contract method 0xdfa162d3. // GameImpls is a free data retrieval call binding the contract method 0xdfa162d3.
// //
// Solidity: function gameImpls(uint8 ) view returns(address) // Solidity: function gameImpls(uint8 ) view returns(address)
...@@ -325,6 +387,37 @@ func (_DisputeGameFactory *DisputeGameFactoryCallerSession) Owner() (common.Addr ...@@ -325,6 +387,37 @@ func (_DisputeGameFactory *DisputeGameFactoryCallerSession) Owner() (common.Addr
return _DisputeGameFactory.Contract.Owner(&_DisputeGameFactory.CallOpts) return _DisputeGameFactory.Contract.Owner(&_DisputeGameFactory.CallOpts)
} }
// Version is a free data retrieval call binding the contract method 0x54fd4d50.
//
// Solidity: function version() pure returns(string)
func (_DisputeGameFactory *DisputeGameFactoryCaller) Version(opts *bind.CallOpts) (string, error) {
var out []interface{}
err := _DisputeGameFactory.contract.Call(opts, &out, "version")
if err != nil {
return *new(string), err
}
out0 := *abi.ConvertType(out[0], new(string)).(*string)
return out0, err
}
// Version is a free data retrieval call binding the contract method 0x54fd4d50.
//
// Solidity: function version() pure returns(string)
func (_DisputeGameFactory *DisputeGameFactorySession) Version() (string, error) {
return _DisputeGameFactory.Contract.Version(&_DisputeGameFactory.CallOpts)
}
// Version is a free data retrieval call binding the contract method 0x54fd4d50.
//
// Solidity: function version() pure returns(string)
func (_DisputeGameFactory *DisputeGameFactoryCallerSession) Version() (string, error) {
return _DisputeGameFactory.Contract.Version(&_DisputeGameFactory.CallOpts)
}
// 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)
...@@ -346,6 +439,27 @@ func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) Create(gameType ...@@ -346,6 +439,27 @@ func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) Create(gameType
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.
//
// Solidity: function initialize(address _owner) returns()
func (_DisputeGameFactory *DisputeGameFactoryTransactor) Initialize(opts *bind.TransactOpts, _owner common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.contract.Transact(opts, "initialize", _owner)
}
// Initialize is a paid mutator transaction binding the contract method 0xc4d66de8.
//
// Solidity: function initialize(address _owner) returns()
func (_DisputeGameFactory *DisputeGameFactorySession) Initialize(_owner common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.Initialize(&_DisputeGameFactory.TransactOpts, _owner)
}
// Initialize is a paid mutator transaction binding the contract method 0xc4d66de8.
//
// Solidity: function initialize(address _owner) returns()
func (_DisputeGameFactory *DisputeGameFactoryTransactorSession) Initialize(_owner common.Address) (*types.Transaction, error) {
return _DisputeGameFactory.Contract.Initialize(&_DisputeGameFactory.TransactOpts, _owner)
}
// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. // RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6.
// //
// Solidity: function renounceOwnership() returns() // Solidity: function renounceOwnership() returns()
...@@ -724,6 +838,140 @@ func (_DisputeGameFactory *DisputeGameFactoryFilterer) ParseImplementationSet(lo ...@@ -724,6 +838,140 @@ func (_DisputeGameFactory *DisputeGameFactoryFilterer) ParseImplementationSet(lo
return event, nil return event, nil
} }
// DisputeGameFactoryInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the DisputeGameFactory contract.
type DisputeGameFactoryInitializedIterator struct {
Event *DisputeGameFactoryInitialized // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *DisputeGameFactoryInitializedIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(DisputeGameFactoryInitialized)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(DisputeGameFactoryInitialized)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *DisputeGameFactoryInitializedIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *DisputeGameFactoryInitializedIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// DisputeGameFactoryInitialized represents a Initialized event raised by the DisputeGameFactory contract.
type DisputeGameFactoryInitialized struct {
Version uint8
Raw types.Log // Blockchain specific contextual infos
}
// FilterInitialized is a free log retrieval operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498.
//
// Solidity: event Initialized(uint8 version)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) FilterInitialized(opts *bind.FilterOpts) (*DisputeGameFactoryInitializedIterator, error) {
logs, sub, err := _DisputeGameFactory.contract.FilterLogs(opts, "Initialized")
if err != nil {
return nil, err
}
return &DisputeGameFactoryInitializedIterator{contract: _DisputeGameFactory.contract, event: "Initialized", logs: logs, sub: sub}, nil
}
// WatchInitialized is a free log subscription operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498.
//
// Solidity: event Initialized(uint8 version)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *DisputeGameFactoryInitialized) (event.Subscription, error) {
logs, sub, err := _DisputeGameFactory.contract.WatchLogs(opts, "Initialized")
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(DisputeGameFactoryInitialized)
if err := _DisputeGameFactory.contract.UnpackLog(event, "Initialized", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseInitialized is a log parse operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498.
//
// Solidity: event Initialized(uint8 version)
func (_DisputeGameFactory *DisputeGameFactoryFilterer) ParseInitialized(log types.Log) (*DisputeGameFactoryInitialized, error) {
event := new(DisputeGameFactoryInitialized)
if err := _DisputeGameFactory.contract.UnpackLog(event, "Initialized", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// DisputeGameFactoryOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the DisputeGameFactory contract. // DisputeGameFactoryOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the DisputeGameFactory contract.
type DisputeGameFactoryOwnershipTransferredIterator struct { type DisputeGameFactoryOwnershipTransferredIterator struct {
Event *DisputeGameFactoryOwnershipTransferred // Event containing the contract specifics and raw log Event *DisputeGameFactoryOwnershipTransferred // Event containing the contract specifics and raw log
......
...@@ -49,7 +49,7 @@ type TypesWithdrawalTransaction struct { ...@@ -49,7 +49,7 @@ type TypesWithdrawalTransaction struct {
// OptimismPortalMetaData contains all meta data concerning the OptimismPortal contract. // OptimismPortalMetaData contains all meta data concerning the OptimismPortal contract.
var OptimismPortalMetaData = &bind.MetaData{ var OptimismPortalMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[{\"internalType\":\"contractL2OutputOracle\",\"name\":\"_l2Oracle\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_guardian\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"_paused\",\"type\":\"bool\"},{\"internalType\":\"contractSystemConfig\",\"name\":\"_config\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"version\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"opaqueData\",\"type\":\"bytes\"}],\"name\":\"TransactionDeposited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"withdrawalHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"WithdrawalFinalized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"withdrawalHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"WithdrawalProven\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"GUARDIAN\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"L2_ORACLE\",\"outputs\":[{\"internalType\":\"contractL2OutputOracle\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SYSTEM_CONFIG\",\"outputs\":[{\"internalType\":\"contractSystemConfig\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"_gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"_isCreation\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"depositTransaction\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"donateETH\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.WithdrawalTransaction\",\"name\":\"_tx\",\"type\":\"tuple\"}],\"name\":\"finalizeWithdrawalTransaction\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"finalizedWithdrawals\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"_paused\",\"type\":\"bool\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_l2OutputIndex\",\"type\":\"uint256\"}],\"name\":\"isOutputFinalized\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"l2Sender\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_byteCount\",\"type\":\"uint64\"}],\"name\":\"minimumGasLimit\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"params\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"prevBaseFee\",\"type\":\"uint128\"},{\"internalType\":\"uint64\",\"name\":\"prevBoughtGas\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"prevBlockNum\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.WithdrawalTransaction\",\"name\":\"_tx\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"_l2OutputIndex\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"messagePasserStorageRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"latestBlockhash\",\"type\":\"bytes32\"}],\"internalType\":\"structTypes.OutputRootProof\",\"name\":\"_outputRootProof\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"_withdrawalProof\",\"type\":\"bytes[]\"}],\"name\":\"proveWithdrawalTransaction\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"provenWithdrawals\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"outputRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint128\",\"name\":\"timestamp\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"l2OutputIndex\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", ABI: "[{\"inputs\":[{\"internalType\":\"contractL2OutputOracle\",\"name\":\"_l2Oracle\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_guardian\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"_paused\",\"type\":\"bool\"},{\"internalType\":\"contractSystemConfig\",\"name\":\"_config\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"version\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"opaqueData\",\"type\":\"bytes\"}],\"name\":\"TransactionDeposited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"withdrawalHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"WithdrawalFinalized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"withdrawalHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"WithdrawalProven\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"GUARDIAN\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"L2_ORACLE\",\"outputs\":[{\"internalType\":\"contractL2OutputOracle\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SYSTEM_CONFIG\",\"outputs\":[{\"internalType\":\"contractSystemConfig\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"_gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"_isCreation\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"depositTransaction\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"donateETH\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.WithdrawalTransaction\",\"name\":\"_tx\",\"type\":\"tuple\"}],\"name\":\"finalizeWithdrawalTransaction\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"finalizedWithdrawals\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"_paused\",\"type\":\"bool\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_l2OutputIndex\",\"type\":\"uint256\"}],\"name\":\"isOutputFinalized\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"l2Sender\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_byteCount\",\"type\":\"uint64\"}],\"name\":\"minimumGasLimit\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"params\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"prevBaseFee\",\"type\":\"uint128\"},{\"internalType\":\"uint64\",\"name\":\"prevBoughtGas\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"prevBlockNum\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.WithdrawalTransaction\",\"name\":\"_tx\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"_l2OutputIndex\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"messagePasserStorageRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"latestBlockhash\",\"type\":\"bytes32\"}],\"internalType\":\"structTypes.OutputRootProof\",\"name\":\"_outputRootProof\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"_withdrawalProof\",\"type\":\"bytes[]\"}],\"name\":\"proveWithdrawalTransaction\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"provenWithdrawals\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"outputRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint128\",\"name\":\"timestamp\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"l2OutputIndex\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]",
Bin: "0x6101406040523480156200001257600080fd5b5060405162005b0038038062005b00833981016040819052620000359162000296565b6001608052600660a052600060c0526001600160a01b0380851660e052838116610120528116610100526200006a8262000074565b5050505062000302565b600054610100900460ff1615808015620000955750600054600160ff909116105b80620000c55750620000b230620001cb60201b62001c891760201c565b158015620000c5575060005460ff166001145b6200012e5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562000152576000805461ff0019166101001790555b603280546001600160a01b03191661dead1790556035805483151560ff1990911617905562000180620001da565b8015620001c7576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6001600160a01b03163b151590565b600054610100900460ff16620002475760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840162000125565b60408051606081018252633b9aca0080825260006020830152436001600160401b031691909201819052600160c01b0217600155565b6001600160a01b03811681146200029357600080fd5b50565b60008060008060808587031215620002ad57600080fd5b8451620002ba816200027d565b6020860151909450620002cd816200027d565b60408601519093508015158114620002e457600080fd5b6060860151909250620002f7816200027d565b939692955090935050565b60805160a05160c05160e051610100516101205161576f62000391600039600081816102690152818161079d01526110a00152600081816104c801526123d701526000818161016a01528181610a0601528181610be701528181610ffc015281816113b60152818161162801526121c301526000610f6701526000610f3e01526000610f15015261576f6000f3fe60806040526004361061012c5760003560e01c80638c3152e9116100a5578063cff0ab9611610074578063e965084c11610059578063e965084c14610417578063e9e05c42146104a3578063f0498750146104b657600080fd5b8063cff0ab9614610356578063d53a822f146103f757600080fd5b80638c3152e9146102a05780639bf62d82146102c0578063a14238e7146102ed578063a35d99df1461031d57600080fd5b80635c975abb116100fc578063724c184c116100e1578063724c184c146102575780638456cb591461028b5780638b4c40b01461015157600080fd5b80635c975abb1461020d5780636dbffb781461023757600080fd5b80621c2ff6146101585780633f4ba83a146101b65780634870496f146101cb57806354fd4d50146101eb57600080fd5b36610153576101513334620186a06000604051806020016040528060008152506104ea565b005b600080fd5b34801561016457600080fd5b5061018c7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101c257600080fd5b50610151610785565b3480156101d757600080fd5b506101516101e6366004614d21565b6108a8565b3480156101f757600080fd5b50610200610f0e565b6040516101ad9190614e77565b34801561021957600080fd5b506035546102279060ff1681565b60405190151581526020016101ad565b34801561024357600080fd5b50610227610252366004614e8a565b610fb1565b34801561026357600080fd5b5061018c7f000000000000000000000000000000000000000000000000000000000000000081565b34801561029757600080fd5b50610151611088565b3480156102ac57600080fd5b506101516102bb366004614ea3565b6111a8565b3480156102cc57600080fd5b5060325461018c9073ffffffffffffffffffffffffffffffffffffffff1681565b3480156102f957600080fd5b50610227610308366004614e8a565b60336020526000908152604090205460ff1681565b34801561032957600080fd5b5061033d610338366004614ef0565b611a83565b60405167ffffffffffffffff90911681526020016101ad565b34801561036257600080fd5b506001546103be906fffffffffffffffffffffffffffffffff81169067ffffffffffffffff7001000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041683565b604080516fffffffffffffffffffffffffffffffff909416845267ffffffffffffffff92831660208501529116908201526060016101ad565b34801561040357600080fd5b50610151610412366004614f1b565b611a9c565b34801561042357600080fd5b50610475610432366004614e8a565b603460205260009081526040902080546001909101546fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041683565b604080519384526fffffffffffffffffffffffffffffffff92831660208501529116908201526060016101ad565b6101516104b1366004614f36565b6104ea565b3480156104c257600080fd5b5061018c7f000000000000000000000000000000000000000000000000000000000000000081565b8260005a905083156105a15773ffffffffffffffffffffffffffffffffffffffff8716156105a157604080517f08c379a00000000000000000000000000000000000000000000000000000000081526020600482015260248101919091527f4f7074696d69736d506f7274616c3a206d7573742073656e6420746f2061646460448201527f72657373283029207768656e206372656174696e67206120636f6e747261637460648201526084015b60405180910390fd5b6105ab8351611a83565b67ffffffffffffffff168567ffffffffffffffff16101561064e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f4f7074696d69736d506f7274616c3a20676173206c696d697420746f6f20736d60448201527f616c6c00000000000000000000000000000000000000000000000000000000006064820152608401610598565b6201d4c0835111156106bc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4f7074696d69736d506f7274616c3a206461746120746f6f206c6172676500006044820152606401610598565b333281146106dd575033731111000000000000000000000000000000001111015b600034888888886040516020016106f8959493929190614faf565b604051602081830303815290604052905060008973ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32846040516107689190614e77565b60405180910390a4505061077c8282611ca5565b50505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461084a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e20756e706175736500000000000000000000000000000000000000000000006064820152608401610598565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b60355460ff1615610915576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610598565b3073ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff16036109d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a20796f752063616e6e6f742073656e642060448201527f6d6573736167657320746f2074686520706f7274616c20636f6e7472616374006064820152608401610598565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018590526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610a62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a869190615034565b519050610aa0610a9b36869003860186615099565b611fd2565b8114610b2e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a20696e76616c6964206f7574707574207260448201527f6f6f742070726f6f6600000000000000000000000000000000000000000000006064820152608401610598565b6000610b398761202e565b6000818152603460209081526040918290208251606081018452815481526001909101546fffffffffffffffffffffffffffffffff8082169383018490527001000000000000000000000000000000009091041692810192909252919250901580610c6b5750805160408083015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610c43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c679190615034565b5114155b610cf7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173682060448201527f68617320616c7265616479206265656e2070726f76656e0000000000000000006064820152608401610598565b60408051602081018490526000918101829052606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083018190529250610dc09101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f0100000000000000000000000000000000000000000000000000000000000000602083015290610db6888a6150ff565b8a6040013561205e565b610e4c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a20696e76616c696420776974686472617760448201527f616c20696e636c7573696f6e2070726f6f6600000000000000000000000000006064820152608401610598565b604080516060810182528581526fffffffffffffffffffffffffffffffff42811660208084019182528c831684860190815260008981526034835286812095518655925190518416700100000000000000000000000000000000029316929092176001909301929092558b830151908c0151925173ffffffffffffffffffffffffffffffffffffffff918216939091169186917f67a6208cfcc0801d50f6cbe764733f4fddf66ac0b04442061a8a8c0cb6b63f629190a4505050505050505050565b6060610f397f0000000000000000000000000000000000000000000000000000000000000000612082565b610f627f0000000000000000000000000000000000000000000000000000000000000000612082565b610f8b7f0000000000000000000000000000000000000000000000000000000000000000612082565b604051602001610f9d93929190615183565b604051602081830303815290604052905090565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018290526000906110829073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063a25ae55790602401606060405180830381865afa158015611043573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110679190615034565b602001516fffffffffffffffffffffffffffffffff166121bf565b92915050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461114d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e207061757365000000000000000000000000000000000000000000000000006064820152608401610598565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2589060200161089e565b60355460ff1615611215576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610598565b60325473ffffffffffffffffffffffffffffffffffffffff1661dead146112be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a2063616e206f6e6c79207472696767657260448201527f206f6e65207769746864726177616c20706572207472616e73616374696f6e006064820152608401610598565b60006112c98261202e565b60008181526034602090815260408083208151606081018352815481526001909101546fffffffffffffffffffffffffffffffff808216948301859052700100000000000000000000000000000000909104169181019190915292935090036113b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206e60448201527f6f74206265656e2070726f76656e2079657400000000000000000000000000006064820152608401610598565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663887862726040518163ffffffff1660e01b8152600401602060405180830381865afa15801561141f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144391906151f9565b81602001516fffffffffffffffffffffffffffffffff16101561150e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604b60248201527f4f7074696d69736d506f7274616c3a207769746864726177616c2074696d657360448201527f74616d70206c657373207468616e204c32204f7261636c65207374617274696e60648201527f672074696d657374616d70000000000000000000000000000000000000000000608482015260a401610598565b61152d81602001516fffffffffffffffffffffffffffffffff166121bf565b6115df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604560248201527f4f7074696d69736d506f7274616c3a2070726f76656e2077697468647261776160448201527f6c2066696e616c697a6174696f6e20706572696f6420686173206e6f7420656c60648201527f6170736564000000000000000000000000000000000000000000000000000000608482015260a401610598565b60408181015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015611684573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116a89190615034565b8251815191925014611762576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604960248201527f4f7074696d69736d506f7274616c3a206f757470757420726f6f742070726f7660448201527f656e206973206e6f74207468652073616d652061732063757272656e74206f7560648201527f7470757420726f6f740000000000000000000000000000000000000000000000608482015260a401610598565b61178181602001516fffffffffffffffffffffffffffffffff166121bf565b611833576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604360248201527f4f7074696d69736d506f7274616c3a206f75747075742070726f706f73616c2060448201527f66696e616c697a6174696f6e20706572696f6420686173206e6f7420656c617060648201527f7365640000000000000000000000000000000000000000000000000000000000608482015260a401610598565b60008381526033602052604090205460ff16156118d2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206160448201527f6c7265616479206265656e2066696e616c697a656400000000000000000000006064820152608401610598565b600083815260336020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055908601516032805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff00000000000000000000000000000000000000009092169190911790558501516080860151606087015160a088015161197493929190612262565b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405190915084907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b906119d990841515815260200190565b60405180910390a2801580156119ef5750326001145b15611a7c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4f7074696d69736d506f7274616c3a207769746864726177616c206661696c6560448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610598565b5050505050565b6000611a90826010615241565b61108290615208615271565b600054610100900460ff1615808015611abc5750600054600160ff909116105b80611ad65750303b158015611ad6575060005460ff166001145b611b62576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610598565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611bc057600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead179055603580548315157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116179055611c226122c0565b8015611c8557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b600154600090611cdb907801000000000000000000000000000000000000000000000000900467ffffffffffffffff164361529d565b90506000611ce76123a3565b90506000816020015160ff16826000015163ffffffff16611d0891906152e3565b90508215611e3f57600154600090611d3f908390700100000000000000000000000000000000900467ffffffffffffffff1661534b565b90506000836040015160ff1683611d5691906153bf565b600154611d769084906fffffffffffffffffffffffffffffffff166153bf565b611d8091906152e3565b600154909150600090611dd190611daa9084906fffffffffffffffffffffffffffffffff1661547b565b866060015163ffffffff168760a001516fffffffffffffffffffffffffffffffff16612469565b90506001861115611e0057611dfd611daa82876040015160ff1660018a611df8919061529d565b612488565b90505b6fffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000067ffffffffffffffff4316021760015550505b60018054869190601090611e72908490700100000000000000000000000000000000900467ffffffffffffffff16615271565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550816000015163ffffffff16600160000160109054906101000a900467ffffffffffffffff1667ffffffffffffffff161315611f55576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603e60248201527f5265736f757263654d65746572696e673a2063616e6e6f7420627579206d6f7260448201527f6520676173207468616e20617661696c61626c6520676173206c696d697400006064820152608401610598565b600154600090611f81906fffffffffffffffffffffffffffffffff1667ffffffffffffffff88166154ef565b90506000611f9348633b9aca006124dd565b611f9d908361552c565b905060005a611fac908861529d565b905080821115611fc857611fc8611fc3828461529d565b6124f4565b5050505050505050565b60008160000151826020015183604001518460600151604051602001612011949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b80516020808301516040808501516060860151608087015160a08801519351600097612011979096959101615540565b60008061206a86612522565b905061207881868686612554565b9695505050505050565b6060816000036120c557505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156120ef57806120d981615597565b91506120e89050600a8361552c565b91506120c9565b60008167ffffffffffffffff81111561210a5761210a614b47565b6040519080825280601f01601f191660200182016040528015612134576020820181803683370190505b5090505b84156121b75761214960018361529d565b9150612156600a866155cf565b6121619060306155e3565b60f81b818381518110612176576121766155fb565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506121b0600a8661552c565b9450612138565b949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663f4daa2916040518163ffffffff1660e01b8152600401602060405180830381865afa15801561222c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225091906151f9565b61225a90836155e3565b421192915050565b6000806000612272866000612584565b9050806122a8576308c379a06000526020805278185361666543616c6c3a204e6f7420656e6f756768206761736058526064601cfd5b600080855160208701888b5af1979650505050505050565b600054610100900460ff16612357576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610598565b60408051606081018252633b9aca00808252600060208301524367ffffffffffffffff169190920181905278010000000000000000000000000000000000000000000000000217600155565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663cc731b026040518163ffffffff1660e01b815260040160c060405180830381865afa158015612440573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612464919061564f565b905090565b600061247e61247885856125a2565b836125b2565b90505b9392505050565b6000670de0b6b3a76400006124c96124a085836152e3565b6124b290670de0b6b3a764000061534b565b6124c485670de0b6b3a76400006153bf565b6125c1565b6124d390866153bf565b61247e91906152e3565b6000818310156124ed5781612481565b5090919050565b6000805a90505b825a612507908361529d565b101561251d5761251682615597565b91506124fb565b505050565b6060818051906020012060405160200161253e91815260200190565b6040516020818303038152906040529050919050565b600061257b846125658786866125f2565b8051602091820120825192909101919091201490565b95945050505050565b600080603f83619c4001026040850201603f5a021015949350505050565b6000818312156124ed5781612481565b60008183126124ed5781612481565b6000612481670de0b6b3a7640000836125d98661307a565b6125e391906153bf565b6125ed91906152e3565b6132be565b6060600084511161265f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d65726b6c65547269653a20656d707479206b657900000000000000000000006044820152606401610598565b600061266a846134fd565b90506000612677866135ec565b905060008460405160200161268e91815260200190565b60405160208183030381529060405290506000805b8451811015612ff15760008582815181106126c0576126c06155fb565b60200260200101519050845183111561275b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201527f74616c206b6579206c656e6774680000000000000000000000000000000000006064820152608401610598565b8260000361281457805180516020918201206040516127a99261278392910190815260200190565b604051602081830303815290604052858051602091820120825192909101919091201490565b61280f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f7420686173680000006044820152606401610598565b61296b565b8051516020116128ca578051805160209182012060405161283e9261278392910190815260200190565b61280f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e60448201527f616c2068617368000000000000000000000000000000000000000000000000006064820152608401610598565b80518451602080870191909120825191909201201461296b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f6460448201527f65206861736800000000000000000000000000000000000000000000000000006064820152608401610598565b612977601060016155e3565b81602001515103612b585784518303612af05760006129b382602001516010815181106129a6576129a66155fb565b6020026020010151613787565b90506000815111612a46576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e63682900000000006064820152608401610598565b60018751612a54919061529d565b8314612ae2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e6368290000000000006064820152608401610598565b965061248195505050505050565b6000858481518110612b0457612b046155fb565b602001015160f81c60f81b60f81c9050600082602001518260ff1681518110612b2f57612b2f6155fb565b60200260200101519050612b42816138e7565b9550612b4f6001866155e3565b94505050612fde565b600281602001515103612f56576000612b708261390c565b9050600081600081518110612b8757612b876155fb565b016020015160f81c90506000612b9e6002836156ee565b612ba9906002615710565b90506000612bba848360ff16613930565b90506000612bc88a89613930565b90506000612bd68383613966565b905080835114612c68576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b65790000000000006064820152608401610598565b60ff851660021480612c7d575060ff85166003145b15612e715780825114612d12576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e6465720000006064820152608401610598565b6000612d2e88602001516001815181106129a6576129a66155fb565b90506000815111612dc1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c65616629000000000000006064820152608401610598565b60018d51612dcf919061529d565b8914612e5d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c6561662900000000000000006064820152608401610598565b9c506124819b505050505050505050505050565b60ff85161580612e84575060ff85166001145b15612ec357612eb08760200151600181518110612ea357612ea36155fb565b60200260200101516138e7565b9950612ebc818a6155e3565b9850612f4b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f6465207769746860448201527f20616e20756e6b6e6f776e2070726566697800000000000000000000000000006064820152608401610598565b505050505050612fde565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e70617273656160448201527f626c65206e6f64650000000000000000000000000000000000000000000000006064820152608401610598565b5080612fe981615597565b9150506126a3565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c6560448201527f6d656e74730000000000000000000000000000000000000000000000000000006064820152608401610598565b60008082136130e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610598565b600060606130f284613a15565b03609f8181039490941b90931c6c465772b2bbbb5f824b15207a3081018102606090811d6d0388eaa27412d5aca026815d636e018202811d6d0df99ac502031bf953eff472fdcc018202811d6d13cdffb29d51d99322bdff5f2211018202811d6d0a0f742023def783a307a986912e018202811d6d01920d8043ca89b5239253284e42018202811d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7883018302821d6d0139601a2efabe717e604cbb4894018302821d6d02247f7a7b6594320649aa03aba1018302821d7fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9018302821d7ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f01830290911d7fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832019091027ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000000105711340daa0d5f769dba1915cef59f0815a5506027d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b393909302929092017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d92915050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdb731c958f34d94c182136132ef57506000919050565b680755bf798b4a1bf1e58212613361576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4558505f4f564552464c4f5700000000000000000000000000000000000000006044820152606401610598565b6503782dace9d9604e83901b059150600060606bb17217f7d1cf79abc9e3b39884821b056b80000000000000000000000001901d6bb17217f7d1cf79abc9e3b39881029093037fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a550481018102606090811d6d0277594991cfc85f6e2461837cd9018202811d7fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c018202811d6db1bbb201f443cf962f1a1d3db4a5018202811d7ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb018202811d6e05180bb14799ab47a8a8cb2a527d57016d02d16720577bd19bf614176fe9ea6c10fe68e7fd37d0007b713f765084018402831d9081019084017ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e010290911d6e0587f503bb6ea29d25fcb7401964500190910279d835ebba824c98fb31b83b2ca45c000000000000000000000000010574029d9dc38563c32e5c2f6dc192ee70ef65f9978af30260c3939093039290921c92915050565b805160609060008167ffffffffffffffff81111561351d5761351d614b47565b60405190808252806020026020018201604052801561356257816020015b604080518082019091526060808252602082015281526020019060019003908161353b5790505b50905060005b828110156135e457604051806040016040528086838151811061358d5761358d6155fb565b602002602001015181526020016135bc8784815181106135af576135af6155fb565b6020026020010151613aeb565b8152508282815181106135d1576135d16155fb565b6020908102919091010152600101613568565b509392505050565b805160609060006135fe8260026154ef565b67ffffffffffffffff81111561361657613616614b47565b6040519080825280601f01601f191660200182016040528015613640576020820181803683370190505b5090506000805b8381101561377d57858181518110613661576136616155fb565b6020910101517fff000000000000000000000000000000000000000000000000000000000000008116925060041c7f0ff000000000000000000000000000000000000000000000000000000000000016836136bd8360026154ef565b815181106136cd576136cd6155fb565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f0f0000000000000000000000000000000000000000000000000000000000000082168361372b8360026154ef565b6137369060016155e3565b81518110613746576137466155fb565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101613647565b5090949350505050565b6060600080600061379785613afe565b9194509250905060008160018111156137b2576137b2615733565b1461383f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206279746573206973206e6f7420612064617461206974656d000000000000006064820152608401610598565b61384982846155e3565b8551146138d8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f524c505265616465723a2062797465732076616c756520636f6e7461696e732060448201527f616e20696e76616c69642072656d61696e6465720000000000000000000000006064820152608401610598565b61257b8560200151848461456b565b60606020826000015110613903576138fe82613787565b611082565b6110828261460c565b606061108261392b83602001516000815181106129a6576129a66155fb565b6135ec565b60608251821061394f5750604080516020810190915260008152611082565b6124818383848651613961919061529d565b614622565b6000806000835185511061397b57835161397e565b84515b90505b8082108015613a05575083828151811061399d5761399d6155fb565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168583815181106139dc576139dc6155fb565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b156135e457816001019150613981565b6000808211613a80576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610598565b5060016fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110821b1791821c111790565b6060611082613af9836147fa565b6148e3565b600080600080846000015111613bbc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610598565b6020840151805160001a607f8111613be1576000600160009450945094505050614564565b60b78111613def576000613bf660808361529d565b905080876000015111613cb1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604e60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20737472696e67206c656e6774682060648201527f2873686f727420737472696e6729000000000000000000000000000000000000608482015260a401610598565b6001838101517fff00000000000000000000000000000000000000000000000000000000000000169082141580613d2a57507f80000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821610155b613ddc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a20696e76616c6964207072656669782c2073696e676c60448201527f652062797465203c203078383020617265206e6f74207072656669786564202860648201527f73686f727420737472696e672900000000000000000000000000000000000000608482015260a401610598565b5060019550935060009250614564915050565b60bf811161413d576000613e0460b78361529d565b905080876000015111613ebf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f6620737472696e67206c656e60648201527f67746820286c6f6e6720737472696e6729000000000000000000000000000000608482015260a401610598565b60018301517fff00000000000000000000000000000000000000000000000000000000000000166000819003613f9d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e6720737472696e672900000000000000000000000000000000000000000000608482015260a401610598565b600184015160088302610100031c60378111614061576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f20737472696e6729000000000000000000000000000000000000000000000000608482015260a401610598565b61406b81846155e3565b895111614120576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e6720737472696e67290000000000000000000000000000000000000000608482015260a401610598565b61412b8360016155e3565b97509550600094506145649350505050565b60f7811161421e57600061415260c08361529d565b90508087600001511161420d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e206c697374206c656e67746820287360648201527f686f7274206c6973742900000000000000000000000000000000000000000000608482015260a401610598565b600195509350849250614564915050565b600061422b60f78361529d565b9050808760000151116142e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f66206c697374206c656e677460648201527f6820286c6f6e67206c6973742900000000000000000000000000000000000000608482015260a401610598565b60018301517fff000000000000000000000000000000000000000000000000000000000000001660008190036143c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e67206c69737429000000000000000000000000000000000000000000000000608482015260a401610598565b600184015160088302610100031c60378111614488576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604660248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f206c697374290000000000000000000000000000000000000000000000000000608482015260a401610598565b61449281846155e3565b895111614547576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e67206c6973742900000000000000000000000000000000000000000000608482015260a401610598565b6145528360016155e3565b97509550600194506145649350505050565b9193909250565b606060008267ffffffffffffffff81111561458857614588614b47565b6040519080825280601f01601f1916602001820160405280156145b2576020820181803683370190505b509050826000036145c4579050612481565b60006145d085876155e3565b90506020820160005b858110156145f15782810151828201526020016145d9565b85811115614600576000868301525b50919695505050505050565b606061108282602001516000846000015161456b565b60608182601f011015614691576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610598565b8282840110156146fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610598565b8183018451101561476a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401610598565b60608215801561478957604051915060008252602082016040526147f1565b6040519150601f8416801560200281840101858101878315602002848b0101015b818310156147c25780518352602092830192016147aa565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b604080518082019091526000808252602082015260008251116148c5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610598565b50604080518082019091528151815260209182019181019190915290565b606060008060006148f385613afe565b91945092509050600181600181111561490e5761490e615733565b1461499b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206c697374206973206e6f742061206c697374206974656d00000000000000006064820152608401610598565b84516149a783856155e3565b14614a34576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f524c505265616465723a206c697374206974656d2068617320616e20696e766160448201527f6c696420646174612072656d61696e64657200000000000000000000000000006064820152608401610598565b6040805160208082526104208201909252600091816020015b6040805180820190915260008082526020820152815260200190600190039081614a4d5790505090506000845b8751811015614b3b57600080614ac06040518060400160405280858d60000151614aa4919061529d565b8152602001858d60200151614ab991906155e3565b9052613afe565b509150915060405180604001604052808383614adc91906155e3565b8152602001848c60200151614af191906155e3565b815250858581518110614b0657614b066155fb565b6020908102919091010152614b1c6001856155e3565b9350614b2881836155e3565b614b3290846155e3565b92505050614a7a565b50815295945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614bbd57614bbd614b47565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114614be957600080fd5b919050565b600082601f830112614bff57600080fd5b813567ffffffffffffffff811115614c1957614c19614b47565b614c4a60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614b76565b818152846020838601011115614c5f57600080fd5b816020850160208301376000918101602001919091529392505050565b600060c08284031215614c8e57600080fd5b60405160c0810167ffffffffffffffff8282108183111715614cb257614cb2614b47565b8160405282935084358352614cc960208601614bc5565b6020840152614cda60408601614bc5565b6040840152606085013560608401526080850135608084015260a0850135915080821115614d0757600080fd5b50614d1485828601614bee565b60a0830152505092915050565b600080600080600085870360e0811215614d3a57600080fd5b863567ffffffffffffffff80821115614d5257600080fd5b614d5e8a838b01614c7c565b97506020890135965060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc084011215614d9757600080fd5b60408901955060c0890135925080831115614db157600080fd5b828901925089601f840112614dc557600080fd5b8235915080821115614dd657600080fd5b508860208260051b8401011115614dec57600080fd5b959894975092955050506020019190565b60005b83811015614e18578181015183820152602001614e00565b83811115614e27576000848401525b50505050565b60008151808452614e45816020860160208601614dfd565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006124816020830184614e2d565b600060208284031215614e9c57600080fd5b5035919050565b600060208284031215614eb557600080fd5b813567ffffffffffffffff811115614ecc57600080fd5b6121b784828501614c7c565b803567ffffffffffffffff81168114614be957600080fd5b600060208284031215614f0257600080fd5b61248182614ed8565b80358015158114614be957600080fd5b600060208284031215614f2d57600080fd5b61248182614f0b565b600080600080600060a08688031215614f4e57600080fd5b614f5786614bc5565b945060208601359350614f6c60408701614ed8565b9250614f7a60608701614f0b565b9150608086013567ffffffffffffffff811115614f9657600080fd5b614fa288828901614bee565b9150509295509295909350565b8581528460208201527fffffffffffffffff0000000000000000000000000000000000000000000000008460c01b16604082015282151560f81b604882015260008251615003816049850160208701614dfd565b919091016049019695505050505050565b80516fffffffffffffffffffffffffffffffff81168114614be957600080fd5b60006060828403121561504657600080fd5b6040516060810181811067ffffffffffffffff8211171561506957615069614b47565b6040528251815261507c60208401615014565b602082015261508d60408401615014565b60408201529392505050565b6000608082840312156150ab57600080fd5b6040516080810181811067ffffffffffffffff821117156150ce576150ce614b47565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b600067ffffffffffffffff8084111561511a5761511a614b47565b8360051b602061512b818301614b76565b86815291850191818101903684111561514357600080fd5b865b848110156151775780358681111561515d5760008081fd5b61516936828b01614bee565b845250918301918301615145565b50979650505050505050565b60008451615195818460208901614dfd565b80830190507f2e0000000000000000000000000000000000000000000000000000000000000080825285516151d1816001850160208a01614dfd565b600192019182015283516151ec816002840160208801614dfd565b0160020195945050505050565b60006020828403121561520b57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600067ffffffffffffffff8083168185168183048111821515161561526857615268615212565b02949350505050565b600067ffffffffffffffff80831681851680830382111561529457615294615212565b01949350505050565b6000828210156152af576152af615212565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826152f2576152f26152b4565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561534657615346615212565b500590565b6000808312837f80000000000000000000000000000000000000000000000000000000000000000183128115161561538557615385615212565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0183138116156153b9576153b9615212565b50500390565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60008413600084138583048511828216161561540057615400615212565b7f8000000000000000000000000000000000000000000000000000000000000000600087128682058812818416161561543b5761543b615212565b6000871292508782058712848416161561545757615457615212565b8785058712818416161561546d5761546d615212565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038413811516156154b5576154b5615212565b827f80000000000000000000000000000000000000000000000000000000000000000384128116156154e9576154e9615212565b50500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561552757615527615212565b500290565b60008261553b5761553b6152b4565b500490565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a083015261558b60c0830184614e2d565b98975050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036155c8576155c8615212565b5060010190565b6000826155de576155de6152b4565b500690565b600082198211156155f6576155f6615212565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b805163ffffffff81168114614be957600080fd5b805160ff81168114614be957600080fd5b600060c0828403121561566157600080fd5b60405160c0810181811067ffffffffffffffff8211171561568457615684614b47565b6040526156908361562a565b815261569e6020840161563e565b60208201526156af6040840161563e565b60408201526156c06060840161562a565b60608201526156d16080840161562a565b60808201526156e260a08401615014565b60a08201529392505050565b600060ff831680615701576157016152b4565b8060ff84160691505092915050565b600060ff821660ff84168082101561572a5761572a615212565b90039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea164736f6c634300080f000a", Bin: "0x6101406040523480156200001257600080fd5b50604051620059c8380380620059c8833981016040819052620000359162000296565b6001608052600760a052600060c0526001600160a01b0380851660e052838116610120528116610100526200006a8262000074565b5050505062000302565b600054610100900460ff1615808015620000955750600054600160ff909116105b80620000c55750620000b230620001cb60201b62001c891760201c565b158015620000c5575060005460ff166001145b6200012e5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562000152576000805461ff0019166101001790555b603280546001600160a01b03191661dead1790556035805483151560ff1990911617905562000180620001da565b8015620001c7576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6001600160a01b03163b151590565b600054610100900460ff16620002475760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840162000125565b60408051606081018252633b9aca0080825260006020830152436001600160401b031691909201819052600160c01b0217600155565b6001600160a01b03811681146200029357600080fd5b50565b60008060008060808587031215620002ad57600080fd5b8451620002ba816200027d565b6020860151909450620002cd816200027d565b60408601519093508015158114620002e457600080fd5b6060860151909250620002f7816200027d565b939692955090935050565b60805160a05160c05160e051610100516101205161563762000391600039600081816102690152818161079d01526110a00152600081816104c801526123d701526000818161016a01528181610a0601528181610be701528181610ffc015281816113b60152818161162801526121c301526000610f6701526000610f3e01526000610f1501526156376000f3fe60806040526004361061012c5760003560e01c80638c3152e9116100a5578063cff0ab9611610074578063e965084c11610059578063e965084c14610417578063e9e05c42146104a3578063f0498750146104b657600080fd5b8063cff0ab9614610356578063d53a822f146103f757600080fd5b80638c3152e9146102a05780639bf62d82146102c0578063a14238e7146102ed578063a35d99df1461031d57600080fd5b80635c975abb116100fc578063724c184c116100e1578063724c184c146102575780638456cb591461028b5780638b4c40b01461015157600080fd5b80635c975abb1461020d5780636dbffb781461023757600080fd5b80621c2ff6146101585780633f4ba83a146101b65780634870496f146101cb57806354fd4d50146101eb57600080fd5b36610153576101513334620186a06000604051806020016040528060008152506104ea565b005b600080fd5b34801561016457600080fd5b5061018c7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101c257600080fd5b50610151610785565b3480156101d757600080fd5b506101516101e6366004614be9565b6108a8565b3480156101f757600080fd5b50610200610f0e565b6040516101ad9190614d3f565b34801561021957600080fd5b506035546102279060ff1681565b60405190151581526020016101ad565b34801561024357600080fd5b50610227610252366004614d52565b610fb1565b34801561026357600080fd5b5061018c7f000000000000000000000000000000000000000000000000000000000000000081565b34801561029757600080fd5b50610151611088565b3480156102ac57600080fd5b506101516102bb366004614d6b565b6111a8565b3480156102cc57600080fd5b5060325461018c9073ffffffffffffffffffffffffffffffffffffffff1681565b3480156102f957600080fd5b50610227610308366004614d52565b60336020526000908152604090205460ff1681565b34801561032957600080fd5b5061033d610338366004614db8565b611a83565b60405167ffffffffffffffff90911681526020016101ad565b34801561036257600080fd5b506001546103be906fffffffffffffffffffffffffffffffff81169067ffffffffffffffff7001000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041683565b604080516fffffffffffffffffffffffffffffffff909416845267ffffffffffffffff92831660208501529116908201526060016101ad565b34801561040357600080fd5b50610151610412366004614de3565b611a9c565b34801561042357600080fd5b50610475610432366004614d52565b603460205260009081526040902080546001909101546fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041683565b604080519384526fffffffffffffffffffffffffffffffff92831660208501529116908201526060016101ad565b6101516104b1366004614dfe565b6104ea565b3480156104c257600080fd5b5061018c7f000000000000000000000000000000000000000000000000000000000000000081565b8260005a905083156105a15773ffffffffffffffffffffffffffffffffffffffff8716156105a157604080517f08c379a00000000000000000000000000000000000000000000000000000000081526020600482015260248101919091527f4f7074696d69736d506f7274616c3a206d7573742073656e6420746f2061646460448201527f72657373283029207768656e206372656174696e67206120636f6e747261637460648201526084015b60405180910390fd5b6105ab8351611a83565b67ffffffffffffffff168567ffffffffffffffff16101561064e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f4f7074696d69736d506f7274616c3a20676173206c696d697420746f6f20736d60448201527f616c6c00000000000000000000000000000000000000000000000000000000006064820152608401610598565b6201d4c0835111156106bc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4f7074696d69736d506f7274616c3a206461746120746f6f206c6172676500006044820152606401610598565b333281146106dd575033731111000000000000000000000000000000001111015b600034888888886040516020016106f8959493929190614e77565b604051602081830303815290604052905060008973ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32846040516107689190614d3f565b60405180910390a4505061077c8282611ca5565b50505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461084a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e20756e706175736500000000000000000000000000000000000000000000006064820152608401610598565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b60355460ff1615610915576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610598565b3073ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff16036109d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a20796f752063616e6e6f742073656e642060448201527f6d6573736167657320746f2074686520706f7274616c20636f6e7472616374006064820152608401610598565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018590526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610a62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a869190614efc565b519050610aa0610a9b36869003860186614f61565b611fd2565b8114610b2e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a20696e76616c6964206f7574707574207260448201527f6f6f742070726f6f6600000000000000000000000000000000000000000000006064820152608401610598565b6000610b398761202e565b6000818152603460209081526040918290208251606081018452815481526001909101546fffffffffffffffffffffffffffffffff8082169383018490527001000000000000000000000000000000009091041692810192909252919250901580610c6b5750805160408083015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610c43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c679190614efc565b5114155b610cf7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173682060448201527f68617320616c7265616479206265656e2070726f76656e0000000000000000006064820152608401610598565b60408051602081018490526000918101829052606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083018190529250610dc09101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f0100000000000000000000000000000000000000000000000000000000000000602083015290610db6888a614fc7565b8a6040013561205e565b610e4c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a20696e76616c696420776974686472617760448201527f616c20696e636c7573696f6e2070726f6f6600000000000000000000000000006064820152608401610598565b604080516060810182528581526fffffffffffffffffffffffffffffffff42811660208084019182528c831684860190815260008981526034835286812095518655925190518416700100000000000000000000000000000000029316929092176001909301929092558b830151908c0151925173ffffffffffffffffffffffffffffffffffffffff918216939091169186917f67a6208cfcc0801d50f6cbe764733f4fddf66ac0b04442061a8a8c0cb6b63f629190a4505050505050505050565b6060610f397f0000000000000000000000000000000000000000000000000000000000000000612082565b610f627f0000000000000000000000000000000000000000000000000000000000000000612082565b610f8b7f0000000000000000000000000000000000000000000000000000000000000000612082565b604051602001610f9d9392919061504b565b604051602081830303815290604052905090565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018290526000906110829073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063a25ae55790602401606060405180830381865afa158015611043573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110679190614efc565b602001516fffffffffffffffffffffffffffffffff166121bf565b92915050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461114d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e207061757365000000000000000000000000000000000000000000000000006064820152608401610598565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2589060200161089e565b60355460ff1615611215576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610598565b60325473ffffffffffffffffffffffffffffffffffffffff1661dead146112be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a2063616e206f6e6c79207472696767657260448201527f206f6e65207769746864726177616c20706572207472616e73616374696f6e006064820152608401610598565b60006112c98261202e565b60008181526034602090815260408083208151606081018352815481526001909101546fffffffffffffffffffffffffffffffff808216948301859052700100000000000000000000000000000000909104169181019190915292935090036113b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206e60448201527f6f74206265656e2070726f76656e2079657400000000000000000000000000006064820152608401610598565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663887862726040518163ffffffff1660e01b8152600401602060405180830381865afa15801561141f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144391906150c1565b81602001516fffffffffffffffffffffffffffffffff16101561150e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604b60248201527f4f7074696d69736d506f7274616c3a207769746864726177616c2074696d657360448201527f74616d70206c657373207468616e204c32204f7261636c65207374617274696e60648201527f672074696d657374616d70000000000000000000000000000000000000000000608482015260a401610598565b61152d81602001516fffffffffffffffffffffffffffffffff166121bf565b6115df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604560248201527f4f7074696d69736d506f7274616c3a2070726f76656e2077697468647261776160448201527f6c2066696e616c697a6174696f6e20706572696f6420686173206e6f7420656c60648201527f6170736564000000000000000000000000000000000000000000000000000000608482015260a401610598565b60408181015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015611684573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116a89190614efc565b8251815191925014611762576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604960248201527f4f7074696d69736d506f7274616c3a206f757470757420726f6f742070726f7660448201527f656e206973206e6f74207468652073616d652061732063757272656e74206f7560648201527f7470757420726f6f740000000000000000000000000000000000000000000000608482015260a401610598565b61178181602001516fffffffffffffffffffffffffffffffff166121bf565b611833576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604360248201527f4f7074696d69736d506f7274616c3a206f75747075742070726f706f73616c2060448201527f66696e616c697a6174696f6e20706572696f6420686173206e6f7420656c617060648201527f7365640000000000000000000000000000000000000000000000000000000000608482015260a401610598565b60008381526033602052604090205460ff16156118d2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206160448201527f6c7265616479206265656e2066696e616c697a656400000000000000000000006064820152608401610598565b600083815260336020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055908601516032805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff00000000000000000000000000000000000000009092169190911790558501516080860151606087015160a088015161197493929190612262565b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405190915084907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b906119d990841515815260200190565b60405180910390a2801580156119ef5750326001145b15611a7c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4f7074696d69736d506f7274616c3a207769746864726177616c206661696c6560448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610598565b5050505050565b6000611a90826010615109565b61108290615208615139565b600054610100900460ff1615808015611abc5750600054600160ff909116105b80611ad65750303b158015611ad6575060005460ff166001145b611b62576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610598565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611bc057600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead179055603580548315157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116179055611c226122c0565b8015611c8557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b600154600090611cdb907801000000000000000000000000000000000000000000000000900467ffffffffffffffff1643615165565b90506000611ce76123a3565b90506000816020015160ff16826000015163ffffffff16611d0891906151ab565b90508215611e3f57600154600090611d3f908390700100000000000000000000000000000000900467ffffffffffffffff16615213565b90506000836040015160ff1683611d569190615287565b600154611d769084906fffffffffffffffffffffffffffffffff16615287565b611d8091906151ab565b600154909150600090611dd190611daa9084906fffffffffffffffffffffffffffffffff16615343565b866060015163ffffffff168760a001516fffffffffffffffffffffffffffffffff16612469565b90506001861115611e0057611dfd611daa82876040015160ff1660018a611df89190615165565b612488565b90505b6fffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000067ffffffffffffffff4316021760015550505b60018054869190601090611e72908490700100000000000000000000000000000000900467ffffffffffffffff16615139565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550816000015163ffffffff16600160000160109054906101000a900467ffffffffffffffff1667ffffffffffffffff161315611f55576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603e60248201527f5265736f757263654d65746572696e673a2063616e6e6f7420627579206d6f7260448201527f6520676173207468616e20617661696c61626c6520676173206c696d697400006064820152608401610598565b600154600090611f81906fffffffffffffffffffffffffffffffff1667ffffffffffffffff88166153b7565b90506000611f9348633b9aca006124dd565b611f9d90836153f4565b905060005a611fac9088615165565b905080821115611fc857611fc8611fc38284615165565b6124f4565b5050505050505050565b60008160000151826020015183604001518460600151604051602001612011949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b80516020808301516040808501516060860151608087015160a08801519351600097612011979096959101615408565b60008061206a86612522565b905061207881868686612554565b9695505050505050565b6060816000036120c557505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156120ef57806120d98161545f565b91506120e89050600a836153f4565b91506120c9565b60008167ffffffffffffffff81111561210a5761210a614a0f565b6040519080825280601f01601f191660200182016040528015612134576020820181803683370190505b5090505b84156121b757612149600183615165565b9150612156600a86615497565b6121619060306154ab565b60f81b818381518110612176576121766154c3565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506121b0600a866153f4565b9450612138565b949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663f4daa2916040518163ffffffff1660e01b8152600401602060405180830381865afa15801561222c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225091906150c1565b61225a90836154ab565b421192915050565b6000806000612272866000612584565b9050806122a8576308c379a06000526020805278185361666543616c6c3a204e6f7420656e6f756768206761736058526064601cfd5b600080855160208701888b5af1979650505050505050565b600054610100900460ff16612357576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610598565b60408051606081018252633b9aca00808252600060208301524367ffffffffffffffff169190920181905278010000000000000000000000000000000000000000000000000217600155565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663cc731b026040518163ffffffff1660e01b815260040160c060405180830381865afa158015612440573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124649190615517565b905090565b600061247e61247885856125a2565b836125b2565b90505b9392505050565b6000670de0b6b3a76400006124c96124a085836151ab565b6124b290670de0b6b3a7640000615213565b6124c485670de0b6b3a7640000615287565b6125c1565b6124d39086615287565b61247e91906151ab565b6000818310156124ed5781612481565b5090919050565b6000805a90505b825a6125079083615165565b101561251d576125168261545f565b91506124fb565b505050565b6060818051906020012060405160200161253e91815260200190565b6040516020818303038152906040529050919050565b600061257b846125658786866125f2565b8051602091820120825192909101919091201490565b95945050505050565b600080603f83619c4001026040850201603f5a021015949350505050565b6000818312156124ed5781612481565b60008183126124ed5781612481565b6000612481670de0b6b3a7640000836125d98661307a565b6125e39190615287565b6125ed91906151ab565b6132be565b6060600084511161265f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d65726b6c65547269653a20656d707479206b657900000000000000000000006044820152606401610598565b600061266a846134fd565b90506000612677866135ec565b905060008460405160200161268e91815260200190565b60405160208183030381529060405290506000805b8451811015612ff15760008582815181106126c0576126c06154c3565b60200260200101519050845183111561275b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201527f74616c206b6579206c656e6774680000000000000000000000000000000000006064820152608401610598565b8260000361281457805180516020918201206040516127a99261278392910190815260200190565b604051602081830303815290604052858051602091820120825192909101919091201490565b61280f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f7420686173680000006044820152606401610598565b61296b565b8051516020116128ca578051805160209182012060405161283e9261278392910190815260200190565b61280f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e60448201527f616c2068617368000000000000000000000000000000000000000000000000006064820152608401610598565b80518451602080870191909120825191909201201461296b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f6460448201527f65206861736800000000000000000000000000000000000000000000000000006064820152608401610598565b612977601060016154ab565b81602001515103612b585784518303612af05760006129b382602001516010815181106129a6576129a66154c3565b602002602001015161364f565b90506000815111612a46576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e63682900000000006064820152608401610598565b60018751612a549190615165565b8314612ae2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e6368290000000000006064820152608401610598565b965061248195505050505050565b6000858481518110612b0457612b046154c3565b602001015160f81c60f81b60f81c9050600082602001518260ff1681518110612b2f57612b2f6154c3565b60200260200101519050612b42816137af565b9550612b4f6001866154ab565b94505050612fde565b600281602001515103612f56576000612b70826137d4565b9050600081600081518110612b8757612b876154c3565b016020015160f81c90506000612b9e6002836155b6565b612ba99060026155d8565b90506000612bba848360ff166137f8565b90506000612bc88a896137f8565b90506000612bd6838361382e565b905080835114612c68576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b65790000000000006064820152608401610598565b60ff851660021480612c7d575060ff85166003145b15612e715780825114612d12576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e6465720000006064820152608401610598565b6000612d2e88602001516001815181106129a6576129a66154c3565b90506000815111612dc1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c65616629000000000000006064820152608401610598565b60018d51612dcf9190615165565b8914612e5d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c6561662900000000000000006064820152608401610598565b9c506124819b505050505050505050505050565b60ff85161580612e84575060ff85166001145b15612ec357612eb08760200151600181518110612ea357612ea36154c3565b60200260200101516137af565b9950612ebc818a6154ab565b9850612f4b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f6465207769746860448201527f20616e20756e6b6e6f776e2070726566697800000000000000000000000000006064820152608401610598565b505050505050612fde565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e70617273656160448201527f626c65206e6f64650000000000000000000000000000000000000000000000006064820152608401610598565b5080612fe98161545f565b9150506126a3565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c6560448201527f6d656e74730000000000000000000000000000000000000000000000000000006064820152608401610598565b60008082136130e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610598565b600060606130f2846138dd565b03609f8181039490941b90931c6c465772b2bbbb5f824b15207a3081018102606090811d6d0388eaa27412d5aca026815d636e018202811d6d0df99ac502031bf953eff472fdcc018202811d6d13cdffb29d51d99322bdff5f2211018202811d6d0a0f742023def783a307a986912e018202811d6d01920d8043ca89b5239253284e42018202811d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7883018302821d6d0139601a2efabe717e604cbb4894018302821d6d02247f7a7b6594320649aa03aba1018302821d7fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9018302821d7ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f01830290911d7fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832019091027ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000000105711340daa0d5f769dba1915cef59f0815a5506027d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b393909302929092017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d92915050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdb731c958f34d94c182136132ef57506000919050565b680755bf798b4a1bf1e58212613361576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4558505f4f564552464c4f5700000000000000000000000000000000000000006044820152606401610598565b6503782dace9d9604e83901b059150600060606bb17217f7d1cf79abc9e3b39884821b056b80000000000000000000000001901d6bb17217f7d1cf79abc9e3b39881029093037fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a550481018102606090811d6d0277594991cfc85f6e2461837cd9018202811d7fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c018202811d6db1bbb201f443cf962f1a1d3db4a5018202811d7ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb018202811d6e05180bb14799ab47a8a8cb2a527d57016d02d16720577bd19bf614176fe9ea6c10fe68e7fd37d0007b713f765084018402831d9081019084017ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e010290911d6e0587f503bb6ea29d25fcb7401964500190910279d835ebba824c98fb31b83b2ca45c000000000000000000000000010574029d9dc38563c32e5c2f6dc192ee70ef65f9978af30260c3939093039290921c92915050565b805160609060008167ffffffffffffffff81111561351d5761351d614a0f565b60405190808252806020026020018201604052801561356257816020015b604080518082019091526060808252602082015281526020019060019003908161353b5790505b50905060005b828110156135e457604051806040016040528086838151811061358d5761358d6154c3565b602002602001015181526020016135bc8784815181106135af576135af6154c3565b60200260200101516139b3565b8152508282815181106135d1576135d16154c3565b6020908102919091010152600101613568565b509392505050565b606080604051905082518060011b603f8101601f1916830160405280835250602084016020830160005b83811015613644578060011b82018184015160001a8060041c8253600f811660018301535050600101613616565b509295945050505050565b6060600080600061365f856139c6565b91945092509050600081600181111561367a5761367a6155fb565b14613707576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206279746573206973206e6f7420612064617461206974656d000000000000006064820152608401610598565b61371182846154ab565b8551146137a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f524c505265616465723a2062797465732076616c756520636f6e7461696e732060448201527f616e20696e76616c69642072656d61696e6465720000000000000000000000006064820152608401610598565b61257b85602001518484614433565b606060208260000151106137cb576137c68261364f565b611082565b611082826144d4565b60606110826137f383602001516000815181106129a6576129a66154c3565b6135ec565b6060825182106138175750604080516020810190915260008152611082565b61248183838486516138299190615165565b6144ea565b60008060008351855110613843578351613846565b84515b90505b80821080156138cd5750838281518110613865576138656154c3565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168583815181106138a4576138a46154c3565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b156135e457816001019150613849565b6000808211613948576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610598565b5060016fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110821b1791821c111790565b60606110826139c1836146c2565b6147ab565b600080600080846000015111613a84576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610598565b6020840151805160001a607f8111613aa957600060016000945094509450505061442c565b60b78111613cb7576000613abe608083615165565b905080876000015111613b79576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604e60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20737472696e67206c656e6774682060648201527f2873686f727420737472696e6729000000000000000000000000000000000000608482015260a401610598565b6001838101517fff00000000000000000000000000000000000000000000000000000000000000169082141580613bf257507f80000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821610155b613ca4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a20696e76616c6964207072656669782c2073696e676c60448201527f652062797465203c203078383020617265206e6f74207072656669786564202860648201527f73686f727420737472696e672900000000000000000000000000000000000000608482015260a401610598565b506001955093506000925061442c915050565b60bf8111614005576000613ccc60b783615165565b905080876000015111613d87576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f6620737472696e67206c656e60648201527f67746820286c6f6e6720737472696e6729000000000000000000000000000000608482015260a401610598565b60018301517fff00000000000000000000000000000000000000000000000000000000000000166000819003613e65576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e6720737472696e672900000000000000000000000000000000000000000000608482015260a401610598565b600184015160088302610100031c60378111613f29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f20737472696e6729000000000000000000000000000000000000000000000000608482015260a401610598565b613f3381846154ab565b895111613fe8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e6720737472696e67290000000000000000000000000000000000000000608482015260a401610598565b613ff38360016154ab565b975095506000945061442c9350505050565b60f781116140e657600061401a60c083615165565b9050808760000151116140d5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e206c697374206c656e67746820287360648201527f686f7274206c6973742900000000000000000000000000000000000000000000608482015260a401610598565b60019550935084925061442c915050565b60006140f360f783615165565b9050808760000151116141ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f66206c697374206c656e677460648201527f6820286c6f6e67206c6973742900000000000000000000000000000000000000608482015260a401610598565b60018301517fff0000000000000000000000000000000000000000000000000000000000000016600081900361428c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e67206c69737429000000000000000000000000000000000000000000000000608482015260a401610598565b600184015160088302610100031c60378111614350576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604660248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f206c697374290000000000000000000000000000000000000000000000000000608482015260a401610598565b61435a81846154ab565b89511161440f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e67206c6973742900000000000000000000000000000000000000000000608482015260a401610598565b61441a8360016154ab565b975095506001945061442c9350505050565b9193909250565b606060008267ffffffffffffffff81111561445057614450614a0f565b6040519080825280601f01601f19166020018201604052801561447a576020820181803683370190505b5090508260000361448c579050612481565b600061449885876154ab565b90506020820160005b858110156144b95782810151828201526020016144a1565b858111156144c8576000868301525b50919695505050505050565b6060611082826020015160008460000151614433565b60608182601f011015614559576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610598565b8282840110156145c5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610598565b81830184511015614632576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401610598565b60608215801561465157604051915060008252602082016040526146b9565b6040519150601f8416801560200281840101858101878315602002848b0101015b8183101561468a578051835260209283019201614672565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b6040805180820190915260008082526020820152600082511161478d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610598565b50604080518082019091528151815260209182019181019190915290565b606060008060006147bb856139c6565b9194509250905060018160018111156147d6576147d66155fb565b14614863576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206c697374206973206e6f742061206c697374206974656d00000000000000006064820152608401610598565b845161486f83856154ab565b146148fc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f524c505265616465723a206c697374206974656d2068617320616e20696e766160448201527f6c696420646174612072656d61696e64657200000000000000000000000000006064820152608401610598565b6040805160208082526104208201909252600091816020015b60408051808201909152600080825260208201528152602001906001900390816149155790505090506000845b8751811015614a03576000806149886040518060400160405280858d6000015161496c9190615165565b8152602001858d6020015161498191906154ab565b90526139c6565b5091509150604051806040016040528083836149a491906154ab565b8152602001848c602001516149b991906154ab565b8152508585815181106149ce576149ce6154c3565b60209081029190910101526149e46001856154ab565b93506149f081836154ab565b6149fa90846154ab565b92505050614942565b50815295945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614a8557614a85614a0f565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114614ab157600080fd5b919050565b600082601f830112614ac757600080fd5b813567ffffffffffffffff811115614ae157614ae1614a0f565b614b1260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614a3e565b818152846020838601011115614b2757600080fd5b816020850160208301376000918101602001919091529392505050565b600060c08284031215614b5657600080fd5b60405160c0810167ffffffffffffffff8282108183111715614b7a57614b7a614a0f565b8160405282935084358352614b9160208601614a8d565b6020840152614ba260408601614a8d565b6040840152606085013560608401526080850135608084015260a0850135915080821115614bcf57600080fd5b50614bdc85828601614ab6565b60a0830152505092915050565b600080600080600085870360e0811215614c0257600080fd5b863567ffffffffffffffff80821115614c1a57600080fd5b614c268a838b01614b44565b97506020890135965060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc084011215614c5f57600080fd5b60408901955060c0890135925080831115614c7957600080fd5b828901925089601f840112614c8d57600080fd5b8235915080821115614c9e57600080fd5b508860208260051b8401011115614cb457600080fd5b959894975092955050506020019190565b60005b83811015614ce0578181015183820152602001614cc8565b83811115614cef576000848401525b50505050565b60008151808452614d0d816020860160208601614cc5565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006124816020830184614cf5565b600060208284031215614d6457600080fd5b5035919050565b600060208284031215614d7d57600080fd5b813567ffffffffffffffff811115614d9457600080fd5b6121b784828501614b44565b803567ffffffffffffffff81168114614ab157600080fd5b600060208284031215614dca57600080fd5b61248182614da0565b80358015158114614ab157600080fd5b600060208284031215614df557600080fd5b61248182614dd3565b600080600080600060a08688031215614e1657600080fd5b614e1f86614a8d565b945060208601359350614e3460408701614da0565b9250614e4260608701614dd3565b9150608086013567ffffffffffffffff811115614e5e57600080fd5b614e6a88828901614ab6565b9150509295509295909350565b8581528460208201527fffffffffffffffff0000000000000000000000000000000000000000000000008460c01b16604082015282151560f81b604882015260008251614ecb816049850160208701614cc5565b919091016049019695505050505050565b80516fffffffffffffffffffffffffffffffff81168114614ab157600080fd5b600060608284031215614f0e57600080fd5b6040516060810181811067ffffffffffffffff82111715614f3157614f31614a0f565b60405282518152614f4460208401614edc565b6020820152614f5560408401614edc565b60408201529392505050565b600060808284031215614f7357600080fd5b6040516080810181811067ffffffffffffffff82111715614f9657614f96614a0f565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b600067ffffffffffffffff80841115614fe257614fe2614a0f565b8360051b6020614ff3818301614a3e565b86815291850191818101903684111561500b57600080fd5b865b8481101561503f578035868111156150255760008081fd5b61503136828b01614ab6565b84525091830191830161500d565b50979650505050505050565b6000845161505d818460208901614cc5565b80830190507f2e000000000000000000000000000000000000000000000000000000000000008082528551615099816001850160208a01614cc5565b600192019182015283516150b4816002840160208801614cc5565b0160020195945050505050565b6000602082840312156150d357600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600067ffffffffffffffff80831681851681830481118215151615615130576151306150da565b02949350505050565b600067ffffffffffffffff80831681851680830382111561515c5761515c6150da565b01949350505050565b600082821015615177576151776150da565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826151ba576151ba61517c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561520e5761520e6150da565b500590565b6000808312837f80000000000000000000000000000000000000000000000000000000000000000183128115161561524d5761524d6150da565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018313811615615281576152816150da565b50500390565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000841360008413858304851182821616156152c8576152c86150da565b7f80000000000000000000000000000000000000000000000000000000000000006000871286820588128184161615615303576153036150da565b6000871292508782058712848416161561531f5761531f6150da565b87850587128184161615615335576153356150da565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0384138115161561537d5761537d6150da565b827f80000000000000000000000000000000000000000000000000000000000000000384128116156153b1576153b16150da565b50500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156153ef576153ef6150da565b500290565b6000826154035761540361517c565b500490565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a083015261545360c0830184614cf5565b98975050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615490576154906150da565b5060010190565b6000826154a6576154a661517c565b500690565b600082198211156154be576154be6150da565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b805163ffffffff81168114614ab157600080fd5b805160ff81168114614ab157600080fd5b600060c0828403121561552957600080fd5b60405160c0810181811067ffffffffffffffff8211171561554c5761554c614a0f565b604052615558836154f2565b815261556660208401615506565b602082015261557760408401615506565b6040820152615588606084016154f2565b6060820152615599608084016154f2565b60808201526155aa60a08401614edc565b60a08201529392505050565b600060ff8316806155c9576155c961517c565b8060ff84160691505092915050565b600060ff821660ff8416808210156155f2576155f26150da565b90039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea164736f6c634300080f000a",
} }
// OptimismPortalABI is the input ABI used to generate the binding from. // OptimismPortalABI is the input ABI used to generate the binding from.
......
...@@ -13,7 +13,7 @@ const OptimismPortalStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contrac ...@@ -13,7 +13,7 @@ const OptimismPortalStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contrac
var OptimismPortalStorageLayout = new(solc.StorageLayout) var OptimismPortalStorageLayout = new(solc.StorageLayout)
var OptimismPortalDeployedBin = "0x60806040526004361061012c5760003560e01c80638c3152e9116100a5578063cff0ab9611610074578063e965084c11610059578063e965084c14610417578063e9e05c42146104a3578063f0498750146104b657600080fd5b8063cff0ab9614610356578063d53a822f146103f757600080fd5b80638c3152e9146102a05780639bf62d82146102c0578063a14238e7146102ed578063a35d99df1461031d57600080fd5b80635c975abb116100fc578063724c184c116100e1578063724c184c146102575780638456cb591461028b5780638b4c40b01461015157600080fd5b80635c975abb1461020d5780636dbffb781461023757600080fd5b80621c2ff6146101585780633f4ba83a146101b65780634870496f146101cb57806354fd4d50146101eb57600080fd5b36610153576101513334620186a06000604051806020016040528060008152506104ea565b005b600080fd5b34801561016457600080fd5b5061018c7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101c257600080fd5b50610151610785565b3480156101d757600080fd5b506101516101e6366004614d21565b6108a8565b3480156101f757600080fd5b50610200610f0e565b6040516101ad9190614e77565b34801561021957600080fd5b506035546102279060ff1681565b60405190151581526020016101ad565b34801561024357600080fd5b50610227610252366004614e8a565b610fb1565b34801561026357600080fd5b5061018c7f000000000000000000000000000000000000000000000000000000000000000081565b34801561029757600080fd5b50610151611088565b3480156102ac57600080fd5b506101516102bb366004614ea3565b6111a8565b3480156102cc57600080fd5b5060325461018c9073ffffffffffffffffffffffffffffffffffffffff1681565b3480156102f957600080fd5b50610227610308366004614e8a565b60336020526000908152604090205460ff1681565b34801561032957600080fd5b5061033d610338366004614ef0565b611a83565b60405167ffffffffffffffff90911681526020016101ad565b34801561036257600080fd5b506001546103be906fffffffffffffffffffffffffffffffff81169067ffffffffffffffff7001000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041683565b604080516fffffffffffffffffffffffffffffffff909416845267ffffffffffffffff92831660208501529116908201526060016101ad565b34801561040357600080fd5b50610151610412366004614f1b565b611a9c565b34801561042357600080fd5b50610475610432366004614e8a565b603460205260009081526040902080546001909101546fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041683565b604080519384526fffffffffffffffffffffffffffffffff92831660208501529116908201526060016101ad565b6101516104b1366004614f36565b6104ea565b3480156104c257600080fd5b5061018c7f000000000000000000000000000000000000000000000000000000000000000081565b8260005a905083156105a15773ffffffffffffffffffffffffffffffffffffffff8716156105a157604080517f08c379a00000000000000000000000000000000000000000000000000000000081526020600482015260248101919091527f4f7074696d69736d506f7274616c3a206d7573742073656e6420746f2061646460448201527f72657373283029207768656e206372656174696e67206120636f6e747261637460648201526084015b60405180910390fd5b6105ab8351611a83565b67ffffffffffffffff168567ffffffffffffffff16101561064e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f4f7074696d69736d506f7274616c3a20676173206c696d697420746f6f20736d60448201527f616c6c00000000000000000000000000000000000000000000000000000000006064820152608401610598565b6201d4c0835111156106bc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4f7074696d69736d506f7274616c3a206461746120746f6f206c6172676500006044820152606401610598565b333281146106dd575033731111000000000000000000000000000000001111015b600034888888886040516020016106f8959493929190614faf565b604051602081830303815290604052905060008973ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32846040516107689190614e77565b60405180910390a4505061077c8282611ca5565b50505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461084a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e20756e706175736500000000000000000000000000000000000000000000006064820152608401610598565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b60355460ff1615610915576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610598565b3073ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff16036109d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a20796f752063616e6e6f742073656e642060448201527f6d6573736167657320746f2074686520706f7274616c20636f6e7472616374006064820152608401610598565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018590526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610a62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a869190615034565b519050610aa0610a9b36869003860186615099565b611fd2565b8114610b2e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a20696e76616c6964206f7574707574207260448201527f6f6f742070726f6f6600000000000000000000000000000000000000000000006064820152608401610598565b6000610b398761202e565b6000818152603460209081526040918290208251606081018452815481526001909101546fffffffffffffffffffffffffffffffff8082169383018490527001000000000000000000000000000000009091041692810192909252919250901580610c6b5750805160408083015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610c43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c679190615034565b5114155b610cf7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173682060448201527f68617320616c7265616479206265656e2070726f76656e0000000000000000006064820152608401610598565b60408051602081018490526000918101829052606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083018190529250610dc09101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f0100000000000000000000000000000000000000000000000000000000000000602083015290610db6888a6150ff565b8a6040013561205e565b610e4c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a20696e76616c696420776974686472617760448201527f616c20696e636c7573696f6e2070726f6f6600000000000000000000000000006064820152608401610598565b604080516060810182528581526fffffffffffffffffffffffffffffffff42811660208084019182528c831684860190815260008981526034835286812095518655925190518416700100000000000000000000000000000000029316929092176001909301929092558b830151908c0151925173ffffffffffffffffffffffffffffffffffffffff918216939091169186917f67a6208cfcc0801d50f6cbe764733f4fddf66ac0b04442061a8a8c0cb6b63f629190a4505050505050505050565b6060610f397f0000000000000000000000000000000000000000000000000000000000000000612082565b610f627f0000000000000000000000000000000000000000000000000000000000000000612082565b610f8b7f0000000000000000000000000000000000000000000000000000000000000000612082565b604051602001610f9d93929190615183565b604051602081830303815290604052905090565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018290526000906110829073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063a25ae55790602401606060405180830381865afa158015611043573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110679190615034565b602001516fffffffffffffffffffffffffffffffff166121bf565b92915050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461114d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e207061757365000000000000000000000000000000000000000000000000006064820152608401610598565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2589060200161089e565b60355460ff1615611215576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610598565b60325473ffffffffffffffffffffffffffffffffffffffff1661dead146112be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a2063616e206f6e6c79207472696767657260448201527f206f6e65207769746864726177616c20706572207472616e73616374696f6e006064820152608401610598565b60006112c98261202e565b60008181526034602090815260408083208151606081018352815481526001909101546fffffffffffffffffffffffffffffffff808216948301859052700100000000000000000000000000000000909104169181019190915292935090036113b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206e60448201527f6f74206265656e2070726f76656e2079657400000000000000000000000000006064820152608401610598565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663887862726040518163ffffffff1660e01b8152600401602060405180830381865afa15801561141f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144391906151f9565b81602001516fffffffffffffffffffffffffffffffff16101561150e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604b60248201527f4f7074696d69736d506f7274616c3a207769746864726177616c2074696d657360448201527f74616d70206c657373207468616e204c32204f7261636c65207374617274696e60648201527f672074696d657374616d70000000000000000000000000000000000000000000608482015260a401610598565b61152d81602001516fffffffffffffffffffffffffffffffff166121bf565b6115df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604560248201527f4f7074696d69736d506f7274616c3a2070726f76656e2077697468647261776160448201527f6c2066696e616c697a6174696f6e20706572696f6420686173206e6f7420656c60648201527f6170736564000000000000000000000000000000000000000000000000000000608482015260a401610598565b60408181015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015611684573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116a89190615034565b8251815191925014611762576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604960248201527f4f7074696d69736d506f7274616c3a206f757470757420726f6f742070726f7660448201527f656e206973206e6f74207468652073616d652061732063757272656e74206f7560648201527f7470757420726f6f740000000000000000000000000000000000000000000000608482015260a401610598565b61178181602001516fffffffffffffffffffffffffffffffff166121bf565b611833576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604360248201527f4f7074696d69736d506f7274616c3a206f75747075742070726f706f73616c2060448201527f66696e616c697a6174696f6e20706572696f6420686173206e6f7420656c617060648201527f7365640000000000000000000000000000000000000000000000000000000000608482015260a401610598565b60008381526033602052604090205460ff16156118d2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206160448201527f6c7265616479206265656e2066696e616c697a656400000000000000000000006064820152608401610598565b600083815260336020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055908601516032805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff00000000000000000000000000000000000000009092169190911790558501516080860151606087015160a088015161197493929190612262565b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405190915084907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b906119d990841515815260200190565b60405180910390a2801580156119ef5750326001145b15611a7c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4f7074696d69736d506f7274616c3a207769746864726177616c206661696c6560448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610598565b5050505050565b6000611a90826010615241565b61108290615208615271565b600054610100900460ff1615808015611abc5750600054600160ff909116105b80611ad65750303b158015611ad6575060005460ff166001145b611b62576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610598565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611bc057600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead179055603580548315157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116179055611c226122c0565b8015611c8557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b600154600090611cdb907801000000000000000000000000000000000000000000000000900467ffffffffffffffff164361529d565b90506000611ce76123a3565b90506000816020015160ff16826000015163ffffffff16611d0891906152e3565b90508215611e3f57600154600090611d3f908390700100000000000000000000000000000000900467ffffffffffffffff1661534b565b90506000836040015160ff1683611d5691906153bf565b600154611d769084906fffffffffffffffffffffffffffffffff166153bf565b611d8091906152e3565b600154909150600090611dd190611daa9084906fffffffffffffffffffffffffffffffff1661547b565b866060015163ffffffff168760a001516fffffffffffffffffffffffffffffffff16612469565b90506001861115611e0057611dfd611daa82876040015160ff1660018a611df8919061529d565b612488565b90505b6fffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000067ffffffffffffffff4316021760015550505b60018054869190601090611e72908490700100000000000000000000000000000000900467ffffffffffffffff16615271565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550816000015163ffffffff16600160000160109054906101000a900467ffffffffffffffff1667ffffffffffffffff161315611f55576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603e60248201527f5265736f757263654d65746572696e673a2063616e6e6f7420627579206d6f7260448201527f6520676173207468616e20617661696c61626c6520676173206c696d697400006064820152608401610598565b600154600090611f81906fffffffffffffffffffffffffffffffff1667ffffffffffffffff88166154ef565b90506000611f9348633b9aca006124dd565b611f9d908361552c565b905060005a611fac908861529d565b905080821115611fc857611fc8611fc3828461529d565b6124f4565b5050505050505050565b60008160000151826020015183604001518460600151604051602001612011949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b80516020808301516040808501516060860151608087015160a08801519351600097612011979096959101615540565b60008061206a86612522565b905061207881868686612554565b9695505050505050565b6060816000036120c557505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156120ef57806120d981615597565b91506120e89050600a8361552c565b91506120c9565b60008167ffffffffffffffff81111561210a5761210a614b47565b6040519080825280601f01601f191660200182016040528015612134576020820181803683370190505b5090505b84156121b75761214960018361529d565b9150612156600a866155cf565b6121619060306155e3565b60f81b818381518110612176576121766155fb565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506121b0600a8661552c565b9450612138565b949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663f4daa2916040518163ffffffff1660e01b8152600401602060405180830381865afa15801561222c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225091906151f9565b61225a90836155e3565b421192915050565b6000806000612272866000612584565b9050806122a8576308c379a06000526020805278185361666543616c6c3a204e6f7420656e6f756768206761736058526064601cfd5b600080855160208701888b5af1979650505050505050565b600054610100900460ff16612357576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610598565b60408051606081018252633b9aca00808252600060208301524367ffffffffffffffff169190920181905278010000000000000000000000000000000000000000000000000217600155565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663cc731b026040518163ffffffff1660e01b815260040160c060405180830381865afa158015612440573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612464919061564f565b905090565b600061247e61247885856125a2565b836125b2565b90505b9392505050565b6000670de0b6b3a76400006124c96124a085836152e3565b6124b290670de0b6b3a764000061534b565b6124c485670de0b6b3a76400006153bf565b6125c1565b6124d390866153bf565b61247e91906152e3565b6000818310156124ed5781612481565b5090919050565b6000805a90505b825a612507908361529d565b101561251d5761251682615597565b91506124fb565b505050565b6060818051906020012060405160200161253e91815260200190565b6040516020818303038152906040529050919050565b600061257b846125658786866125f2565b8051602091820120825192909101919091201490565b95945050505050565b600080603f83619c4001026040850201603f5a021015949350505050565b6000818312156124ed5781612481565b60008183126124ed5781612481565b6000612481670de0b6b3a7640000836125d98661307a565b6125e391906153bf565b6125ed91906152e3565b6132be565b6060600084511161265f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d65726b6c65547269653a20656d707479206b657900000000000000000000006044820152606401610598565b600061266a846134fd565b90506000612677866135ec565b905060008460405160200161268e91815260200190565b60405160208183030381529060405290506000805b8451811015612ff15760008582815181106126c0576126c06155fb565b60200260200101519050845183111561275b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201527f74616c206b6579206c656e6774680000000000000000000000000000000000006064820152608401610598565b8260000361281457805180516020918201206040516127a99261278392910190815260200190565b604051602081830303815290604052858051602091820120825192909101919091201490565b61280f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f7420686173680000006044820152606401610598565b61296b565b8051516020116128ca578051805160209182012060405161283e9261278392910190815260200190565b61280f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e60448201527f616c2068617368000000000000000000000000000000000000000000000000006064820152608401610598565b80518451602080870191909120825191909201201461296b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f6460448201527f65206861736800000000000000000000000000000000000000000000000000006064820152608401610598565b612977601060016155e3565b81602001515103612b585784518303612af05760006129b382602001516010815181106129a6576129a66155fb565b6020026020010151613787565b90506000815111612a46576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e63682900000000006064820152608401610598565b60018751612a54919061529d565b8314612ae2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e6368290000000000006064820152608401610598565b965061248195505050505050565b6000858481518110612b0457612b046155fb565b602001015160f81c60f81b60f81c9050600082602001518260ff1681518110612b2f57612b2f6155fb565b60200260200101519050612b42816138e7565b9550612b4f6001866155e3565b94505050612fde565b600281602001515103612f56576000612b708261390c565b9050600081600081518110612b8757612b876155fb565b016020015160f81c90506000612b9e6002836156ee565b612ba9906002615710565b90506000612bba848360ff16613930565b90506000612bc88a89613930565b90506000612bd68383613966565b905080835114612c68576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b65790000000000006064820152608401610598565b60ff851660021480612c7d575060ff85166003145b15612e715780825114612d12576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e6465720000006064820152608401610598565b6000612d2e88602001516001815181106129a6576129a66155fb565b90506000815111612dc1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c65616629000000000000006064820152608401610598565b60018d51612dcf919061529d565b8914612e5d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c6561662900000000000000006064820152608401610598565b9c506124819b505050505050505050505050565b60ff85161580612e84575060ff85166001145b15612ec357612eb08760200151600181518110612ea357612ea36155fb565b60200260200101516138e7565b9950612ebc818a6155e3565b9850612f4b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f6465207769746860448201527f20616e20756e6b6e6f776e2070726566697800000000000000000000000000006064820152608401610598565b505050505050612fde565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e70617273656160448201527f626c65206e6f64650000000000000000000000000000000000000000000000006064820152608401610598565b5080612fe981615597565b9150506126a3565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c6560448201527f6d656e74730000000000000000000000000000000000000000000000000000006064820152608401610598565b60008082136130e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610598565b600060606130f284613a15565b03609f8181039490941b90931c6c465772b2bbbb5f824b15207a3081018102606090811d6d0388eaa27412d5aca026815d636e018202811d6d0df99ac502031bf953eff472fdcc018202811d6d13cdffb29d51d99322bdff5f2211018202811d6d0a0f742023def783a307a986912e018202811d6d01920d8043ca89b5239253284e42018202811d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7883018302821d6d0139601a2efabe717e604cbb4894018302821d6d02247f7a7b6594320649aa03aba1018302821d7fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9018302821d7ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f01830290911d7fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832019091027ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000000105711340daa0d5f769dba1915cef59f0815a5506027d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b393909302929092017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d92915050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdb731c958f34d94c182136132ef57506000919050565b680755bf798b4a1bf1e58212613361576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4558505f4f564552464c4f5700000000000000000000000000000000000000006044820152606401610598565b6503782dace9d9604e83901b059150600060606bb17217f7d1cf79abc9e3b39884821b056b80000000000000000000000001901d6bb17217f7d1cf79abc9e3b39881029093037fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a550481018102606090811d6d0277594991cfc85f6e2461837cd9018202811d7fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c018202811d6db1bbb201f443cf962f1a1d3db4a5018202811d7ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb018202811d6e05180bb14799ab47a8a8cb2a527d57016d02d16720577bd19bf614176fe9ea6c10fe68e7fd37d0007b713f765084018402831d9081019084017ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e010290911d6e0587f503bb6ea29d25fcb7401964500190910279d835ebba824c98fb31b83b2ca45c000000000000000000000000010574029d9dc38563c32e5c2f6dc192ee70ef65f9978af30260c3939093039290921c92915050565b805160609060008167ffffffffffffffff81111561351d5761351d614b47565b60405190808252806020026020018201604052801561356257816020015b604080518082019091526060808252602082015281526020019060019003908161353b5790505b50905060005b828110156135e457604051806040016040528086838151811061358d5761358d6155fb565b602002602001015181526020016135bc8784815181106135af576135af6155fb565b6020026020010151613aeb565b8152508282815181106135d1576135d16155fb565b6020908102919091010152600101613568565b509392505050565b805160609060006135fe8260026154ef565b67ffffffffffffffff81111561361657613616614b47565b6040519080825280601f01601f191660200182016040528015613640576020820181803683370190505b5090506000805b8381101561377d57858181518110613661576136616155fb565b6020910101517fff000000000000000000000000000000000000000000000000000000000000008116925060041c7f0ff000000000000000000000000000000000000000000000000000000000000016836136bd8360026154ef565b815181106136cd576136cd6155fb565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f0f0000000000000000000000000000000000000000000000000000000000000082168361372b8360026154ef565b6137369060016155e3565b81518110613746576137466155fb565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101613647565b5090949350505050565b6060600080600061379785613afe565b9194509250905060008160018111156137b2576137b2615733565b1461383f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206279746573206973206e6f7420612064617461206974656d000000000000006064820152608401610598565b61384982846155e3565b8551146138d8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f524c505265616465723a2062797465732076616c756520636f6e7461696e732060448201527f616e20696e76616c69642072656d61696e6465720000000000000000000000006064820152608401610598565b61257b8560200151848461456b565b60606020826000015110613903576138fe82613787565b611082565b6110828261460c565b606061108261392b83602001516000815181106129a6576129a66155fb565b6135ec565b60608251821061394f5750604080516020810190915260008152611082565b6124818383848651613961919061529d565b614622565b6000806000835185511061397b57835161397e565b84515b90505b8082108015613a05575083828151811061399d5761399d6155fb565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168583815181106139dc576139dc6155fb565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b156135e457816001019150613981565b6000808211613a80576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610598565b5060016fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110821b1791821c111790565b6060611082613af9836147fa565b6148e3565b600080600080846000015111613bbc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610598565b6020840151805160001a607f8111613be1576000600160009450945094505050614564565b60b78111613def576000613bf660808361529d565b905080876000015111613cb1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604e60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20737472696e67206c656e6774682060648201527f2873686f727420737472696e6729000000000000000000000000000000000000608482015260a401610598565b6001838101517fff00000000000000000000000000000000000000000000000000000000000000169082141580613d2a57507f80000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821610155b613ddc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a20696e76616c6964207072656669782c2073696e676c60448201527f652062797465203c203078383020617265206e6f74207072656669786564202860648201527f73686f727420737472696e672900000000000000000000000000000000000000608482015260a401610598565b5060019550935060009250614564915050565b60bf811161413d576000613e0460b78361529d565b905080876000015111613ebf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f6620737472696e67206c656e60648201527f67746820286c6f6e6720737472696e6729000000000000000000000000000000608482015260a401610598565b60018301517fff00000000000000000000000000000000000000000000000000000000000000166000819003613f9d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e6720737472696e672900000000000000000000000000000000000000000000608482015260a401610598565b600184015160088302610100031c60378111614061576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f20737472696e6729000000000000000000000000000000000000000000000000608482015260a401610598565b61406b81846155e3565b895111614120576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e6720737472696e67290000000000000000000000000000000000000000608482015260a401610598565b61412b8360016155e3565b97509550600094506145649350505050565b60f7811161421e57600061415260c08361529d565b90508087600001511161420d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e206c697374206c656e67746820287360648201527f686f7274206c6973742900000000000000000000000000000000000000000000608482015260a401610598565b600195509350849250614564915050565b600061422b60f78361529d565b9050808760000151116142e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f66206c697374206c656e677460648201527f6820286c6f6e67206c6973742900000000000000000000000000000000000000608482015260a401610598565b60018301517fff000000000000000000000000000000000000000000000000000000000000001660008190036143c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e67206c69737429000000000000000000000000000000000000000000000000608482015260a401610598565b600184015160088302610100031c60378111614488576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604660248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f206c697374290000000000000000000000000000000000000000000000000000608482015260a401610598565b61449281846155e3565b895111614547576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e67206c6973742900000000000000000000000000000000000000000000608482015260a401610598565b6145528360016155e3565b97509550600194506145649350505050565b9193909250565b606060008267ffffffffffffffff81111561458857614588614b47565b6040519080825280601f01601f1916602001820160405280156145b2576020820181803683370190505b509050826000036145c4579050612481565b60006145d085876155e3565b90506020820160005b858110156145f15782810151828201526020016145d9565b85811115614600576000868301525b50919695505050505050565b606061108282602001516000846000015161456b565b60608182601f011015614691576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610598565b8282840110156146fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610598565b8183018451101561476a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401610598565b60608215801561478957604051915060008252602082016040526147f1565b6040519150601f8416801560200281840101858101878315602002848b0101015b818310156147c25780518352602092830192016147aa565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b604080518082019091526000808252602082015260008251116148c5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610598565b50604080518082019091528151815260209182019181019190915290565b606060008060006148f385613afe565b91945092509050600181600181111561490e5761490e615733565b1461499b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206c697374206973206e6f742061206c697374206974656d00000000000000006064820152608401610598565b84516149a783856155e3565b14614a34576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f524c505265616465723a206c697374206974656d2068617320616e20696e766160448201527f6c696420646174612072656d61696e64657200000000000000000000000000006064820152608401610598565b6040805160208082526104208201909252600091816020015b6040805180820190915260008082526020820152815260200190600190039081614a4d5790505090506000845b8751811015614b3b57600080614ac06040518060400160405280858d60000151614aa4919061529d565b8152602001858d60200151614ab991906155e3565b9052613afe565b509150915060405180604001604052808383614adc91906155e3565b8152602001848c60200151614af191906155e3565b815250858581518110614b0657614b066155fb565b6020908102919091010152614b1c6001856155e3565b9350614b2881836155e3565b614b3290846155e3565b92505050614a7a565b50815295945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614bbd57614bbd614b47565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114614be957600080fd5b919050565b600082601f830112614bff57600080fd5b813567ffffffffffffffff811115614c1957614c19614b47565b614c4a60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614b76565b818152846020838601011115614c5f57600080fd5b816020850160208301376000918101602001919091529392505050565b600060c08284031215614c8e57600080fd5b60405160c0810167ffffffffffffffff8282108183111715614cb257614cb2614b47565b8160405282935084358352614cc960208601614bc5565b6020840152614cda60408601614bc5565b6040840152606085013560608401526080850135608084015260a0850135915080821115614d0757600080fd5b50614d1485828601614bee565b60a0830152505092915050565b600080600080600085870360e0811215614d3a57600080fd5b863567ffffffffffffffff80821115614d5257600080fd5b614d5e8a838b01614c7c565b97506020890135965060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc084011215614d9757600080fd5b60408901955060c0890135925080831115614db157600080fd5b828901925089601f840112614dc557600080fd5b8235915080821115614dd657600080fd5b508860208260051b8401011115614dec57600080fd5b959894975092955050506020019190565b60005b83811015614e18578181015183820152602001614e00565b83811115614e27576000848401525b50505050565b60008151808452614e45816020860160208601614dfd565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006124816020830184614e2d565b600060208284031215614e9c57600080fd5b5035919050565b600060208284031215614eb557600080fd5b813567ffffffffffffffff811115614ecc57600080fd5b6121b784828501614c7c565b803567ffffffffffffffff81168114614be957600080fd5b600060208284031215614f0257600080fd5b61248182614ed8565b80358015158114614be957600080fd5b600060208284031215614f2d57600080fd5b61248182614f0b565b600080600080600060a08688031215614f4e57600080fd5b614f5786614bc5565b945060208601359350614f6c60408701614ed8565b9250614f7a60608701614f0b565b9150608086013567ffffffffffffffff811115614f9657600080fd5b614fa288828901614bee565b9150509295509295909350565b8581528460208201527fffffffffffffffff0000000000000000000000000000000000000000000000008460c01b16604082015282151560f81b604882015260008251615003816049850160208701614dfd565b919091016049019695505050505050565b80516fffffffffffffffffffffffffffffffff81168114614be957600080fd5b60006060828403121561504657600080fd5b6040516060810181811067ffffffffffffffff8211171561506957615069614b47565b6040528251815261507c60208401615014565b602082015261508d60408401615014565b60408201529392505050565b6000608082840312156150ab57600080fd5b6040516080810181811067ffffffffffffffff821117156150ce576150ce614b47565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b600067ffffffffffffffff8084111561511a5761511a614b47565b8360051b602061512b818301614b76565b86815291850191818101903684111561514357600080fd5b865b848110156151775780358681111561515d5760008081fd5b61516936828b01614bee565b845250918301918301615145565b50979650505050505050565b60008451615195818460208901614dfd565b80830190507f2e0000000000000000000000000000000000000000000000000000000000000080825285516151d1816001850160208a01614dfd565b600192019182015283516151ec816002840160208801614dfd565b0160020195945050505050565b60006020828403121561520b57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600067ffffffffffffffff8083168185168183048111821515161561526857615268615212565b02949350505050565b600067ffffffffffffffff80831681851680830382111561529457615294615212565b01949350505050565b6000828210156152af576152af615212565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826152f2576152f26152b4565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561534657615346615212565b500590565b6000808312837f80000000000000000000000000000000000000000000000000000000000000000183128115161561538557615385615212565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0183138116156153b9576153b9615212565b50500390565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60008413600084138583048511828216161561540057615400615212565b7f8000000000000000000000000000000000000000000000000000000000000000600087128682058812818416161561543b5761543b615212565b6000871292508782058712848416161561545757615457615212565b8785058712818416161561546d5761546d615212565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038413811516156154b5576154b5615212565b827f80000000000000000000000000000000000000000000000000000000000000000384128116156154e9576154e9615212565b50500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561552757615527615212565b500290565b60008261553b5761553b6152b4565b500490565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a083015261558b60c0830184614e2d565b98975050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036155c8576155c8615212565b5060010190565b6000826155de576155de6152b4565b500690565b600082198211156155f6576155f6615212565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b805163ffffffff81168114614be957600080fd5b805160ff81168114614be957600080fd5b600060c0828403121561566157600080fd5b60405160c0810181811067ffffffffffffffff8211171561568457615684614b47565b6040526156908361562a565b815261569e6020840161563e565b60208201526156af6040840161563e565b60408201526156c06060840161562a565b60608201526156d16080840161562a565b60808201526156e260a08401615014565b60a08201529392505050565b600060ff831680615701576157016152b4565b8060ff84160691505092915050565b600060ff821660ff84168082101561572a5761572a615212565b90039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea164736f6c634300080f000a" var OptimismPortalDeployedBin = "0x60806040526004361061012c5760003560e01c80638c3152e9116100a5578063cff0ab9611610074578063e965084c11610059578063e965084c14610417578063e9e05c42146104a3578063f0498750146104b657600080fd5b8063cff0ab9614610356578063d53a822f146103f757600080fd5b80638c3152e9146102a05780639bf62d82146102c0578063a14238e7146102ed578063a35d99df1461031d57600080fd5b80635c975abb116100fc578063724c184c116100e1578063724c184c146102575780638456cb591461028b5780638b4c40b01461015157600080fd5b80635c975abb1461020d5780636dbffb781461023757600080fd5b80621c2ff6146101585780633f4ba83a146101b65780634870496f146101cb57806354fd4d50146101eb57600080fd5b36610153576101513334620186a06000604051806020016040528060008152506104ea565b005b600080fd5b34801561016457600080fd5b5061018c7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101c257600080fd5b50610151610785565b3480156101d757600080fd5b506101516101e6366004614be9565b6108a8565b3480156101f757600080fd5b50610200610f0e565b6040516101ad9190614d3f565b34801561021957600080fd5b506035546102279060ff1681565b60405190151581526020016101ad565b34801561024357600080fd5b50610227610252366004614d52565b610fb1565b34801561026357600080fd5b5061018c7f000000000000000000000000000000000000000000000000000000000000000081565b34801561029757600080fd5b50610151611088565b3480156102ac57600080fd5b506101516102bb366004614d6b565b6111a8565b3480156102cc57600080fd5b5060325461018c9073ffffffffffffffffffffffffffffffffffffffff1681565b3480156102f957600080fd5b50610227610308366004614d52565b60336020526000908152604090205460ff1681565b34801561032957600080fd5b5061033d610338366004614db8565b611a83565b60405167ffffffffffffffff90911681526020016101ad565b34801561036257600080fd5b506001546103be906fffffffffffffffffffffffffffffffff81169067ffffffffffffffff7001000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041683565b604080516fffffffffffffffffffffffffffffffff909416845267ffffffffffffffff92831660208501529116908201526060016101ad565b34801561040357600080fd5b50610151610412366004614de3565b611a9c565b34801561042357600080fd5b50610475610432366004614d52565b603460205260009081526040902080546001909101546fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041683565b604080519384526fffffffffffffffffffffffffffffffff92831660208501529116908201526060016101ad565b6101516104b1366004614dfe565b6104ea565b3480156104c257600080fd5b5061018c7f000000000000000000000000000000000000000000000000000000000000000081565b8260005a905083156105a15773ffffffffffffffffffffffffffffffffffffffff8716156105a157604080517f08c379a00000000000000000000000000000000000000000000000000000000081526020600482015260248101919091527f4f7074696d69736d506f7274616c3a206d7573742073656e6420746f2061646460448201527f72657373283029207768656e206372656174696e67206120636f6e747261637460648201526084015b60405180910390fd5b6105ab8351611a83565b67ffffffffffffffff168567ffffffffffffffff16101561064e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f4f7074696d69736d506f7274616c3a20676173206c696d697420746f6f20736d60448201527f616c6c00000000000000000000000000000000000000000000000000000000006064820152608401610598565b6201d4c0835111156106bc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4f7074696d69736d506f7274616c3a206461746120746f6f206c6172676500006044820152606401610598565b333281146106dd575033731111000000000000000000000000000000001111015b600034888888886040516020016106f8959493929190614e77565b604051602081830303815290604052905060008973ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32846040516107689190614d3f565b60405180910390a4505061077c8282611ca5565b50505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461084a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e20756e706175736500000000000000000000000000000000000000000000006064820152608401610598565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b60355460ff1615610915576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610598565b3073ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff16036109d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a20796f752063616e6e6f742073656e642060448201527f6d6573736167657320746f2074686520706f7274616c20636f6e7472616374006064820152608401610598565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018590526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610a62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a869190614efc565b519050610aa0610a9b36869003860186614f61565b611fd2565b8114610b2e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a20696e76616c6964206f7574707574207260448201527f6f6f742070726f6f6600000000000000000000000000000000000000000000006064820152608401610598565b6000610b398761202e565b6000818152603460209081526040918290208251606081018452815481526001909101546fffffffffffffffffffffffffffffffff8082169383018490527001000000000000000000000000000000009091041692810192909252919250901580610c6b5750805160408083015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610c43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c679190614efc565b5114155b610cf7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173682060448201527f68617320616c7265616479206265656e2070726f76656e0000000000000000006064820152608401610598565b60408051602081018490526000918101829052606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083018190529250610dc09101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f0100000000000000000000000000000000000000000000000000000000000000602083015290610db6888a614fc7565b8a6040013561205e565b610e4c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a20696e76616c696420776974686472617760448201527f616c20696e636c7573696f6e2070726f6f6600000000000000000000000000006064820152608401610598565b604080516060810182528581526fffffffffffffffffffffffffffffffff42811660208084019182528c831684860190815260008981526034835286812095518655925190518416700100000000000000000000000000000000029316929092176001909301929092558b830151908c0151925173ffffffffffffffffffffffffffffffffffffffff918216939091169186917f67a6208cfcc0801d50f6cbe764733f4fddf66ac0b04442061a8a8c0cb6b63f629190a4505050505050505050565b6060610f397f0000000000000000000000000000000000000000000000000000000000000000612082565b610f627f0000000000000000000000000000000000000000000000000000000000000000612082565b610f8b7f0000000000000000000000000000000000000000000000000000000000000000612082565b604051602001610f9d9392919061504b565b604051602081830303815290604052905090565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018290526000906110829073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063a25ae55790602401606060405180830381865afa158015611043573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110679190614efc565b602001516fffffffffffffffffffffffffffffffff166121bf565b92915050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461114d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e207061757365000000000000000000000000000000000000000000000000006064820152608401610598565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2589060200161089e565b60355460ff1615611215576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610598565b60325473ffffffffffffffffffffffffffffffffffffffff1661dead146112be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a2063616e206f6e6c79207472696767657260448201527f206f6e65207769746864726177616c20706572207472616e73616374696f6e006064820152608401610598565b60006112c98261202e565b60008181526034602090815260408083208151606081018352815481526001909101546fffffffffffffffffffffffffffffffff808216948301859052700100000000000000000000000000000000909104169181019190915292935090036113b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206e60448201527f6f74206265656e2070726f76656e2079657400000000000000000000000000006064820152608401610598565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663887862726040518163ffffffff1660e01b8152600401602060405180830381865afa15801561141f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144391906150c1565b81602001516fffffffffffffffffffffffffffffffff16101561150e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604b60248201527f4f7074696d69736d506f7274616c3a207769746864726177616c2074696d657360448201527f74616d70206c657373207468616e204c32204f7261636c65207374617274696e60648201527f672074696d657374616d70000000000000000000000000000000000000000000608482015260a401610598565b61152d81602001516fffffffffffffffffffffffffffffffff166121bf565b6115df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604560248201527f4f7074696d69736d506f7274616c3a2070726f76656e2077697468647261776160448201527f6c2066696e616c697a6174696f6e20706572696f6420686173206e6f7420656c60648201527f6170736564000000000000000000000000000000000000000000000000000000608482015260a401610598565b60408181015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015611684573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116a89190614efc565b8251815191925014611762576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604960248201527f4f7074696d69736d506f7274616c3a206f757470757420726f6f742070726f7660448201527f656e206973206e6f74207468652073616d652061732063757272656e74206f7560648201527f7470757420726f6f740000000000000000000000000000000000000000000000608482015260a401610598565b61178181602001516fffffffffffffffffffffffffffffffff166121bf565b611833576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604360248201527f4f7074696d69736d506f7274616c3a206f75747075742070726f706f73616c2060448201527f66696e616c697a6174696f6e20706572696f6420686173206e6f7420656c617060648201527f7365640000000000000000000000000000000000000000000000000000000000608482015260a401610598565b60008381526033602052604090205460ff16156118d2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206160448201527f6c7265616479206265656e2066696e616c697a656400000000000000000000006064820152608401610598565b600083815260336020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055908601516032805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff00000000000000000000000000000000000000009092169190911790558501516080860151606087015160a088015161197493929190612262565b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405190915084907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b906119d990841515815260200190565b60405180910390a2801580156119ef5750326001145b15611a7c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4f7074696d69736d506f7274616c3a207769746864726177616c206661696c6560448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610598565b5050505050565b6000611a90826010615109565b61108290615208615139565b600054610100900460ff1615808015611abc5750600054600160ff909116105b80611ad65750303b158015611ad6575060005460ff166001145b611b62576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610598565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611bc057600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead179055603580548315157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116179055611c226122c0565b8015611c8557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b600154600090611cdb907801000000000000000000000000000000000000000000000000900467ffffffffffffffff1643615165565b90506000611ce76123a3565b90506000816020015160ff16826000015163ffffffff16611d0891906151ab565b90508215611e3f57600154600090611d3f908390700100000000000000000000000000000000900467ffffffffffffffff16615213565b90506000836040015160ff1683611d569190615287565b600154611d769084906fffffffffffffffffffffffffffffffff16615287565b611d8091906151ab565b600154909150600090611dd190611daa9084906fffffffffffffffffffffffffffffffff16615343565b866060015163ffffffff168760a001516fffffffffffffffffffffffffffffffff16612469565b90506001861115611e0057611dfd611daa82876040015160ff1660018a611df89190615165565b612488565b90505b6fffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000067ffffffffffffffff4316021760015550505b60018054869190601090611e72908490700100000000000000000000000000000000900467ffffffffffffffff16615139565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550816000015163ffffffff16600160000160109054906101000a900467ffffffffffffffff1667ffffffffffffffff161315611f55576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603e60248201527f5265736f757263654d65746572696e673a2063616e6e6f7420627579206d6f7260448201527f6520676173207468616e20617661696c61626c6520676173206c696d697400006064820152608401610598565b600154600090611f81906fffffffffffffffffffffffffffffffff1667ffffffffffffffff88166153b7565b90506000611f9348633b9aca006124dd565b611f9d90836153f4565b905060005a611fac9088615165565b905080821115611fc857611fc8611fc38284615165565b6124f4565b5050505050505050565b60008160000151826020015183604001518460600151604051602001612011949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b80516020808301516040808501516060860151608087015160a08801519351600097612011979096959101615408565b60008061206a86612522565b905061207881868686612554565b9695505050505050565b6060816000036120c557505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156120ef57806120d98161545f565b91506120e89050600a836153f4565b91506120c9565b60008167ffffffffffffffff81111561210a5761210a614a0f565b6040519080825280601f01601f191660200182016040528015612134576020820181803683370190505b5090505b84156121b757612149600183615165565b9150612156600a86615497565b6121619060306154ab565b60f81b818381518110612176576121766154c3565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506121b0600a866153f4565b9450612138565b949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663f4daa2916040518163ffffffff1660e01b8152600401602060405180830381865afa15801561222c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225091906150c1565b61225a90836154ab565b421192915050565b6000806000612272866000612584565b9050806122a8576308c379a06000526020805278185361666543616c6c3a204e6f7420656e6f756768206761736058526064601cfd5b600080855160208701888b5af1979650505050505050565b600054610100900460ff16612357576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610598565b60408051606081018252633b9aca00808252600060208301524367ffffffffffffffff169190920181905278010000000000000000000000000000000000000000000000000217600155565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663cc731b026040518163ffffffff1660e01b815260040160c060405180830381865afa158015612440573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124649190615517565b905090565b600061247e61247885856125a2565b836125b2565b90505b9392505050565b6000670de0b6b3a76400006124c96124a085836151ab565b6124b290670de0b6b3a7640000615213565b6124c485670de0b6b3a7640000615287565b6125c1565b6124d39086615287565b61247e91906151ab565b6000818310156124ed5781612481565b5090919050565b6000805a90505b825a6125079083615165565b101561251d576125168261545f565b91506124fb565b505050565b6060818051906020012060405160200161253e91815260200190565b6040516020818303038152906040529050919050565b600061257b846125658786866125f2565b8051602091820120825192909101919091201490565b95945050505050565b600080603f83619c4001026040850201603f5a021015949350505050565b6000818312156124ed5781612481565b60008183126124ed5781612481565b6000612481670de0b6b3a7640000836125d98661307a565b6125e39190615287565b6125ed91906151ab565b6132be565b6060600084511161265f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d65726b6c65547269653a20656d707479206b657900000000000000000000006044820152606401610598565b600061266a846134fd565b90506000612677866135ec565b905060008460405160200161268e91815260200190565b60405160208183030381529060405290506000805b8451811015612ff15760008582815181106126c0576126c06154c3565b60200260200101519050845183111561275b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201527f74616c206b6579206c656e6774680000000000000000000000000000000000006064820152608401610598565b8260000361281457805180516020918201206040516127a99261278392910190815260200190565b604051602081830303815290604052858051602091820120825192909101919091201490565b61280f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f7420686173680000006044820152606401610598565b61296b565b8051516020116128ca578051805160209182012060405161283e9261278392910190815260200190565b61280f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e60448201527f616c2068617368000000000000000000000000000000000000000000000000006064820152608401610598565b80518451602080870191909120825191909201201461296b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f6460448201527f65206861736800000000000000000000000000000000000000000000000000006064820152608401610598565b612977601060016154ab565b81602001515103612b585784518303612af05760006129b382602001516010815181106129a6576129a66154c3565b602002602001015161364f565b90506000815111612a46576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e63682900000000006064820152608401610598565b60018751612a549190615165565b8314612ae2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e6368290000000000006064820152608401610598565b965061248195505050505050565b6000858481518110612b0457612b046154c3565b602001015160f81c60f81b60f81c9050600082602001518260ff1681518110612b2f57612b2f6154c3565b60200260200101519050612b42816137af565b9550612b4f6001866154ab565b94505050612fde565b600281602001515103612f56576000612b70826137d4565b9050600081600081518110612b8757612b876154c3565b016020015160f81c90506000612b9e6002836155b6565b612ba99060026155d8565b90506000612bba848360ff166137f8565b90506000612bc88a896137f8565b90506000612bd6838361382e565b905080835114612c68576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b65790000000000006064820152608401610598565b60ff851660021480612c7d575060ff85166003145b15612e715780825114612d12576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e6465720000006064820152608401610598565b6000612d2e88602001516001815181106129a6576129a66154c3565b90506000815111612dc1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c65616629000000000000006064820152608401610598565b60018d51612dcf9190615165565b8914612e5d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c6561662900000000000000006064820152608401610598565b9c506124819b505050505050505050505050565b60ff85161580612e84575060ff85166001145b15612ec357612eb08760200151600181518110612ea357612ea36154c3565b60200260200101516137af565b9950612ebc818a6154ab565b9850612f4b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f6465207769746860448201527f20616e20756e6b6e6f776e2070726566697800000000000000000000000000006064820152608401610598565b505050505050612fde565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e70617273656160448201527f626c65206e6f64650000000000000000000000000000000000000000000000006064820152608401610598565b5080612fe98161545f565b9150506126a3565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c6560448201527f6d656e74730000000000000000000000000000000000000000000000000000006064820152608401610598565b60008082136130e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610598565b600060606130f2846138dd565b03609f8181039490941b90931c6c465772b2bbbb5f824b15207a3081018102606090811d6d0388eaa27412d5aca026815d636e018202811d6d0df99ac502031bf953eff472fdcc018202811d6d13cdffb29d51d99322bdff5f2211018202811d6d0a0f742023def783a307a986912e018202811d6d01920d8043ca89b5239253284e42018202811d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7883018302821d6d0139601a2efabe717e604cbb4894018302821d6d02247f7a7b6594320649aa03aba1018302821d7fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9018302821d7ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f01830290911d7fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832019091027ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000000105711340daa0d5f769dba1915cef59f0815a5506027d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b393909302929092017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d92915050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdb731c958f34d94c182136132ef57506000919050565b680755bf798b4a1bf1e58212613361576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4558505f4f564552464c4f5700000000000000000000000000000000000000006044820152606401610598565b6503782dace9d9604e83901b059150600060606bb17217f7d1cf79abc9e3b39884821b056b80000000000000000000000001901d6bb17217f7d1cf79abc9e3b39881029093037fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a550481018102606090811d6d0277594991cfc85f6e2461837cd9018202811d7fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c018202811d6db1bbb201f443cf962f1a1d3db4a5018202811d7ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb018202811d6e05180bb14799ab47a8a8cb2a527d57016d02d16720577bd19bf614176fe9ea6c10fe68e7fd37d0007b713f765084018402831d9081019084017ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e010290911d6e0587f503bb6ea29d25fcb7401964500190910279d835ebba824c98fb31b83b2ca45c000000000000000000000000010574029d9dc38563c32e5c2f6dc192ee70ef65f9978af30260c3939093039290921c92915050565b805160609060008167ffffffffffffffff81111561351d5761351d614a0f565b60405190808252806020026020018201604052801561356257816020015b604080518082019091526060808252602082015281526020019060019003908161353b5790505b50905060005b828110156135e457604051806040016040528086838151811061358d5761358d6154c3565b602002602001015181526020016135bc8784815181106135af576135af6154c3565b60200260200101516139b3565b8152508282815181106135d1576135d16154c3565b6020908102919091010152600101613568565b509392505050565b606080604051905082518060011b603f8101601f1916830160405280835250602084016020830160005b83811015613644578060011b82018184015160001a8060041c8253600f811660018301535050600101613616565b509295945050505050565b6060600080600061365f856139c6565b91945092509050600081600181111561367a5761367a6155fb565b14613707576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206279746573206973206e6f7420612064617461206974656d000000000000006064820152608401610598565b61371182846154ab565b8551146137a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f524c505265616465723a2062797465732076616c756520636f6e7461696e732060448201527f616e20696e76616c69642072656d61696e6465720000000000000000000000006064820152608401610598565b61257b85602001518484614433565b606060208260000151106137cb576137c68261364f565b611082565b611082826144d4565b60606110826137f383602001516000815181106129a6576129a66154c3565b6135ec565b6060825182106138175750604080516020810190915260008152611082565b61248183838486516138299190615165565b6144ea565b60008060008351855110613843578351613846565b84515b90505b80821080156138cd5750838281518110613865576138656154c3565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168583815181106138a4576138a46154c3565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b156135e457816001019150613849565b6000808211613948576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610598565b5060016fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110821b1791821c111790565b60606110826139c1836146c2565b6147ab565b600080600080846000015111613a84576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610598565b6020840151805160001a607f8111613aa957600060016000945094509450505061442c565b60b78111613cb7576000613abe608083615165565b905080876000015111613b79576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604e60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20737472696e67206c656e6774682060648201527f2873686f727420737472696e6729000000000000000000000000000000000000608482015260a401610598565b6001838101517fff00000000000000000000000000000000000000000000000000000000000000169082141580613bf257507f80000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821610155b613ca4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a20696e76616c6964207072656669782c2073696e676c60448201527f652062797465203c203078383020617265206e6f74207072656669786564202860648201527f73686f727420737472696e672900000000000000000000000000000000000000608482015260a401610598565b506001955093506000925061442c915050565b60bf8111614005576000613ccc60b783615165565b905080876000015111613d87576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f6620737472696e67206c656e60648201527f67746820286c6f6e6720737472696e6729000000000000000000000000000000608482015260a401610598565b60018301517fff00000000000000000000000000000000000000000000000000000000000000166000819003613e65576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e6720737472696e672900000000000000000000000000000000000000000000608482015260a401610598565b600184015160088302610100031c60378111613f29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f20737472696e6729000000000000000000000000000000000000000000000000608482015260a401610598565b613f3381846154ab565b895111613fe8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e6720737472696e67290000000000000000000000000000000000000000608482015260a401610598565b613ff38360016154ab565b975095506000945061442c9350505050565b60f781116140e657600061401a60c083615165565b9050808760000151116140d5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e206c697374206c656e67746820287360648201527f686f7274206c6973742900000000000000000000000000000000000000000000608482015260a401610598565b60019550935084925061442c915050565b60006140f360f783615165565b9050808760000151116141ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f66206c697374206c656e677460648201527f6820286c6f6e67206c6973742900000000000000000000000000000000000000608482015260a401610598565b60018301517fff0000000000000000000000000000000000000000000000000000000000000016600081900361428c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e67206c69737429000000000000000000000000000000000000000000000000608482015260a401610598565b600184015160088302610100031c60378111614350576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604660248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f206c697374290000000000000000000000000000000000000000000000000000608482015260a401610598565b61435a81846154ab565b89511161440f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e67206c6973742900000000000000000000000000000000000000000000608482015260a401610598565b61441a8360016154ab565b975095506001945061442c9350505050565b9193909250565b606060008267ffffffffffffffff81111561445057614450614a0f565b6040519080825280601f01601f19166020018201604052801561447a576020820181803683370190505b5090508260000361448c579050612481565b600061449885876154ab565b90506020820160005b858110156144b95782810151828201526020016144a1565b858111156144c8576000868301525b50919695505050505050565b6060611082826020015160008460000151614433565b60608182601f011015614559576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610598565b8282840110156145c5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610598565b81830184511015614632576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401610598565b60608215801561465157604051915060008252602082016040526146b9565b6040519150601f8416801560200281840101858101878315602002848b0101015b8183101561468a578051835260209283019201614672565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b6040805180820190915260008082526020820152600082511161478d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610598565b50604080518082019091528151815260209182019181019190915290565b606060008060006147bb856139c6565b9194509250905060018160018111156147d6576147d66155fb565b14614863576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206c697374206973206e6f742061206c697374206974656d00000000000000006064820152608401610598565b845161486f83856154ab565b146148fc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f524c505265616465723a206c697374206974656d2068617320616e20696e766160448201527f6c696420646174612072656d61696e64657200000000000000000000000000006064820152608401610598565b6040805160208082526104208201909252600091816020015b60408051808201909152600080825260208201528152602001906001900390816149155790505090506000845b8751811015614a03576000806149886040518060400160405280858d6000015161496c9190615165565b8152602001858d6020015161498191906154ab565b90526139c6565b5091509150604051806040016040528083836149a491906154ab565b8152602001848c602001516149b991906154ab565b8152508585815181106149ce576149ce6154c3565b60209081029190910101526149e46001856154ab565b93506149f081836154ab565b6149fa90846154ab565b92505050614942565b50815295945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614a8557614a85614a0f565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114614ab157600080fd5b919050565b600082601f830112614ac757600080fd5b813567ffffffffffffffff811115614ae157614ae1614a0f565b614b1260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614a3e565b818152846020838601011115614b2757600080fd5b816020850160208301376000918101602001919091529392505050565b600060c08284031215614b5657600080fd5b60405160c0810167ffffffffffffffff8282108183111715614b7a57614b7a614a0f565b8160405282935084358352614b9160208601614a8d565b6020840152614ba260408601614a8d565b6040840152606085013560608401526080850135608084015260a0850135915080821115614bcf57600080fd5b50614bdc85828601614ab6565b60a0830152505092915050565b600080600080600085870360e0811215614c0257600080fd5b863567ffffffffffffffff80821115614c1a57600080fd5b614c268a838b01614b44565b97506020890135965060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc084011215614c5f57600080fd5b60408901955060c0890135925080831115614c7957600080fd5b828901925089601f840112614c8d57600080fd5b8235915080821115614c9e57600080fd5b508860208260051b8401011115614cb457600080fd5b959894975092955050506020019190565b60005b83811015614ce0578181015183820152602001614cc8565b83811115614cef576000848401525b50505050565b60008151808452614d0d816020860160208601614cc5565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006124816020830184614cf5565b600060208284031215614d6457600080fd5b5035919050565b600060208284031215614d7d57600080fd5b813567ffffffffffffffff811115614d9457600080fd5b6121b784828501614b44565b803567ffffffffffffffff81168114614ab157600080fd5b600060208284031215614dca57600080fd5b61248182614da0565b80358015158114614ab157600080fd5b600060208284031215614df557600080fd5b61248182614dd3565b600080600080600060a08688031215614e1657600080fd5b614e1f86614a8d565b945060208601359350614e3460408701614da0565b9250614e4260608701614dd3565b9150608086013567ffffffffffffffff811115614e5e57600080fd5b614e6a88828901614ab6565b9150509295509295909350565b8581528460208201527fffffffffffffffff0000000000000000000000000000000000000000000000008460c01b16604082015282151560f81b604882015260008251614ecb816049850160208701614cc5565b919091016049019695505050505050565b80516fffffffffffffffffffffffffffffffff81168114614ab157600080fd5b600060608284031215614f0e57600080fd5b6040516060810181811067ffffffffffffffff82111715614f3157614f31614a0f565b60405282518152614f4460208401614edc565b6020820152614f5560408401614edc565b60408201529392505050565b600060808284031215614f7357600080fd5b6040516080810181811067ffffffffffffffff82111715614f9657614f96614a0f565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b600067ffffffffffffffff80841115614fe257614fe2614a0f565b8360051b6020614ff3818301614a3e565b86815291850191818101903684111561500b57600080fd5b865b8481101561503f578035868111156150255760008081fd5b61503136828b01614ab6565b84525091830191830161500d565b50979650505050505050565b6000845161505d818460208901614cc5565b80830190507f2e000000000000000000000000000000000000000000000000000000000000008082528551615099816001850160208a01614cc5565b600192019182015283516150b4816002840160208801614cc5565b0160020195945050505050565b6000602082840312156150d357600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600067ffffffffffffffff80831681851681830481118215151615615130576151306150da565b02949350505050565b600067ffffffffffffffff80831681851680830382111561515c5761515c6150da565b01949350505050565b600082821015615177576151776150da565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826151ba576151ba61517c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561520e5761520e6150da565b500590565b6000808312837f80000000000000000000000000000000000000000000000000000000000000000183128115161561524d5761524d6150da565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018313811615615281576152816150da565b50500390565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000841360008413858304851182821616156152c8576152c86150da565b7f80000000000000000000000000000000000000000000000000000000000000006000871286820588128184161615615303576153036150da565b6000871292508782058712848416161561531f5761531f6150da565b87850587128184161615615335576153356150da565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0384138115161561537d5761537d6150da565b827f80000000000000000000000000000000000000000000000000000000000000000384128116156153b1576153b16150da565b50500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156153ef576153ef6150da565b500290565b6000826154035761540361517c565b500490565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a083015261545360c0830184614cf5565b98975050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615490576154906150da565b5060010190565b6000826154a6576154a661517c565b500690565b600082198211156154be576154be6150da565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b805163ffffffff81168114614ab157600080fd5b805160ff81168114614ab157600080fd5b600060c0828403121561552957600080fd5b60405160c0810181811067ffffffffffffffff8211171561554c5761554c614a0f565b604052615558836154f2565b815261556660208401615506565b602082015261557760408401615506565b6040820152615588606084016154f2565b6060820152615599608084016154f2565b60808201526155aa60a08401614edc565b60a08201529392505050565b600060ff8316806155c9576155c961517c565b8060ff84160691505092915050565b600060ff821660ff8416808210156155f2576155f26150da565b90039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea164736f6c634300080f000a"
func init() { func init() {
if err := json.Unmarshal([]byte(OptimismPortalStorageLayoutJSON), OptimismPortalStorageLayout); err != nil { if err := json.Unmarshal([]byte(OptimismPortalStorageLayoutJSON), OptimismPortalStorageLayout); err != nil {
......
...@@ -2,15 +2,20 @@ package bindings ...@@ -2,15 +2,20 @@ package bindings
import ( import (
"fmt" "fmt"
"strings"
"github.com/ethereum-optimism/optimism/op-bindings/solc" "github.com/ethereum-optimism/optimism/op-bindings/solc"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
// layouts respresents the set of storage layouts. It is populated in an init function.
var layouts = make(map[string]*solc.StorageLayout) var layouts = make(map[string]*solc.StorageLayout)
// deployedBytecodes represents the set of deployed bytecodes. It is populated
// in an init function.
var deployedBytecodes = make(map[string]string) var deployedBytecodes = make(map[string]string)
// GetStorageLayout returns the storage layout of a contract by name.
func GetStorageLayout(name string) (*solc.StorageLayout, error) { func GetStorageLayout(name string) (*solc.StorageLayout, error) {
layout := layouts[name] layout := layouts[name]
if layout == nil { if layout == nil {
...@@ -19,11 +24,36 @@ func GetStorageLayout(name string) (*solc.StorageLayout, error) { ...@@ -19,11 +24,36 @@ func GetStorageLayout(name string) (*solc.StorageLayout, error) {
return layout, nil return layout, nil
} }
// GetDeployedBytecode returns the deployed bytecode of a contract by name.
func GetDeployedBytecode(name string) ([]byte, error) { func GetDeployedBytecode(name string) ([]byte, error) {
bc := deployedBytecodes[name] bc := deployedBytecodes[name]
if bc == "" { if bc == "" {
return nil, fmt.Errorf("%s: deployed bytecode not found", name) return nil, fmt.Errorf("%s: deployed bytecode not found", name)
} }
if !isHex(bc) {
return nil, fmt.Errorf("%s: invalid deployed bytecode", name)
}
return common.FromHex(bc), nil return common.FromHex(bc), nil
} }
// isHexCharacter returns bool of c being a valid hexadecimal.
func isHexCharacter(c byte) bool {
return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
}
// isHex validates whether each byte is valid hexadecimal string.
func isHex(str string) bool {
if len(str)%2 != 0 {
return false
}
str = strings.TrimPrefix(str, "0x")
for _, c := range []byte(str) {
if !isHexCharacter(c) {
return false
}
}
return true
}
...@@ -5,7 +5,6 @@ import "github.com/ethereum/go-ethereum/common" ...@@ -5,7 +5,6 @@ import "github.com/ethereum/go-ethereum/common"
const ( const (
L2ToL1MessagePasser = "0x4200000000000000000000000000000000000016" L2ToL1MessagePasser = "0x4200000000000000000000000000000000000016"
DeployerWhitelist = "0x4200000000000000000000000000000000000002" DeployerWhitelist = "0x4200000000000000000000000000000000000002"
LegacyERC20ETH = "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
WETH9 = "0x4200000000000000000000000000000000000006" WETH9 = "0x4200000000000000000000000000000000000006"
L2CrossDomainMessenger = "0x4200000000000000000000000000000000000007" L2CrossDomainMessenger = "0x4200000000000000000000000000000000000007"
L2StandardBridge = "0x4200000000000000000000000000000000000010" L2StandardBridge = "0x4200000000000000000000000000000000000010"
...@@ -26,7 +25,6 @@ const ( ...@@ -26,7 +25,6 @@ const (
var ( var (
L2ToL1MessagePasserAddr = common.HexToAddress(L2ToL1MessagePasser) L2ToL1MessagePasserAddr = common.HexToAddress(L2ToL1MessagePasser)
DeployerWhitelistAddr = common.HexToAddress(DeployerWhitelist) DeployerWhitelistAddr = common.HexToAddress(DeployerWhitelist)
LegacyERC20ETHAddr = common.HexToAddress(LegacyERC20ETH)
WETH9Addr = common.HexToAddress(WETH9) WETH9Addr = common.HexToAddress(WETH9)
L2CrossDomainMessengerAddr = common.HexToAddress(L2CrossDomainMessenger) L2CrossDomainMessengerAddr = common.HexToAddress(L2CrossDomainMessenger)
L2StandardBridgeAddr = common.HexToAddress(L2StandardBridge) L2StandardBridgeAddr = common.HexToAddress(L2StandardBridge)
...@@ -46,10 +44,20 @@ var ( ...@@ -46,10 +44,20 @@ var (
Predeploys = make(map[string]*common.Address) Predeploys = make(map[string]*common.Address)
) )
// IsProxied returns true for predeploys that will sit behind a proxy contract
func IsProxied(predeployAddr common.Address) bool {
switch predeployAddr {
case WETH9Addr:
case GovernanceTokenAddr:
default:
return true
}
return false
}
func init() { func init() {
Predeploys["L2ToL1MessagePasser"] = &L2ToL1MessagePasserAddr Predeploys["L2ToL1MessagePasser"] = &L2ToL1MessagePasserAddr
Predeploys["DeployerWhitelist"] = &DeployerWhitelistAddr Predeploys["DeployerWhitelist"] = &DeployerWhitelistAddr
Predeploys["LegacyERC20ETH"] = &LegacyERC20ETHAddr
Predeploys["WETH9"] = &WETH9Addr Predeploys["WETH9"] = &WETH9Addr
Predeploys["L2CrossDomainMessenger"] = &L2CrossDomainMessengerAddr Predeploys["L2CrossDomainMessenger"] = &L2CrossDomainMessengerAddr
Predeploys["L2StandardBridge"] = &L2StandardBridgeAddr Predeploys["L2StandardBridge"] = &L2StandardBridgeAddr
......
...@@ -52,7 +52,7 @@ func Main(cliCtx *cli.Context) error { ...@@ -52,7 +52,7 @@ func Main(cliCtx *cli.Context) error {
return err return err
} }
p2pConfig, err := p2pcli.NewConfig(cliCtx, config.BlockTime) p2pConfig, err := p2pcli.NewConfig(cliCtx, config)
if err != nil { if err != nil {
return fmt.Errorf("failed to load p2p config: %w", err) return fmt.Errorf("failed to load p2p config: %w", err)
} }
......
...@@ -113,6 +113,8 @@ type DeployConfig struct { ...@@ -113,6 +113,8 @@ type DeployConfig struct {
GasPriceOracleOverhead uint64 `json:"gasPriceOracleOverhead"` GasPriceOracleOverhead uint64 `json:"gasPriceOracleOverhead"`
// The initial value of the gas scalar // The initial value of the gas scalar
GasPriceOracleScalar uint64 `json:"gasPriceOracleScalar"` GasPriceOracleScalar uint64 `json:"gasPriceOracleScalar"`
// Whether or not include governance token predeploy
EnableGovernance bool `json:"enableGovernance"`
// The ERC20 symbol of the GovernanceToken // The ERC20 symbol of the GovernanceToken
GovernanceTokenSymbol string `json:"governanceTokenSymbol"` GovernanceTokenSymbol string `json:"governanceTokenSymbol"`
// The ERC20 name of the GovernanceToken // The ERC20 name of the GovernanceToken
...@@ -240,14 +242,16 @@ func (d *DeployConfig) Check() error { ...@@ -240,14 +242,16 @@ func (d *DeployConfig) Check() error {
if d.L2GenesisBlockBaseFeePerGas == nil { if d.L2GenesisBlockBaseFeePerGas == nil {
return fmt.Errorf("%w: L2 genesis block base fee per gas cannot be nil", ErrInvalidDeployConfig) return fmt.Errorf("%w: L2 genesis block base fee per gas cannot be nil", ErrInvalidDeployConfig)
} }
if d.GovernanceTokenName == "" { if d.EnableGovernance {
return fmt.Errorf("%w: GovernanceToken.name cannot be empty", ErrInvalidDeployConfig) if d.GovernanceTokenName == "" {
} return fmt.Errorf("%w: GovernanceToken.name cannot be empty", ErrInvalidDeployConfig)
if d.GovernanceTokenSymbol == "" { }
return fmt.Errorf("%w: GovernanceToken.symbol cannot be empty", ErrInvalidDeployConfig) if d.GovernanceTokenSymbol == "" {
} return fmt.Errorf("%w: GovernanceToken.symbol cannot be empty", ErrInvalidDeployConfig)
if d.GovernanceTokenOwner == (common.Address{}) { }
return fmt.Errorf("%w: GovernanceToken owner cannot be address(0)", ErrInvalidDeployConfig) if d.GovernanceTokenOwner == (common.Address{}) {
return fmt.Errorf("%w: GovernanceToken owner cannot be address(0)", ErrInvalidDeployConfig)
}
} }
return nil return nil
} }
...@@ -492,10 +496,12 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage ...@@ -492,10 +496,12 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage
"symbol": "WETH", "symbol": "WETH",
"decimals": 18, "decimals": 18,
} }
storage["GovernanceToken"] = state.StorageValues{ if config.EnableGovernance {
"_name": config.GovernanceTokenName, storage["GovernanceToken"] = state.StorageValues{
"_symbol": config.GovernanceTokenSymbol, "_name": config.GovernanceTokenName,
"_owner": config.GovernanceTokenOwner, "_symbol": config.GovernanceTokenSymbol,
"_owner": config.GovernanceTokenOwner,
}
} }
storage["ProxyAdmin"] = state.StorageValues{ storage["ProxyAdmin"] = state.StorageValues{
"_owner": config.ProxyAdminOwner, "_owner": config.ProxyAdminOwner,
......
package genesis package genesis
import ( import (
"github.com/ethereum-optimism/optimism/op-chain-ops/state" "fmt"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/immutables"
"github.com/ethereum-optimism/optimism/op-chain-ops/state"
) )
// BuildL2DeveloperGenesis will build the developer Optimism Genesis // BuildL2DeveloperGenesis will build the L2 genesis block.
// Block. Suitable for devnets. func BuildL2Genesis(config *DeployConfig, l1StartBlock *types.Block) (*core.Genesis, error) {
func BuildL2DeveloperGenesis(config *DeployConfig, l1StartBlock *types.Block) (*core.Genesis, error) {
genspec, err := NewL2Genesis(config, l1StartBlock) genspec, err := NewL2Genesis(config, l1StartBlock)
if err != nil { if err != nil {
return nil, err return nil, err
} }
db := state.NewMemoryStateDB(genspec) db := state.NewMemoryStateDB(genspec)
if config.FundDevAccounts { if config.FundDevAccounts {
FundDevAccounts(db) FundDevAccounts(db)
SetPrecompileBalances(db)
} }
SetPrecompileBalances(db)
storage, err := NewL2StorageConfig(config, l1StartBlock) storage, err := NewL2StorageConfig(config, l1StartBlock)
if err != nil { if err != nil {
...@@ -32,16 +35,42 @@ func BuildL2DeveloperGenesis(config *DeployConfig, l1StartBlock *types.Block) (* ...@@ -32,16 +35,42 @@ func BuildL2DeveloperGenesis(config *DeployConfig, l1StartBlock *types.Block) (*
return nil, err return nil, err
} }
if err := SetL2Proxies(db); err != nil { // Set up the proxies
err = setProxies(db, predeploys.ProxyAdminAddr, bigL2PredeployNamespace, 2048)
if err != nil {
return nil, err return nil, err
} }
if err := SetImplementations(db, storage, immutable); err != nil { // Set up the implementations
deployResults, err := immutables.BuildOptimism(immutable)
if err != nil {
return nil, err return nil, err
} }
for name, predeploy := range predeploys.Predeploys {
if err := SetDevOnlyL2Implementations(db, storage, immutable); err != nil { addr := *predeploy
return nil, err if addr == predeploys.GovernanceTokenAddr && !config.EnableGovernance {
// there is no governance token configured, so skip the governance token predeploy
log.Warn("Governance is not enabled, skipping governance token predeploy.")
continue
}
codeAddr := addr
if predeploys.IsProxied(addr) {
codeAddr, err = AddressToCodeNamespace(addr)
if err != nil {
return nil, fmt.Errorf("error converting to code namespace: %w", err)
}
db.CreateAccount(codeAddr)
db.SetState(addr, ImplementationSlot, codeAddr.Hash())
} else {
db.DeleteState(addr, AdminSlot)
}
if err := setupPredeploy(db, deployResults, storage, name, addr, codeAddr); err != nil {
return nil, err
}
code := db.GetCode(codeAddr)
if len(code) == 0 {
return nil, fmt.Errorf("code not set for %s", name)
}
} }
return db.Genesis(), nil return db.Genesis(), nil
......
...@@ -8,17 +8,16 @@ import ( ...@@ -8,17 +8,16 @@ import (
"os" "os"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/stretchr/testify/require"
) )
var writeFile bool var writeFile bool
...@@ -29,10 +28,8 @@ func init() { ...@@ -29,10 +28,8 @@ func init() {
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
func TestBuildL2DeveloperGenesis(t *testing.T) { // Tests the BuildL2MainnetGenesis factory with the provided config.
config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json") func testBuildL2Genesis(t *testing.T, config *genesis.DeployConfig) *core.Genesis {
require.Nil(t, err)
backend := backends.NewSimulatedBackend( backend := backends.NewSimulatedBackend(
core.GenesisAlloc{ core.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)}, crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
...@@ -42,55 +39,65 @@ func TestBuildL2DeveloperGenesis(t *testing.T) { ...@@ -42,55 +39,65 @@ func TestBuildL2DeveloperGenesis(t *testing.T) {
block, err := backend.BlockByNumber(context.Background(), common.Big0) block, err := backend.BlockByNumber(context.Background(), common.Big0)
require.NoError(t, err) require.NoError(t, err)
gen, err := genesis.BuildL2DeveloperGenesis(config, block) gen, err := genesis.BuildL2Genesis(config, block)
require.Nil(t, err) require.Nil(t, err)
require.NotNil(t, gen) require.NotNil(t, gen)
depB, err := bindings.GetDeployedBytecode("Proxy") proxyBytecode, err := bindings.GetDeployedBytecode("Proxy")
require.NoError(t, err) require.NoError(t, err)
for name, address := range predeploys.Predeploys { for name, predeploy := range predeploys.Predeploys {
addr := *address addr := *predeploy
account, ok := gen.Alloc[addr] account, ok := gen.Alloc[addr]
require.Equal(t, ok, true) require.Equal(t, true, ok, name)
require.Greater(t, len(account.Code), 0) require.Greater(t, len(account.Code), 0)
if name == "GovernanceToken" || name == "LegacyERC20ETH" || name == "ProxyAdmin" || name == "WETH9" {
continue
}
adminSlot, ok := account.Storage[genesis.AdminSlot] adminSlot, ok := account.Storage[genesis.AdminSlot]
require.Equal(t, ok, true) isProxy := predeploys.IsProxied(addr) ||
require.Equal(t, adminSlot, predeploys.ProxyAdminAddr.Hash()) (!config.EnableGovernance && addr == predeploys.GovernanceTokenAddr)
require.Equal(t, account.Code, depB) if isProxy {
require.Equal(t, true, ok, name)
require.Equal(t, predeploys.ProxyAdminAddr.Hash(), adminSlot)
require.Equal(t, proxyBytecode, account.Code)
} else {
require.Equal(t, false, ok, name)
require.NotEqual(t, proxyBytecode, account.Code, name)
}
} }
require.Equal(t, 2343, len(gen.Alloc))
if writeFile { if writeFile {
file, _ := json.MarshalIndent(gen, "", " ") file, _ := json.MarshalIndent(gen, "", " ")
_ = os.WriteFile("genesis.json", file, 0644) _ = os.WriteFile("genesis.json", file, 0644)
} }
return gen
} }
func TestBuildL2DeveloperGenesisDevAccountsFunding(t *testing.T) { func TestBuildL2DeveloperGenesis(t *testing.T) {
config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json") config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json")
require.Nil(t, err) require.Nil(t, err)
config.FundDevAccounts = false config.EnableGovernance = false
config.FundDevAccounts = true
err = config.InitDeveloperDeployedAddresses() err = config.InitDeveloperDeployedAddresses()
require.NoError(t, err) require.NoError(t, err)
gen := testBuildL2Genesis(t, config)
require.Equal(t, 2342, len(gen.Alloc))
}
backend := backends.NewSimulatedBackend( func TestBuildL2MainnetGenesis(t *testing.T) {
core.GenesisAlloc{ config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json")
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)}, require.Nil(t, err)
}, config.EnableGovernance = true
15000000, config.FundDevAccounts = false
) gen := testBuildL2Genesis(t, config)
block, err := backend.BlockByNumber(context.Background(), common.Big0) require.Equal(t, 2064, len(gen.Alloc))
require.NoError(t, err) }
gen, err := genesis.BuildL2DeveloperGenesis(config, block) func TestBuildL2MainnetNoGovernanceGenesis(t *testing.T) {
require.NoError(t, err) config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json")
require.Equal(t, 2321, len(gen.Alloc)) require.Nil(t, err)
config.EnableGovernance = false
config.FundDevAccounts = false
gen := testBuildL2Genesis(t, config)
require.Equal(t, 2064, len(gen.Alloc))
} }
...@@ -2,12 +2,10 @@ package genesis ...@@ -2,12 +2,10 @@ package genesis
import ( import (
"errors" "errors"
"fmt"
"math/big" "math/big"
"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-chain-ops/immutables" "github.com/ethereum-optimism/optimism/op-chain-ops/immutables"
"github.com/ethereum-optimism/optimism/op-chain-ops/state" "github.com/ethereum-optimism/optimism/op-chain-ops/state"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -15,19 +13,6 @@ import ( ...@@ -15,19 +13,6 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
// UntouchableCodeHashes contains code hashes of all the contracts
// that should not be touched by the migration process.
type ChainHashMap map[uint64]common.Hash
var (
// UntouchablePredeploys are addresses in the predeploy namespace
// that should not be touched by the migration process.
UntouchablePredeploys = map[common.Address]bool{
predeploys.GovernanceTokenAddr: true,
predeploys.WETH9Addr: true,
}
)
// 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 {
...@@ -36,14 +21,6 @@ func FundDevAccounts(db vm.StateDB) { ...@@ -36,14 +21,6 @@ func FundDevAccounts(db vm.StateDB) {
} }
} }
// SetL2Proxies will set each of the proxies in the state. It requires
// a Proxy and ProxyAdmin deployment present so that the Proxy bytecode
// can be set in state and the ProxyAdmin can be set as the admin of the
// Proxy.
func SetL2Proxies(db vm.StateDB) error {
return setProxies(db, predeploys.ProxyAdminAddr, bigL2PredeployNamespace, 2048)
}
// SetL1Proxies will set each of the proxies in the state. It requires // SetL1Proxies will set each of the proxies in the state. It requires
// a Proxy and ProxyAdmin deployment present so that the Proxy bytecode // a Proxy and ProxyAdmin deployment present so that the Proxy bytecode
// can be set in state and the ProxyAdmin can be set as the admin of the // can be set in state and the ProxyAdmin can be set as the admin of the
...@@ -65,11 +42,6 @@ func setProxies(db vm.StateDB, proxyAdminAddr common.Address, namespace *big.Int ...@@ -65,11 +42,6 @@ func setProxies(db vm.StateDB, proxyAdminAddr common.Address, namespace *big.Int
bigAddr := new(big.Int).Or(namespace, new(big.Int).SetUint64(i)) bigAddr := new(big.Int).Or(namespace, new(big.Int).SetUint64(i))
addr := common.BigToAddress(bigAddr) addr := common.BigToAddress(bigAddr)
if UntouchablePredeploys[addr] {
log.Info("Skipping setting proxy", "address", addr)
continue
}
if !db.Exist(addr) { if !db.Exist(addr) {
db.CreateAccount(addr) db.CreateAccount(addr)
} }
...@@ -82,87 +54,6 @@ func setProxies(db vm.StateDB, proxyAdminAddr common.Address, namespace *big.Int ...@@ -82,87 +54,6 @@ func setProxies(db vm.StateDB, proxyAdminAddr common.Address, namespace *big.Int
return nil return nil
} }
func SetLegacyETH(db vm.StateDB, storage state.StorageConfig, immutable immutables.ImmutableConfig) error {
deployResults, err := immutables.BuildOptimism(immutable)
if err != nil {
return err
}
return setupPredeploy(db, deployResults, storage, "LegacyERC20ETH", predeploys.LegacyERC20ETHAddr, predeploys.LegacyERC20ETHAddr)
}
// SetImplementations will set the implementations of the contracts in the state
// and configure the proxies to point to the implementations. It also sets
// the appropriate storage values for each contract at the proxy address.
func SetImplementations(db vm.StateDB, storage state.StorageConfig, immutable immutables.ImmutableConfig) error {
deployResults, err := immutables.BuildOptimism(immutable)
if err != nil {
return err
}
for name, address := range predeploys.Predeploys {
if UntouchablePredeploys[*address] {
continue
}
if *address == predeploys.LegacyERC20ETHAddr {
continue
}
codeAddr, err := AddressToCodeNamespace(*address)
if err != nil {
return fmt.Errorf("error converting to code namespace: %w", err)
}
if !db.Exist(codeAddr) {
db.CreateAccount(codeAddr)
}
db.SetState(*address, ImplementationSlot, codeAddr.Hash())
if err := setupPredeploy(db, deployResults, storage, name, *address, codeAddr); err != nil {
return err
}
code := db.GetCode(codeAddr)
if len(code) == 0 {
return fmt.Errorf("code not set for %s", name)
}
}
return nil
}
func SetDevOnlyL2Implementations(db vm.StateDB, storage state.StorageConfig, immutable immutables.ImmutableConfig) error {
deployResults, err := immutables.BuildOptimism(immutable)
if err != nil {
return err
}
for name, address := range predeploys.Predeploys {
if !UntouchablePredeploys[*address] {
continue
}
db.CreateAccount(*address)
if err := setupPredeploy(db, deployResults, storage, name, *address, *address); err != nil {
return err
}
code := db.GetCode(*address)
if len(code) == 0 {
return fmt.Errorf("code not set for %s", name)
}
}
db.CreateAccount(predeploys.LegacyERC20ETHAddr)
if err := setupPredeploy(db, deployResults, storage, "LegacyERC20ETH", predeploys.LegacyERC20ETHAddr, predeploys.LegacyERC20ETHAddr); err != nil {
return fmt.Errorf("error setting up legacy eth: %w", err)
}
return nil
}
// SetPrecompileBalances will set a single wei at each precompile address. // SetPrecompileBalances will set a single wei at each precompile address.
// 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.
......
...@@ -35,5 +35,10 @@ ...@@ -35,5 +35,10 @@
"l1CrossDomainMessengerProxy": "0xff000000000000000000000000000000000000dd", "l1CrossDomainMessengerProxy": "0xff000000000000000000000000000000000000dd",
"deploymentWaitConfirmations": 1, "deploymentWaitConfirmations": 1,
"fundDevAccounts": true "fundDevAccounts": true,
"enableGovernance": true,
"governanceTokenSymbol": "OP",
"governanceTokenName": "Optimism",
"governanceTokenOwner": "0x0000000000000000000000000000000000000333"
} }
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
"proxyAdminOwner": "0x0000000000000000000000000000000000000222", "proxyAdminOwner": "0x0000000000000000000000000000000000000222",
"gasPriceOracleOverhead": 2100, "gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000, "gasPriceOracleScalar": 1000000,
"enableGovernance": true,
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenName": "Optimism", "governanceTokenName": "Optimism",
"governanceTokenOwner": "0x0000000000000000000000000000000000000333", "governanceTokenOwner": "0x0000000000000000000000000000000000000333",
......
...@@ -226,6 +226,15 @@ func (db *MemoryStateDB) SetState(addr common.Address, key, value common.Hash) { ...@@ -226,6 +226,15 @@ func (db *MemoryStateDB) SetState(addr common.Address, key, value common.Hash) {
db.genesis.Alloc[addr] = account db.genesis.Alloc[addr] = account
} }
func (db *MemoryStateDB) DeleteState(addr common.Address, key common.Hash) {
account, ok := db.genesis.Alloc[addr]
if !ok {
panic(fmt.Sprintf("%s not in state", addr))
}
delete(account.Storage, key)
db.genesis.Alloc[addr] = account
}
func (db *MemoryStateDB) Suicide(common.Address) bool { func (db *MemoryStateDB) Suicide(common.Address) bool {
panic("Suicide unimplemented") panic("Suicide unimplemented")
} }
......
...@@ -185,7 +185,7 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) * ...@@ -185,7 +185,7 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) *
l1Block := l1Genesis.ToBlock() l1Block := l1Genesis.ToBlock()
l2Genesis, err := genesis.BuildL2DeveloperGenesis(deployConf, l1Block) l2Genesis, err := genesis.BuildL2Genesis(deployConf, l1Block)
require.NoError(t, err, "failed to create l2 genesis") require.NoError(t, err, "failed to create l2 genesis")
if alloc.PrefundTestUsers { if alloc.PrefundTestUsers {
for _, addr := range deployParams.Addresses.All() { for _, addr := range deployParams.Addresses.All() {
......
...@@ -53,7 +53,7 @@ func NewOpGeth(t *testing.T, ctx context.Context, cfg *SystemConfig) (*OpGeth, e ...@@ -53,7 +53,7 @@ func NewOpGeth(t *testing.T, ctx context.Context, cfg *SystemConfig) (*OpGeth, e
require.Nil(t, err) require.Nil(t, err)
l1Block := l1Genesis.ToBlock() l1Block := l1Genesis.ToBlock()
l2Genesis, err := genesis.BuildL2DeveloperGenesis(cfg.DeployConfig, l1Block) l2Genesis, err := genesis.BuildL2Genesis(cfg.DeployConfig, l1Block)
require.Nil(t, err) require.Nil(t, err)
l2GenesisBlock := l2Genesis.ToBlock() l2GenesisBlock := l2Genesis.ToBlock()
......
...@@ -359,7 +359,7 @@ func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) { ...@@ -359,7 +359,7 @@ func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) {
} }
l1Block := l1Genesis.ToBlock() l1Block := l1Genesis.ToBlock()
l2Genesis, err := genesis.BuildL2DeveloperGenesis(cfg.DeployConfig, l1Block) l2Genesis, err := genesis.BuildL2Genesis(cfg.DeployConfig, l1Block)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -813,10 +813,10 @@ func TestSystemDenseTopology(t *testing.T) { ...@@ -813,10 +813,10 @@ func TestSystemDenseTopology(t *testing.T) {
// Set peer scoring for each node, but without banning // Set peer scoring for each node, but without banning
for _, node := range cfg.Nodes { for _, node := range cfg.Nodes {
params, err := p2p.GetPeerScoreParams("light", 2) params, err := p2p.GetScoringParams("light", &node.Rollup)
require.NoError(t, err) require.NoError(t, err)
node.P2P = &p2p.Config{ node.P2P = &p2p.Config{
PeerScoring: &params, ScoringParams: params,
BanningEnabled: false, BanningEnabled: false,
} }
} }
......
...@@ -61,7 +61,7 @@ var Subcommands = cli.Commands{ ...@@ -61,7 +61,7 @@ var Subcommands = cli.Commands{
} }
l1StartBlock := l1Genesis.ToBlock() l1StartBlock := l1Genesis.ToBlock()
l2Genesis, err := genesis.BuildL2DeveloperGenesis(config, l1StartBlock) l2Genesis, err := genesis.BuildL2Genesis(config, l1StartBlock)
if err != nil { if err != nil {
return err return err
} }
...@@ -144,7 +144,7 @@ var Subcommands = cli.Commands{ ...@@ -144,7 +144,7 @@ var Subcommands = cli.Commands{
} }
// Build the developer L2 genesis block // Build the developer L2 genesis block
l2Genesis, err := genesis.BuildL2DeveloperGenesis(config, l1StartBlock) l2Genesis, err := genesis.BuildL2Genesis(config, l1StartBlock)
if err != nil { if err != nil {
return fmt.Errorf("error creating l2 developer genesis: %w", err) return fmt.Errorf("error creating l2 developer genesis: %w", err)
} }
......
package flags package flags
import ( import (
"fmt"
"time" "time"
"github.com/urfave/cli" "github.com/urfave/cli"
...@@ -25,15 +26,18 @@ var ( ...@@ -25,15 +26,18 @@ var (
Required: false, Required: false,
EnvVar: p2pEnv("NO_DISCOVERY"), EnvVar: p2pEnv("NO_DISCOVERY"),
} }
PeerScoring = cli.StringFlag{ Scoring = cli.StringFlag{
Name: "p2p.scoring.peers", Name: "p2p.scoring",
Usage: "Sets the peer scoring strategy for the P2P stack. " + Usage: "Sets the peer scoring strategy for the P2P stack. Can be one of: none or light.",
"Can be one of: none or light." +
"Custom scoring strategies can be defined in the config file.",
Required: false, Required: false,
Value: "none",
EnvVar: p2pEnv("PEER_SCORING"), EnvVar: p2pEnv("PEER_SCORING"),
} }
PeerScoring = cli.StringFlag{
Name: "p2p.scoring.peers",
Usage: fmt.Sprintf("Deprecated: Use %v instead", Scoring.Name),
Required: false,
Hidden: true,
}
PeerScoreBands = cli.StringFlag{ PeerScoreBands = cli.StringFlag{
Name: "p2p.score.bands", Name: "p2p.score.bands",
Usage: "Deprecated. This option is ignored and is only present for backwards compatibility.", Usage: "Deprecated. This option is ignored and is only present for backwards compatibility.",
...@@ -65,13 +69,10 @@ var ( ...@@ -65,13 +69,10 @@ var (
} }
TopicScoring = cli.StringFlag{ TopicScoring = cli.StringFlag{
Name: "p2p.scoring.topics", Name: "p2p.scoring.topics",
Usage: "Sets the topic scoring strategy. " + Usage: fmt.Sprintf("Deprecated: Use %v instead", Scoring.Name),
"Can be one of: none or light." +
"Custom scoring strategies can be defined in the config file.",
Required: false, Required: false,
Value: "none", Hidden: true,
EnvVar: p2pEnv("TOPIC_SCORING"),
} }
P2PPrivPath = cli.StringFlag{ P2PPrivPath = cli.StringFlag{
Name: "p2p.priv.path", Name: "p2p.priv.path",
...@@ -303,6 +304,7 @@ var p2pFlags = []cli.Flag{ ...@@ -303,6 +304,7 @@ var p2pFlags = []cli.Flag{
NoDiscovery, NoDiscovery,
P2PPrivPath, P2PPrivPath,
P2PPrivRaw, P2PPrivRaw,
Scoring,
PeerScoring, PeerScoring,
PeerScoreBands, PeerScoreBands,
Banning, Banning,
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"os" "os"
"strings" "strings"
"github.com/ethereum-optimism/optimism/op-node/rollup"
ds "github.com/ipfs/go-datastore" ds "github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/sync" "github.com/ipfs/go-datastore/sync"
leveldb "github.com/ipfs/go-ds-leveldb" leveldb "github.com/ipfs/go-ds-leveldb"
...@@ -24,7 +25,7 @@ import ( ...@@ -24,7 +25,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enode"
) )
func NewConfig(ctx *cli.Context, blockTime uint64) (*p2p.Config, error) { func NewConfig(ctx *cli.Context, rollupCfg *rollup.Config) (*p2p.Config, error) {
conf := &p2p.Config{} conf := &p2p.Config{}
if ctx.GlobalBool(flags.DisableP2P.Name) { if ctx.GlobalBool(flags.DisableP2P.Name) {
...@@ -54,7 +55,7 @@ func NewConfig(ctx *cli.Context, blockTime uint64) (*p2p.Config, error) { ...@@ -54,7 +55,7 @@ func NewConfig(ctx *cli.Context, blockTime uint64) (*p2p.Config, error) {
return nil, fmt.Errorf("failed to load p2p gossip options: %w", err) return nil, fmt.Errorf("failed to load p2p gossip options: %w", err)
} }
if err := loadPeerScoringParams(conf, ctx, blockTime); err != nil { if err := loadScoringParams(conf, ctx, rollupCfg); err != nil {
return nil, fmt.Errorf("failed to load p2p peer scoring options: %w", err) return nil, fmt.Errorf("failed to load p2p peer scoring options: %w", err)
} }
...@@ -62,10 +63,6 @@ func NewConfig(ctx *cli.Context, blockTime uint64) (*p2p.Config, error) { ...@@ -62,10 +63,6 @@ func NewConfig(ctx *cli.Context, blockTime uint64) (*p2p.Config, error) {
return nil, fmt.Errorf("failed to load banning option: %w", err) return nil, fmt.Errorf("failed to load banning option: %w", err)
} }
if err := loadTopicScoringParams(conf, ctx, blockTime); err != nil {
return nil, fmt.Errorf("failed to load p2p topic scoring options: %w", err)
}
conf.EnableReqRespSync = ctx.GlobalBool(flags.SyncReqRespFlag.Name) conf.EnableReqRespSync = ctx.GlobalBool(flags.SyncReqRespFlag.Name)
return conf, nil return conf, nil
...@@ -84,37 +81,22 @@ func validatePort(p uint) (uint16, error) { ...@@ -84,37 +81,22 @@ func validatePort(p uint) (uint16, error) {
return uint16(p), nil return uint16(p), nil
} }
// loadTopicScoringParams loads the topic scoring options from the CLI context. // loadScoringParams loads the peer scoring options from the CLI context.
// func loadScoringParams(conf *p2p.Config, ctx *cli.Context, rollupCfg *rollup.Config) error {
// If the topic scoring options are not set, then the default topic scoring. scoringLevel := ctx.GlobalString(flags.Scoring.Name)
func loadTopicScoringParams(conf *p2p.Config, ctx *cli.Context, blockTime uint64) error { // Check old names for backwards compatibility
scoringLevel := ctx.GlobalString(flags.TopicScoring.Name) if scoringLevel == "" {
if scoringLevel != "" { scoringLevel = ctx.GlobalString(flags.PeerScoring.Name)
// Set default block topic scoring parameters }
// See prysm: https://github.com/prysmaticlabs/prysm/blob/develop/beacon-chain/p2p/gossip_scoring_params.go if scoringLevel == "" {
// And research from lighthouse: https://gist.github.com/blacktemplar/5c1862cb3f0e32a1a7fb0b25e79e6e2c scoringLevel = ctx.GlobalString(flags.TopicScoring.Name)
// And docs: https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#topic-parameter-calculation-and-decay
topicScoreParams, err := p2p.GetTopicScoreParams(scoringLevel, blockTime)
if err != nil {
return err
}
conf.TopicScoring = &topicScoreParams
} }
return nil
}
// loadPeerScoringParams loads the scoring options from the CLI context.
//
// If the scoring level is not set, no scoring is enabled.
func loadPeerScoringParams(conf *p2p.Config, ctx *cli.Context, blockTime uint64) error {
scoringLevel := ctx.GlobalString(flags.PeerScoring.Name)
if scoringLevel != "" { if scoringLevel != "" {
peerScoreParams, err := p2p.GetPeerScoreParams(scoringLevel, blockTime) params, err := p2p.GetScoringParams(scoringLevel, rollupCfg)
if err != nil { if err != nil {
return err return err
} }
conf.PeerScoring = &peerScoreParams conf.ScoringParams = params
} }
return nil return nil
......
...@@ -51,6 +51,11 @@ type SetupP2P interface { ...@@ -51,6 +51,11 @@ type SetupP2P interface {
ReqRespSyncEnabled() bool ReqRespSyncEnabled() bool
} }
// ScoringParams defines the various types of peer scoring parameters.
type ScoringParams struct {
PeerScoring pubsub.PeerScoreParams
}
// Config sets up a p2p host and discv5 service from configuration. // Config sets up a p2p host and discv5 service from configuration.
// This implements SetupP2P. // This implements SetupP2P.
type Config struct { type Config struct {
...@@ -62,9 +67,7 @@ type Config struct { ...@@ -62,9 +67,7 @@ type Config struct {
// Enable P2P-based alt-syncing method (req-resp protocol, not gossip) // Enable P2P-based alt-syncing method (req-resp protocol, not gossip)
AltSync bool AltSync bool
// Pubsub Scoring Parameters ScoringParams *ScoringParams
PeerScoring *pubsub.PeerScoreParams
TopicScoring *pubsub.TopicScoreParams
// Whether to ban peers based on their [PeerScoring] score. Should be negative. // Whether to ban peers based on their [PeerScoring] score. Should be negative.
BanningEnabled bool BanningEnabled bool
...@@ -135,7 +138,10 @@ func (conf *Config) Disabled() bool { ...@@ -135,7 +138,10 @@ func (conf *Config) Disabled() bool {
} }
func (conf *Config) PeerScoringParams() *pubsub.PeerScoreParams { func (conf *Config) PeerScoringParams() *pubsub.PeerScoreParams {
return conf.PeerScoring if conf.ScoringParams == nil {
return nil
}
return &conf.ScoringParams.PeerScoring
} }
func (conf *Config) BanPeers() bool { func (conf *Config) BanPeers() bool {
...@@ -150,10 +156,6 @@ func (conf *Config) BanDuration() time.Duration { ...@@ -150,10 +156,6 @@ func (conf *Config) BanDuration() time.Duration {
return conf.BanningDuration return conf.BanningDuration
} }
func (conf *Config) TopicScoringParams() *pubsub.TopicScoreParams {
return conf.TopicScoring
}
func (conf *Config) ReqRespSyncEnabled() bool { func (conf *Config) ReqRespSyncEnabled() bool {
return conf.EnableReqRespSync return conf.EnableReqRespSync
} }
......
...@@ -52,7 +52,6 @@ var MessageDomainValidSnappy = [4]byte{1, 0, 0, 0} ...@@ -52,7 +52,6 @@ var MessageDomainValidSnappy = [4]byte{1, 0, 0, 0}
type GossipSetupConfigurables interface { type GossipSetupConfigurables interface {
PeerScoringParams() *pubsub.PeerScoreParams PeerScoringParams() *pubsub.PeerScoreParams
TopicScoringParams() *pubsub.TopicScoreParams
// ConfigureGossip creates configuration options to apply to the GossipSub setup // ConfigureGossip creates configuration options to apply to the GossipSub setup
ConfigureGossip(rollupCfg *rollup.Config) []pubsub.Option ConfigureGossip(rollupCfg *rollup.Config) []pubsub.Option
} }
...@@ -423,7 +422,7 @@ func (p *publisher) Close() error { ...@@ -423,7 +422,7 @@ func (p *publisher) Close() error {
return p.blocksTopic.Close() return p.blocksTopic.Close()
} }
func JoinGossip(p2pCtx context.Context, self peer.ID, topicScoreParams *pubsub.TopicScoreParams, ps *pubsub.PubSub, log log.Logger, cfg *rollup.Config, runCfg GossipRuntimeConfig, gossipIn GossipIn) (GossipOut, error) { func JoinGossip(p2pCtx context.Context, self peer.ID, ps *pubsub.PubSub, log log.Logger, cfg *rollup.Config, runCfg GossipRuntimeConfig, gossipIn GossipIn) (GossipOut, error) {
val := guardGossipValidator(log, logValidationResult(self, "validated block", log, BuildBlocksValidator(log, cfg, runCfg))) val := guardGossipValidator(log, logValidationResult(self, "validated block", log, BuildBlocksValidator(log, cfg, runCfg)))
blocksTopicName := blocksTopicV1(cfg) blocksTopicName := blocksTopicV1(cfg)
err := ps.RegisterTopicValidator(blocksTopicName, err := ps.RegisterTopicValidator(blocksTopicName,
...@@ -443,12 +442,6 @@ func JoinGossip(p2pCtx context.Context, self peer.ID, topicScoreParams *pubsub.T ...@@ -443,12 +442,6 @@ func JoinGossip(p2pCtx context.Context, self peer.ID, topicScoreParams *pubsub.T
} }
go LogTopicEvents(p2pCtx, log.New("topic", "blocks"), blocksTopicEvents) go LogTopicEvents(p2pCtx, log.New("topic", "blocks"), blocksTopicEvents)
if topicScoreParams != nil {
if err = blocksTopic.SetScoreParams(topicScoreParams); err != nil {
return nil, fmt.Errorf("failed to set topic score params: %w", err)
}
}
subscription, err := blocksTopic.Subscribe() subscription, err := blocksTopic.Subscribe()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to subscribe to blocks gossip topic: %w", err) return nil, fmt.Errorf("failed to subscribe to blocks gossip topic: %w", err)
......
...@@ -133,7 +133,7 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l ...@@ -133,7 +133,7 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l
if err != nil { if err != nil {
return fmt.Errorf("failed to start gossipsub router: %w", err) return fmt.Errorf("failed to start gossipsub router: %w", err)
} }
n.gsOut, err = JoinGossip(resourcesCtx, n.host.ID(), setup.TopicScoringParams(), n.gs, log, rollupCfg, runCfg, gossipIn) n.gsOut, err = JoinGossip(resourcesCtx, n.host.ID(), n.gs, log, rollupCfg, runCfg, gossipIn)
if err != nil { if err != nil {
return fmt.Errorf("failed to join blocks gossip topic: %w", err) return fmt.Errorf("failed to join blocks gossip topic: %w", err)
} }
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"math" "math"
"time" "time"
"github.com/ethereum-optimism/optimism/op-node/rollup"
pubsub "github.com/libp2p/go-libp2p-pubsub" pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peer"
) )
...@@ -12,6 +13,15 @@ import ( ...@@ -12,6 +13,15 @@ import (
// DecayToZero is the decay factor for a peer's score to zero. // DecayToZero is the decay factor for a peer's score to zero.
const DecayToZero = 0.01 const DecayToZero = 0.01
// MeshWeight is the weight of the mesh delivery topic.
const MeshWeight = -0.7
// MaxInMeshScore is the maximum score for being in the mesh.
const MaxInMeshScore = 10
// DecayEpoch is the number of epochs to decay the score over.
const DecayEpoch = time.Duration(5)
// ScoreDecay returns the decay factor for a given duration. // ScoreDecay returns the decay factor for a given duration.
func ScoreDecay(duration time.Duration, slot time.Duration) float64 { func ScoreDecay(duration time.Duration, slot time.Duration) float64 {
numOfTimes := duration / slot numOfTimes := duration / slot
...@@ -22,8 +32,8 @@ func ScoreDecay(duration time.Duration, slot time.Duration) float64 { ...@@ -22,8 +32,8 @@ func ScoreDecay(duration time.Duration, slot time.Duration) float64 {
// See [PeerScoreParams] for detailed documentation. // See [PeerScoreParams] for detailed documentation.
// //
// [PeerScoreParams]: https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub@v0.8.1#PeerScoreParams // [PeerScoreParams]: https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub@v0.8.1#PeerScoreParams
var LightPeerScoreParams = func(blockTime uint64) pubsub.PeerScoreParams { var LightPeerScoreParams = func(cfg *rollup.Config) pubsub.PeerScoreParams {
slot := time.Duration(blockTime) * time.Second slot := time.Duration(cfg.BlockTime) * time.Second
if slot == 0 { if slot == 0 {
slot = 2 * time.Second slot = 2 * time.Second
} }
...@@ -32,8 +42,29 @@ var LightPeerScoreParams = func(blockTime uint64) pubsub.PeerScoreParams { ...@@ -32,8 +42,29 @@ var LightPeerScoreParams = func(blockTime uint64) pubsub.PeerScoreParams {
epoch := 6 * slot epoch := 6 * slot
tenEpochs := 10 * epoch tenEpochs := 10 * epoch
oneHundredEpochs := 100 * epoch oneHundredEpochs := 100 * epoch
invalidDecayPeriod := 50 * epoch
return pubsub.PeerScoreParams{ return pubsub.PeerScoreParams{
Topics: make(map[string]*pubsub.TopicScoreParams), Topics: map[string]*pubsub.TopicScoreParams{
blocksTopicV1(cfg): {
TopicWeight: 0.8,
TimeInMeshWeight: MaxInMeshScore / inMeshCap(slot),
TimeInMeshQuantum: slot,
TimeInMeshCap: inMeshCap(slot),
FirstMessageDeliveriesWeight: 1,
FirstMessageDeliveriesDecay: ScoreDecay(20*epoch, slot),
FirstMessageDeliveriesCap: 23,
MeshMessageDeliveriesWeight: MeshWeight,
MeshMessageDeliveriesDecay: ScoreDecay(DecayEpoch*epoch, slot),
MeshMessageDeliveriesCap: float64(uint64(epoch/slot) * uint64(DecayEpoch)),
MeshMessageDeliveriesThreshold: float64(uint64(epoch/slot) * uint64(DecayEpoch) / 10),
MeshMessageDeliveriesWindow: 2 * time.Second,
MeshMessageDeliveriesActivation: 4 * epoch,
MeshFailurePenaltyWeight: MeshWeight,
MeshFailurePenaltyDecay: ScoreDecay(DecayEpoch*epoch, slot),
InvalidMessageDeliveriesWeight: -140.4475,
InvalidMessageDeliveriesDecay: ScoreDecay(invalidDecayPeriod, slot),
},
},
TopicScoreCap: 34, TopicScoreCap: 34,
AppSpecificScore: func(p peer.ID) float64 { AppSpecificScore: func(p peer.ID) float64 {
return 0 return 0
...@@ -51,65 +82,22 @@ var LightPeerScoreParams = func(blockTime uint64) pubsub.PeerScoreParams { ...@@ -51,65 +82,22 @@ var LightPeerScoreParams = func(blockTime uint64) pubsub.PeerScoreParams {
} }
} }
// DisabledPeerScoreParams is an instantiation of [pubsub.PeerScoreParams] where all scoring is disabled. // the cap for `inMesh` time scoring.
// See [PeerScoreParams] for detailed documentation. func inMeshCap(slot time.Duration) float64 {
// return float64((3600 * time.Second) / slot)
// [PeerScoreParams]: https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub@v0.8.1#PeerScoreParams
var DisabledPeerScoreParams = func(blockTime uint64) pubsub.PeerScoreParams {
slot := time.Duration(blockTime) * time.Second
if slot == 0 {
slot = 2 * time.Second
}
// We initialize an "epoch" as 6 blocks suggesting 6 blocks,
// each taking ~ 2 seconds, is 12 seconds
epoch := 6 * slot
tenEpochs := 10 * epoch
oneHundredEpochs := 100 * epoch
return pubsub.PeerScoreParams{
Topics: make(map[string]*pubsub.TopicScoreParams),
// 0 represent no cap
TopicScoreCap: 0,
AppSpecificScore: func(p peer.ID) float64 {
return 0
},
AppSpecificWeight: 1,
// ignore colocation scoring
IPColocationFactorWeight: 0,
IPColocationFactorWhitelist: nil,
// 0 disables the behaviour penalty
BehaviourPenaltyWeight: 0,
BehaviourPenaltyDecay: ScoreDecay(tenEpochs, slot),
DecayInterval: slot,
DecayToZero: DecayToZero,
RetainScore: oneHundredEpochs,
}
} }
// PeerScoreParamsByName is a map of name to function that returns a [pubsub.PeerScoreParams] based on the provided [rollup.Config]. func GetScoringParams(name string, cfg *rollup.Config) (*ScoringParams, error) {
var PeerScoreParamsByName = map[string](func(blockTime uint64) pubsub.PeerScoreParams){ switch name {
"light": LightPeerScoreParams, case "light":
"none": DisabledPeerScoreParams, return &ScoringParams{
} PeerScoring: LightPeerScoreParams(cfg),
}, nil
// AvailablePeerScoreParams returns a list of available peer score params. case "none":
// These can be used as an input to [GetPeerScoreParams] which returns the return nil, nil
// corresponding [pubsub.PeerScoreParams]. default:
func AvailablePeerScoreParams() []string { return nil, fmt.Errorf("unknown p2p scoring level: %v", name)
var params []string
for name := range PeerScoreParamsByName {
params = append(params, name)
}
return params
}
// GetPeerScoreParams returns the [pubsub.PeerScoreParams] for the given name.
func GetPeerScoreParams(name string, blockTime uint64) (pubsub.PeerScoreParams, error) {
params, ok := PeerScoreParamsByName[name]
if !ok {
return pubsub.PeerScoreParams{}, fmt.Errorf("invalid params %s", name)
} }
return params(blockTime), nil
} }
// NewPeerScoreThresholds returns a default [pubsub.PeerScoreThresholds]. // NewPeerScoreThresholds returns a default [pubsub.PeerScoreThresholds].
......
...@@ -2,10 +2,10 @@ package p2p ...@@ -2,10 +2,10 @@ package p2p
import ( import (
"math" "math"
"sort"
"testing" "testing"
"time" "time"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
pubsub "github.com/libp2p/go-libp2p-pubsub" pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
...@@ -25,14 +25,6 @@ func (testSuite *PeerParamsTestSuite) TestPeerScoreConstants() { ...@@ -25,14 +25,6 @@ func (testSuite *PeerParamsTestSuite) TestPeerScoreConstants() {
testSuite.Equal(0.01, DecayToZero) testSuite.Equal(0.01, DecayToZero)
} }
// TestAvailablePeerScoreParams validates the available peer score parameters.
func (testSuite *PeerParamsTestSuite) TestAvailablePeerScoreParams() {
available := AvailablePeerScoreParams()
sort.Strings(available)
expected := []string{"light", "none"}
testSuite.Equal(expected, available)
}
// TestNewPeerScoreThresholds validates the peer score thresholds. // TestNewPeerScoreThresholds validates the peer score thresholds.
// //
// This is tested to ensure that the thresholds are not modified and missed in review. // This is tested to ensure that the thresholds are not modified and missed in review.
...@@ -50,57 +42,17 @@ func (testSuite *PeerParamsTestSuite) TestNewPeerScoreThresholds() { ...@@ -50,57 +42,17 @@ func (testSuite *PeerParamsTestSuite) TestNewPeerScoreThresholds() {
} }
// TestGetPeerScoreParams validates the peer score parameters. // TestGetPeerScoreParams validates the peer score parameters.
func (testSuite *PeerParamsTestSuite) TestGetPeerScoreParams() { func (testSuite *PeerParamsTestSuite) TestGetPeerScoreParams_None() {
params, err := GetPeerScoreParams("light", 1) params, err := GetScoringParams("none", &chaincfg.Goerli)
testSuite.NoError(err) testSuite.NoError(err)
expected := LightPeerScoreParams(1) testSuite.Nil(params)
testSuite.Equal(expected.DecayInterval, params.DecayInterval)
testSuite.Equal(time.Duration(1)*time.Second, params.DecayInterval)
params, err = GetPeerScoreParams("none", 1)
testSuite.NoError(err)
expected = DisabledPeerScoreParams(1)
testSuite.Equal(expected.DecayInterval, params.DecayInterval)
testSuite.Equal(time.Duration(1)*time.Second, params.DecayInterval)
_, err = GetPeerScoreParams("invalid", 1)
testSuite.Error(err)
} }
// TestLightPeerScoreParams validates the light peer score params. // TestLightPeerScoreParams validates the light peer score params.
func (testSuite *PeerParamsTestSuite) TestLightPeerScoreParams() { func (testSuite *PeerParamsTestSuite) TestGetPeerScoreParams_Light() {
blockTime := uint64(1) cfg := chaincfg.Goerli
slot := time.Duration(blockTime) * time.Second cfg.BlockTime = 1
epoch := 6 * slot slot := time.Duration(cfg.BlockTime) * time.Second
oneHundredEpochs := 100 * epoch
// calculate the behavior penalty decay
duration := 10 * epoch
decay := math.Pow(DecayToZero, 1/float64(duration/slot))
testSuite.Equal(0.9261187281287935, decay)
// Test the params
params, err := GetPeerScoreParams("light", blockTime)
testSuite.NoError(err)
testSuite.Equal(params.Topics, make(map[string]*pubsub.TopicScoreParams))
testSuite.Equal(params.TopicScoreCap, float64(34))
// testSuite.Equal(params.AppSpecificScore("alice"), float(0))
testSuite.Equal(params.AppSpecificWeight, float64(1))
testSuite.Equal(params.IPColocationFactorWeight, float64(-35))
testSuite.Equal(params.IPColocationFactorThreshold, int(10))
testSuite.Nil(params.IPColocationFactorWhitelist)
testSuite.Equal(params.BehaviourPenaltyWeight, float64(-16))
testSuite.Equal(params.BehaviourPenaltyThreshold, float64(6))
testSuite.Equal(params.BehaviourPenaltyDecay, decay)
testSuite.Equal(params.DecayInterval, slot)
testSuite.Equal(params.DecayToZero, DecayToZero)
testSuite.Equal(params.RetainScore, oneHundredEpochs)
}
// TestDisabledPeerScoreParams validates the disabled peer score params.
func (testSuite *PeerParamsTestSuite) TestDisabledPeerScoreParams() {
blockTime := uint64(1)
slot := time.Duration(blockTime) * time.Second
epoch := 6 * slot epoch := 6 * slot
oneHundredEpochs := 100 * epoch oneHundredEpochs := 100 * epoch
...@@ -110,27 +62,33 @@ func (testSuite *PeerParamsTestSuite) TestDisabledPeerScoreParams() { ...@@ -110,27 +62,33 @@ func (testSuite *PeerParamsTestSuite) TestDisabledPeerScoreParams() {
testSuite.Equal(0.9261187281287935, decay) testSuite.Equal(0.9261187281287935, decay)
// Test the params // Test the params
params, err := GetPeerScoreParams("none", blockTime) scoringParams, err := GetScoringParams("light", &cfg)
peerParams := scoringParams.PeerScoring
testSuite.NoError(err) testSuite.NoError(err)
testSuite.Equal(params.Topics, make(map[string]*pubsub.TopicScoreParams)) // Topics should contain options for block topic
testSuite.Equal(params.TopicScoreCap, float64(0)) testSuite.Len(peerParams.Topics, 1)
testSuite.Equal(params.AppSpecificWeight, float64(1)) topicParams, ok := peerParams.Topics[blocksTopicV1(&cfg)]
testSuite.Equal(params.IPColocationFactorWeight, float64(0)) testSuite.True(ok, "should have block topic params")
testSuite.Nil(params.IPColocationFactorWhitelist) testSuite.NotZero(topicParams.TimeInMeshQuantum)
testSuite.Equal(params.BehaviourPenaltyWeight, float64(0)) testSuite.Equal(peerParams.TopicScoreCap, float64(34))
testSuite.Equal(params.BehaviourPenaltyDecay, decay) testSuite.Equal(peerParams.AppSpecificWeight, float64(1))
testSuite.Equal(params.DecayInterval, slot) testSuite.Equal(peerParams.IPColocationFactorWeight, float64(-35))
testSuite.Equal(params.DecayToZero, DecayToZero) testSuite.Equal(peerParams.IPColocationFactorThreshold, 10)
testSuite.Equal(params.RetainScore, oneHundredEpochs) testSuite.Nil(peerParams.IPColocationFactorWhitelist)
testSuite.Equal(peerParams.BehaviourPenaltyWeight, float64(-16))
testSuite.Equal(peerParams.BehaviourPenaltyThreshold, float64(6))
testSuite.Equal(peerParams.BehaviourPenaltyDecay, decay)
testSuite.Equal(peerParams.DecayInterval, slot)
testSuite.Equal(peerParams.DecayToZero, DecayToZero)
testSuite.Equal(peerParams.RetainScore, oneHundredEpochs)
} }
// TestParamsZeroBlockTime validates peer score params use default slot for 0 block time. // TestParamsZeroBlockTime validates peer score params use default slot for 0 block time.
func (testSuite *PeerParamsTestSuite) TestParamsZeroBlockTime() { func (testSuite *PeerParamsTestSuite) TestParamsZeroBlockTime() {
cfg := chaincfg.Goerli
cfg.BlockTime = 0
slot := 2 * time.Second slot := 2 * time.Second
params, err := GetPeerScoreParams("none", uint64(0)) params, err := GetScoringParams("light", &cfg)
testSuite.NoError(err)
testSuite.Equal(params.DecayInterval, slot)
params, err = GetPeerScoreParams("light", uint64(0))
testSuite.NoError(err) testSuite.NoError(err)
testSuite.Equal(params.DecayInterval, slot) testSuite.Equal(params.PeerScoring.DecayInterval, slot)
} }
...@@ -102,17 +102,19 @@ func newGossipSubs(testSuite *PeerScoresTestSuite, ctx context.Context, hosts [] ...@@ -102,17 +102,19 @@ func newGossipSubs(testSuite *PeerScoresTestSuite, ctx context.Context, hosts []
&rollup.Config{L2ChainID: big.NewInt(123)}, &rollup.Config{L2ChainID: big.NewInt(123)},
extPeerStore, testSuite.mockMetricer, logger) extPeerStore, testSuite.mockMetricer, logger)
opts = append(opts, ConfigurePeerScoring(&Config{ opts = append(opts, ConfigurePeerScoring(&Config{
PeerScoring: &pubsub.PeerScoreParams{ ScoringParams: &ScoringParams{
AppSpecificScore: func(p peer.ID) float64 { PeerScoring: pubsub.PeerScoreParams{
if p == hosts[0].ID() { AppSpecificScore: func(p peer.ID) float64 {
return -1000 if p == hosts[0].ID() {
} else { return -1000
return 0 } else {
} return 0
}
},
AppSpecificWeight: 1,
DecayInterval: time.Second,
DecayToZero: 0.01,
}, },
AppSpecificWeight: 1,
DecayInterval: time.Second,
DecayToZero: 0.01,
}, },
}, scorer, logger)...) }, scorer, logger)...)
ps, err := pubsub.NewGossipSubWithRouter(ctx, h, rt, opts...) ps, err := pubsub.NewGossipSubWithRouter(ctx, h, rt, opts...)
......
...@@ -85,10 +85,6 @@ func (p *Prepared) BanDuration() time.Duration { ...@@ -85,10 +85,6 @@ func (p *Prepared) BanDuration() time.Duration {
return 1 * time.Hour return 1 * time.Hour
} }
func (p *Prepared) TopicScoringParams() *pubsub.TopicScoreParams {
return nil
}
func (p *Prepared) Disabled() bool { func (p *Prepared) Disabled() bool {
return false return false
} }
......
...@@ -100,6 +100,12 @@ type peerRequest struct { ...@@ -100,6 +100,12 @@ type peerRequest struct {
complete *atomic.Bool complete *atomic.Bool
} }
type inFlightCheck struct {
num uint64
result chan bool
}
type SyncClientMetrics interface { type SyncClientMetrics interface {
ClientPayloadByNumberEvent(num uint64, resultCode byte, duration time.Duration) ClientPayloadByNumberEvent(num uint64, resultCode byte, duration time.Duration)
PayloadsQuarantineSize(n int) PayloadsQuarantineSize(n int)
...@@ -198,8 +204,9 @@ type SyncClient struct { ...@@ -198,8 +204,9 @@ type SyncClient struct {
// inFlight requests are not repeated // inFlight requests are not repeated
inFlight map[uint64]*atomic.Bool inFlight map[uint64]*atomic.Bool
requests chan rangeRequest requests chan rangeRequest
peerRequests chan peerRequest peerRequests chan peerRequest
inFlightChecks chan inFlightCheck
results chan syncResult results chan syncResult
...@@ -235,6 +242,7 @@ func NewSyncClient(log log.Logger, cfg *rollup.Config, newStream newStreamFn, rc ...@@ -235,6 +242,7 @@ func NewSyncClient(log log.Logger, cfg *rollup.Config, newStream newStreamFn, rc
requests: make(chan rangeRequest), // blocking requests: make(chan rangeRequest), // blocking
peerRequests: make(chan peerRequest, 128), peerRequests: make(chan peerRequest, 128),
results: make(chan syncResult, 128), results: make(chan syncResult, 128),
inFlightChecks: make(chan inFlightCheck, 128),
globalRL: rate.NewLimiter(globalServerBlocksRateLimit, globalServerBlocksBurst), globalRL: rate.NewLimiter(globalServerBlocksRateLimit, globalServerBlocksBurst),
resCtx: ctx, resCtx: ctx,
resCancel: cancel, resCancel: cancel,
...@@ -328,6 +336,14 @@ func (s *SyncClient) mainLoop() { ...@@ -328,6 +336,14 @@ func (s *SyncClient) mainLoop() {
ctx, cancel := context.WithTimeout(s.resCtx, maxResultProcessing) ctx, cancel := context.WithTimeout(s.resCtx, maxResultProcessing)
s.onResult(ctx, res) s.onResult(ctx, res)
cancel() cancel()
case check := <-s.inFlightChecks:
s.log.Info("Checking in flight", "num", check.num)
complete, ok := s.inFlight[check.num]
if !ok {
check.result <- false
} else {
check.result <- !complete.Load()
}
case <-s.resCtx.Done(): case <-s.resCtx.Done():
s.log.Info("stopped P2P req-resp L2 block sync client") s.log.Info("stopped P2P req-resp L2 block sync client")
return return
...@@ -335,6 +351,21 @@ func (s *SyncClient) mainLoop() { ...@@ -335,6 +351,21 @@ func (s *SyncClient) mainLoop() {
} }
} }
func (s *SyncClient) isInFlight(ctx context.Context, num uint64) (bool, error) {
check := inFlightCheck{num: num, result: make(chan bool, 1)}
select {
case s.inFlightChecks <- check:
case <-ctx.Done():
return false, errors.New("context cancelled when publishing in flight check")
}
select {
case res := <-check.result:
return res, nil
case <-ctx.Done():
return false, errors.New("context cancelled while waiting for in flight check response")
}
}
// onRangeRequest is exclusively called by the main loop, and has thus direct access to the request bookkeeping state. // onRangeRequest is exclusively called by the main loop, and has thus direct access to the request bookkeeping state.
// This function transforms requested block ranges into work for each peer. // This function transforms requested block ranges into work for each peer.
func (s *SyncClient) onRangeRequest(ctx context.Context, req rangeRequest) { func (s *SyncClient) onRangeRequest(ctx context.Context, req rangeRequest) {
......
...@@ -164,9 +164,13 @@ func TestMultiPeerSync(t *testing.T) { ...@@ -164,9 +164,13 @@ func TestMultiPeerSync(t *testing.T) {
cfg, payloads := setupSyncTestData(100) cfg, payloads := setupSyncTestData(100)
// Buffered channel of all blocks requested from any client.
requested := make(chan uint64, 100)
setupPeer := func(ctx context.Context, h host.Host) (*SyncClient, chan *eth.ExecutionPayload) { setupPeer := func(ctx context.Context, h host.Host) (*SyncClient, chan *eth.ExecutionPayload) {
// Serving payloads: just load them from the map, if they exist // Serving payloads: just load them from the map, if they exist
servePayload := mockPayloadFn(func(n uint64) (*eth.ExecutionPayload, error) { servePayload := mockPayloadFn(func(n uint64) (*eth.ExecutionPayload, error) {
requested <- n
p, ok := payloads.getPayload(n) p, ok := payloads.getPayload(n)
if !ok { if !ok {
return nil, ethereum.NotFound return nil, ethereum.NotFound
...@@ -241,6 +245,20 @@ func TestMultiPeerSync(t *testing.T) { ...@@ -241,6 +245,20 @@ func TestMultiPeerSync(t *testing.T) {
require.True(t, ok, "expecting known payload") require.True(t, ok, "expecting known payload")
require.Equal(t, exp.BlockHash, p.BlockHash, "expecting the correct payload") require.Equal(t, exp.BlockHash, p.BlockHash, "expecting the correct payload")
} }
// Wait for the request for block 25 to be made
ctx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second)
defer cancelFunc()
requestMade := false
for requestMade != true {
select {
case blockNum := <-requested:
if blockNum == 25 {
requestMade = true
}
case <-ctx.Done():
t.Fatal("Did not request block 25 in a reasonable time")
}
}
// the request for 25 should fail. See: // the request for 25 should fail. See:
// server: WARN peer requested unknown block by number num=25 // server: WARN peer requested unknown block by number num=25
// client: WARN failed p2p sync request num=25 err="peer failed to serve request with code 1" // client: WARN failed p2p sync request num=25 err="peer failed to serve request with code 1"
...@@ -251,7 +269,14 @@ func TestMultiPeerSync(t *testing.T) { ...@@ -251,7 +269,14 @@ func TestMultiPeerSync(t *testing.T) {
// But the re-request checks the status in the main loop, and it may thus look like it's still in-flight, // But the re-request checks the status in the main loop, and it may thus look like it's still in-flight,
// and thus not run the new request. // and thus not run the new request.
// Wait till the failed request is recognized as marked as done, so the re-request actually runs. // Wait till the failed request is recognized as marked as done, so the re-request actually runs.
for !clB.inFlight[25].Load() { ctx, cancelFunc = context.WithTimeout(context.Background(), 30*time.Second)
defer cancelFunc()
for {
isInFlight, err := clB.isInFlight(ctx, 25)
require.NoError(t, err)
if !isInFlight {
break
}
time.Sleep(time.Second) time.Sleep(time.Second)
} }
// And request a range again, 25 is there now, and 21-24 should follow quickly (some may already have been fetched and wait in quarantine) // And request a range again, 25 is there now, and 21-24 should follow quickly (some may already have been fetched and wait in quarantine)
......
package p2p
import (
"fmt"
"time"
pubsub "github.com/libp2p/go-libp2p-pubsub"
)
// MeshWeight is the weight of the mesh delivery topic.
const MeshWeight = -0.7
// MaxInMeshScore is the maximum score for being in the mesh.
const MaxInMeshScore = 10
// DecayEpoch is the number of epochs to decay the score over.
const DecayEpoch = time.Duration(5)
// LightTopicScoreParams is a default instantiation of [pubsub.TopicScoreParams].
// See [TopicScoreParams] for detailed documentation.
//
// [TopicScoreParams]: https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub@v0.8.1#TopicScoreParams
var LightTopicScoreParams = func(blockTime uint64) pubsub.TopicScoreParams {
slot := time.Duration(blockTime) * time.Second
if slot == 0 {
slot = 2 * time.Second
}
epoch := 6 * slot
invalidDecayPeriod := 50 * epoch
return pubsub.TopicScoreParams{
TopicWeight: 0.8,
TimeInMeshWeight: MaxInMeshScore / inMeshCap(slot),
TimeInMeshQuantum: slot,
TimeInMeshCap: inMeshCap(slot),
FirstMessageDeliveriesWeight: 1,
FirstMessageDeliveriesDecay: ScoreDecay(20*epoch, slot),
FirstMessageDeliveriesCap: 23,
MeshMessageDeliveriesWeight: MeshWeight,
MeshMessageDeliveriesDecay: ScoreDecay(DecayEpoch*epoch, slot),
MeshMessageDeliveriesCap: float64(uint64(epoch/slot) * uint64(DecayEpoch)),
MeshMessageDeliveriesThreshold: float64(uint64(epoch/slot) * uint64(DecayEpoch) / 10),
MeshMessageDeliveriesWindow: 2 * time.Second,
MeshMessageDeliveriesActivation: 4 * epoch,
MeshFailurePenaltyWeight: MeshWeight,
MeshFailurePenaltyDecay: ScoreDecay(DecayEpoch*epoch, slot),
InvalidMessageDeliveriesWeight: -140.4475,
InvalidMessageDeliveriesDecay: ScoreDecay(invalidDecayPeriod, slot),
}
}
// the cap for `inMesh` time scoring.
func inMeshCap(slot time.Duration) float64 {
return float64((3600 * time.Second) / slot)
}
// DisabledTopicScoreParams is an instantiation of [pubsub.TopicScoreParams] where all scoring is disabled.
// See [TopicScoreParams] for detailed documentation.
//
// [TopicScoreParams]: https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub@v0.8.1#TopicScoreParams
var DisabledTopicScoreParams = func(blockTime uint64) pubsub.TopicScoreParams {
slot := time.Duration(blockTime) * time.Second
if slot == 0 {
slot = 2 * time.Second
}
epoch := 6 * slot
invalidDecayPeriod := 50 * epoch
return pubsub.TopicScoreParams{
TopicWeight: 0, // disabled
TimeInMeshWeight: 0, // disabled
TimeInMeshQuantum: slot,
TimeInMeshCap: inMeshCap(slot),
FirstMessageDeliveriesWeight: 0, // disabled
FirstMessageDeliveriesDecay: ScoreDecay(20*epoch, slot),
FirstMessageDeliveriesCap: 23,
MeshMessageDeliveriesWeight: 0, // disabled
MeshMessageDeliveriesDecay: ScoreDecay(DecayEpoch*epoch, slot),
MeshMessageDeliveriesCap: float64(uint64(epoch/slot) * uint64(DecayEpoch)),
MeshMessageDeliveriesThreshold: float64(uint64(epoch/slot) * uint64(DecayEpoch) / 10),
MeshMessageDeliveriesWindow: 2 * time.Second,
MeshMessageDeliveriesActivation: 4 * epoch,
MeshFailurePenaltyWeight: 0, // disabled
MeshFailurePenaltyDecay: ScoreDecay(DecayEpoch*epoch, slot),
InvalidMessageDeliveriesWeight: 0, // disabled
InvalidMessageDeliveriesDecay: ScoreDecay(invalidDecayPeriod, slot),
}
}
// TopicScoreParamsByName is a map of name to [pubsub.TopicScoreParams].
var TopicScoreParamsByName = map[string](func(blockTime uint64) pubsub.TopicScoreParams){
"light": LightTopicScoreParams,
"none": DisabledTopicScoreParams,
}
// AvailableTopicScoreParams returns a list of available topic score params.
// These can be used as an input to [GetTopicScoreParams] which returns the
// corresponding [pubsub.TopicScoreParams].
func AvailableTopicScoreParams() []string {
var params []string
for name := range TopicScoreParamsByName {
params = append(params, name)
}
return params
}
// GetTopicScoreParams returns the [pubsub.TopicScoreParams] for the given name.
func GetTopicScoreParams(name string, blockTime uint64) (pubsub.TopicScoreParams, error) {
params, ok := TopicScoreParamsByName[name]
if !ok {
return pubsub.TopicScoreParams{}, fmt.Errorf("invalid topic params %s", name)
}
return params(blockTime), nil
}
...@@ -142,7 +142,7 @@ func (cfg *Config) CheckL1ChainID(ctx context.Context, client L1Client) error { ...@@ -142,7 +142,7 @@ func (cfg *Config) CheckL1ChainID(ctx context.Context, client L1Client) error {
return err return err
} }
if cfg.L1ChainID.Cmp(id) != 0 { if cfg.L1ChainID.Cmp(id) != 0 {
return fmt.Errorf("incorrect L1 RPC chain id %d, expected %d", cfg.L1ChainID, id) return fmt.Errorf("incorrect L1 RPC chain id %d, expected %d", id, cfg.L1ChainID)
} }
return nil return nil
} }
...@@ -154,7 +154,7 @@ func (cfg *Config) CheckL1GenesisBlockHash(ctx context.Context, client L1Client) ...@@ -154,7 +154,7 @@ func (cfg *Config) CheckL1GenesisBlockHash(ctx context.Context, client L1Client)
return err return err
} }
if l1GenesisBlockRef.Hash != cfg.Genesis.L1.Hash { if l1GenesisBlockRef.Hash != cfg.Genesis.L1.Hash {
return fmt.Errorf("incorrect L1 genesis block hash %d, expected %d", cfg.Genesis.L1.Hash, l1GenesisBlockRef.Hash) return fmt.Errorf("incorrect L1 genesis block hash %s, expected %s", l1GenesisBlockRef.Hash, cfg.Genesis.L1.Hash)
} }
return nil return nil
} }
...@@ -171,7 +171,7 @@ func (cfg *Config) CheckL2ChainID(ctx context.Context, client L2Client) error { ...@@ -171,7 +171,7 @@ func (cfg *Config) CheckL2ChainID(ctx context.Context, client L2Client) error {
return err return err
} }
if cfg.L2ChainID.Cmp(id) != 0 { if cfg.L2ChainID.Cmp(id) != 0 {
return fmt.Errorf("incorrect L2 RPC chain id, expected from config %d, obtained from client %d", cfg.L2ChainID, id) return fmt.Errorf("incorrect L2 RPC chain id %d, expected %d", id, cfg.L2ChainID)
} }
return nil return nil
} }
...@@ -183,7 +183,7 @@ func (cfg *Config) CheckL2GenesisBlockHash(ctx context.Context, client L2Client) ...@@ -183,7 +183,7 @@ func (cfg *Config) CheckL2GenesisBlockHash(ctx context.Context, client L2Client)
return err return err
} }
if l2GenesisBlockRef.Hash != cfg.Genesis.L2.Hash { if l2GenesisBlockRef.Hash != cfg.Genesis.L2.Hash {
return fmt.Errorf("incorrect L2 genesis block hash %d, expected %d", cfg.Genesis.L2.Hash, l2GenesisBlockRef.Hash) return fmt.Errorf("incorrect L2 genesis block hash %s, expected %s", l2GenesisBlockRef.Hash, cfg.Genesis.L2.Hash)
} }
return nil return nil
} }
......
...@@ -43,7 +43,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { ...@@ -43,7 +43,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
return nil, fmt.Errorf("failed to load p2p signer: %w", err) return nil, fmt.Errorf("failed to load p2p signer: %w", err)
} }
p2pConfig, err := p2pcli.NewConfig(ctx, rollupConfig.BlockTime) p2pConfig, err := p2pcli.NewConfig(ctx, rollupConfig)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load p2p config: %w", err) return nil, fmt.Errorf("failed to load p2p config: %w", err)
} }
......
...@@ -266,15 +266,14 @@ type blockHashParameter struct { ...@@ -266,15 +266,14 @@ type blockHashParameter struct {
func unusableMethod(err error) bool { func unusableMethod(err error) bool {
if rpcErr, ok := err.(rpc.Error); ok { if rpcErr, ok := err.(rpc.Error); ok {
code := rpcErr.ErrorCode() code := rpcErr.ErrorCode()
// method not found, or invalid params // invalid request, method not found, or invalid params
if code == -32601 || code == -32602 { if code == -32600 || code == -32601 || code == -32602 {
return true
}
} else {
errText := strings.ToLower(err.Error())
if strings.Contains(errText, "unknown method") || strings.Contains(errText, "invalid param") || strings.Contains(errText, "is not available") {
return true return true
} }
} }
return false errText := strings.ToLower(err.Error())
return strings.Contains(errText, "unsupported method") || // alchemy -32600 message
strings.Contains(errText, "unknown method") ||
strings.Contains(errText, "invalid param") ||
strings.Contains(errText, "is not available")
} }
#!/usr/bin/env bash
# This script starts a local devnet using Docker Compose. We have to use
# this more complicated Bash script rather than Compose's native orchestration
# tooling because we need to start each service in a specific order, and specify
# their configuration along the way. The order is:
#
# 1. Start L1.
# 2. Compile contracts.
# 3. Deploy the contracts to L1 if necessary.
# 4. Start L2, inserting the compiled contract artifacts into the genesis.
# 5. Get the genesis hashes and timestamps from L1/L2.
# 6. Generate the rollup driver's config using the genesis hashes and the
# timestamps recovered in step 4 as well as the address of the OptimismPortal
# contract deployed in step 3.
# 7. Start the rollup driver.
# 8. Start the L2 output submitter.
#
# The timestamps are critically important here, since the rollup driver will fill in
# empty blocks if the tip of L1 lags behind the current timestamp. This can lead to
# a perceived infinite loop. To get around this, we set the timestamp to the current
# time in this script.
#
# This script is safe to run multiple times. It stores state in `.devnet`, and
# contracts-bedrock/deployments/devnetL1.
#
# Don't run this script directly. Run it using the makefile, e.g. `make devnet-up`.
# To clean up your devnet, run `make devnet-clean`.
set -eu
L1_URL="http://localhost:8545"
L2_URL="http://localhost:9545"
OP_NODE="$PWD/op-node"
CONTRACTS_BEDROCK="$PWD/packages/contracts-bedrock"
NETWORK=devnetL1
DEVNET="$PWD/.devnet"
# Helper method that waits for a given URL to be up. Can't use
# cURL's built-in retry logic because connection reset errors
# are ignored unless you're using a very recent version of cURL
function wait_up {
echo -n "Waiting for $1 to come up..."
i=0
until curl -s -f -o /dev/null "$1"
do
echo -n .
sleep 0.25
((i=i+1))
if [ "$i" -eq 300 ]; then
echo " Timeout!" >&2
exit 1
fi
done
echo "Done!"
}
mkdir -p ./.devnet
# Regenerate the L1 genesis file if necessary. The existence of the genesis
# file is used to determine if we need to recreate the devnet's state folder.
if [ ! -f "$DEVNET/done" ]; then
echo "Regenerating genesis files"
TIMESTAMP=$(date +%s | xargs printf '0x%x')
cat "$CONTRACTS_BEDROCK/deploy-config/devnetL1.json" | jq -r ".l1GenesisBlockTimestamp = \"$TIMESTAMP\"" > /tmp/bedrock-devnet-deploy-config.json
(
cd "$OP_NODE"
go run cmd/main.go genesis devnet \
--deploy-config /tmp/bedrock-devnet-deploy-config.json \
--outfile.l1 $DEVNET/genesis-l1.json \
--outfile.l2 $DEVNET/genesis-l2.json \
--outfile.rollup $DEVNET/rollup.json
touch "$DEVNET/done"
)
fi
# Bring up L1.
(
cd ops-bedrock
echo "Bringing up L1..."
DOCKER_BUILDKIT=1 docker-compose build --progress plain
docker-compose up -d l1
wait_up $L1_URL
)
# Bring up L2.
(
cd ops-bedrock
echo "Bringing up L2..."
docker-compose up -d l2
wait_up $L2_URL
)
L2OO_ADDRESS="0x6900000000000000000000000000000000000000"
# Bring up everything else.
(
cd ops-bedrock
echo "Bringing up devnet..."
L2OO_ADDRESS="$L2OO_ADDRESS" \
docker-compose up -d op-proposer op-batcher
echo "Bringing up stateviz webserver..."
docker-compose up -d stateviz
)
echo "Devnet ready."
...@@ -89,3 +89,7 @@ ENTRYPOINT ["npm", "run", "start:drippie-mon"] ...@@ -89,3 +89,7 @@ ENTRYPOINT ["npm", "run", "start:drippie-mon"]
FROM base as wd-mon FROM base as wd-mon
WORKDIR /opt/optimism/packages/chain-mon WORKDIR /opt/optimism/packages/chain-mon
ENTRYPOINT ["yarn", "run", "start:wd-mon"] ENTRYPOINT ["yarn", "run", "start:wd-mon"]
FROM base as wallet-mon
WORKDIR /opt/optimism/packages/chain-mon
ENTRYPOINT ["yarn", "run", "start:wallet-mon"]
FROM debian:bullseye-20220822-slim as foundry-build
SHELL ["/bin/bash", "-c"]
WORKDIR /opt
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y curl build-essential git && \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup.sh && \
chmod +x ./rustup.sh && \
./rustup.sh -y
WORKDIR /opt/foundry
# Only diff from upstream docker image is this clone instead
# of COPY. We select a specific commit to use.
RUN git clone https://github.com/foundry-rs/foundry.git . \
&& git checkout da2392e58bb8a7fefeba46b40c4df1afad8ccd22
RUN source $HOME/.profile && \
cargo build --release && \
strip /opt/foundry/target/release/forge && \
strip /opt/foundry/target/release/cast && \
strip /opt/foundry/target/release/anvil
FROM ethereum/client-go:alltools-v1.10.25 as geth
FROM ghcr.io/crytic/echidna/echidna:v2.0.4 as echidna-test
FROM python:3.8.13-slim-bullseye
ENV GOPATH=/go
ENV PATH=/usr/local/go/bin:$GOPATH/bin:$PATH
ENV DEBIAN_FRONTEND=noninteractive
COPY --from=foundry-build /opt/foundry/target/release/forge /usr/local/bin/forge
COPY --from=foundry-build /opt/foundry/target/release/cast /usr/local/bin/cast
COPY --from=foundry-build /opt/foundry/target/release/anvil /usr/local/bin/anvil
COPY --from=geth /usr/local/bin/abigen /usr/local/bin/abigen
COPY --from=echidna-test /usr/local/bin/echidna-test /usr/local/bin/echidna-test
COPY check-changed.sh /usr/local/bin/check-changed
RUN apt-get update && \
apt-get install -y bash curl openssh-client git build-essential ca-certificates jq musl gnupg coreutils && \
curl -sL https://deb.nodesource.com/setup_16.x -o nodesource_setup.sh && \
curl -sL https://go.dev/dl/go1.19.linux-amd64.tar.gz -o go1.19.linux-amd64.tar.gz && \
tar -C /usr/local/ -xzvf go1.19.linux-amd64.tar.gz && \
ln -s /usr/local/go/bin/gofmt /usr/local/bin/gofmt && \
bash nodesource_setup.sh && \
apt-get install -y nodejs && \
npm i -g npm@8.11.0 \
npm i -g yarn && \
npm i -g depcheck && \
pip install slither-analyzer==0.9.3 && \
go install gotest.tools/gotestsum@latest && \
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.48.0 && \
curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/master/install.sh | bash && \
chmod +x /usr/local/bin/check-changed
RUN echo "downloading solidity compilers" && \
curl -o solc-linux-amd64-v0.5.17+commit.d19bba13 -sL https://binaries.soliditylang.org/linux-amd64/solc-linux-amd64-v0.5.17+commit.d19bba13 && \
curl -o solc-linux-amd64-v0.8.9+commit.e5eed63a -sL https://binaries.soliditylang.org/linux-amd64/solc-linux-amd64-v0.8.9+commit.e5eed63a && \
curl -o solc-linux-amd64-v0.8.10+commit.fc410830 -sL https://binaries.soliditylang.org/linux-amd64/solc-linux-amd64-v0.8.10+commit.fc410830 && \
curl -o solc-linux-amd64-v0.8.12+commit.f00d7308 -sL https://binaries.soliditylang.org/linux-amd64/solc-linux-amd64-v0.8.12+commit.f00d7308 && \
echo "verifying checksums" && \
(echo "c35ce7a4d3ffa5747c178b1e24c8541b2e5d8a82c1db3719eb4433a1f19e16f3 solc-linux-amd64-v0.5.17+commit.d19bba13" | sha256sum --check --status - || exit 1) && \
(echo "f851f11fad37496baabaf8d6cb5c057ca0d9754fddb7a351ab580d7fd728cb94 solc-linux-amd64-v0.8.9+commit.e5eed63a" | sha256sum --check --status - || exit 1) && \
(echo "c7effacf28b9d64495f81b75228fbf4266ac0ec87e8f1adc489ddd8a4dd06d89 solc-linux-amd64-v0.8.10+commit.fc410830" | sha256sum --check --status - || exit 1) && \
(echo "556c3ec44faf8ff6b67933fa8a8a403abe82c978d6e581dbfec4bd07360bfbf3 solc-linux-amd64-v0.8.12+commit.f00d7308" | sha256sum --check --status - || exit 1) && \
echo "caching compilers" && \
mkdir -p ~/.cache/hardhat-nodejs/compilers/linux-amd64 && \
cp solc-linux-amd64-v0.5.17+commit.d19bba13 ~/.cache/hardhat-nodejs/compilers/linux-amd64/ && \
cp solc-linux-amd64-v0.8.9+commit.e5eed63a ~/.cache/hardhat-nodejs/compilers/linux-amd64/ && \
cp solc-linux-amd64-v0.8.10+commit.fc410830 ~/.cache/hardhat-nodejs/compilers/linux-amd64/ && \
cp solc-linux-amd64-v0.8.12+commit.f00d7308 ~/.cache/hardhat-nodejs/compilers/linux-amd64/ && \
mkdir -p ~/.svm/0.5.17 && \
cp solc-linux-amd64-v0.5.17+commit.d19bba13 ~/.svm/0.5.17/solc-0.5.17 && \
mkdir -p ~/.svm/0.8.9 && \
cp solc-linux-amd64-v0.8.9+commit.e5eed63a ~/.svm/0.8.9/solc-0.8.9 && \
mkdir -p ~/.svm/0.8.10 && \
cp solc-linux-amd64-v0.8.10+commit.fc410830 ~/.svm/0.8.10/solc-0.8.10 && \
mkdir -p ~/.svm/0.8.12 && \
cp solc-linux-amd64-v0.8.12+commit.f00d7308 ~/.svm/0.8.12/solc-0.8.12 && \
rm solc-linux-amd64-v0.5.17+commit.d19bba13 && \
rm solc-linux-amd64-v0.8.9+commit.e5eed63a && \
rm solc-linux-amd64-v0.8.10+commit.fc410830 && \
rm solc-linux-amd64-v0.8.12+commit.f00d7308
RUN echo "downloading and verifying Codecov uploader" && \
curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import && \
curl -Os "https://uploader.codecov.io/latest/linux/codecov" && \
curl -Os "https://uploader.codecov.io/latest/linux/codecov.SHA256SUM" && \
curl -Os "https://uploader.codecov.io/latest/linux/codecov.SHA256SUM.sig" && \
gpgv codecov.SHA256SUM.sig codecov.SHA256SUM && \
shasum -a 256 -c codecov.SHA256SUM || sha256sum -c codecov.SHA256SUM && \
cp codecov /usr/local/bin/codecov && \
chmod +x /usr/local/bin/codecov && \
rm codecov
RUN echo "downloading mockery tool" && \
mkdir -p mockery-tmp-dir && \
curl -o mockery-tmp-dir/mockery.tar.gz -sL https://github.com/vektra/mockery/releases/download/v2.28.1/mockery_2.28.1_Linux_x86_64.tar.gz && \
tar -xzvf mockery-tmp-dir/mockery.tar.gz -C mockery-tmp-dir && \
cp mockery-tmp-dir/mockery /usr/local/bin/mockery && \
chmod +x /usr/local/bin/mockery && \
rm -rf mockery-tmp-dir
#!/usr/bin/env bash
set -euo pipefail
DOCKER_REPO=$1
GIT_TAG=$2
GIT_SHA=$3
IMAGE_NAME=$(echo "$GIT_TAG" | grep -Eow '^(fault-detector|proxyd|indexer|op-[a-z0-9\-]*)' || true)
if [ -z "$IMAGE_NAME" ]; then
echo "image name could not be parsed from git tag '$GIT_TAG'"
exit 1
fi
IMAGE_TAG=$(echo "$GIT_TAG" | grep -Eow 'v.*' || true)
if [ -z "$IMAGE_TAG" ]; then
echo "image tag could not be parsed from git tag '$GIT_TAG'"
exit 1
fi
SOURCE_IMAGE_TAG="$DOCKER_REPO/$IMAGE_NAME:$GIT_SHA"
TARGET_IMAGE_TAG="$DOCKER_REPO/$IMAGE_NAME:$IMAGE_TAG"
TARGET_IMAGE_TAG_LATEST="$DOCKER_REPO/$IMAGE_NAME:latest"
echo "Checking if docker images exist for '$IMAGE_NAME'"
echo ""
tags=$(gcloud container images list-tags "$DOCKER_REPO/$IMAGE_NAME" --limit 1 --format json)
if [ "$tags" = "[]" ]; then
echo "No existing docker images were found for '$IMAGE_NAME'. The code tagged with '$GIT_TAG' may not have an associated dockerfile or docker build job."
echo "If this service has a dockerfile, add a docker-publish job for it in the circleci config."
echo ""
echo "Exiting"
exit 0
fi
echo "Tagging $SOURCE_IMAGE_TAG with '$IMAGE_TAG'"
gcloud container images add-tag -q "$SOURCE_IMAGE_TAG" "$TARGET_IMAGE_TAG"
# Do not tag with latest if the release is a release candidate.
if [[ "$IMAGE_TAG" == *"rc"* ]]; then
echo "Not tagging with 'latest' because the release is a release candidate."
exit 0
fi
echo "Tagging $SOURCE_IMAGE_TAG with 'latest'"
gcloud container images add-tag -q "$SOURCE_IMAGE_TAG" "$TARGET_IMAGE_TAG_LATEST"
const os = require('os')
// this script unbundles the published packages output
// from changesets action to a key-value pair to be used
// with our publishing CI workflow
data = process.argv[2]
data = JSON.parse(data)
for (const i of data) {
const name = i.name.replace("@eth-optimism/", "")
const version = i.version
process.stdout.write(`::set-output name=${name}::${version}` + os.EOL)
}
#!/usr/bin/env python3
import json
import subprocess
import os
GETH_VERSION='v1.11.2'
def main():
for project in ('.', 'indexer'):
print(f'Updating {project}...')
update_mod(project)
def update_mod(project):
print('Replacing...')
subprocess.run([
'go',
'mod',
'edit',
'-replace',
f'github.com/ethereum/go-ethereum@{GETH_VERSION}=github.com/ethereum-optimism/op-geth@optimism'
], cwd=os.path.join(project), check=True)
print('Tidying...')
subprocess.run([
'go',
'mod',
'tidy'
], cwd=os.path.join(project), check=True)
if __name__ == '__main__':
main()
...@@ -8,6 +8,17 @@ BALANCE_MON__RPC= ...@@ -8,6 +8,17 @@ BALANCE_MON__RPC=
# JSON array in the format [{ "address": <address>, "nickname": <nickname> }, ... ] # JSON array in the format [{ "address": <address>, "nickname": <nickname> }, ... ]
BALANCE_MON__ACCOUNTS= BALANCE_MON__ACCOUNTS=
###############################################################################
# ↓ wallet-mon ↓ #
###############################################################################
# RPC pointing to network to monitor
WALLET_MON__RPC=
# The block number to start monitoring from
# Defaults to the first bedrock block if unset.
WALLET_MON__START_BLOCK_NUMBER=
############################################################################### ###############################################################################
# ↓ drippie-mon ↓ # # ↓ drippie-mon ↓ #
############################################################################### ###############################################################################
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
], ],
"scripts": { "scripts": {
"start:balance-mon": "ts-node ./src/balance-mon/service.ts", "start:balance-mon": "ts-node ./src/balance-mon/service.ts",
"start:wallet-mon": "ts-node ./src/wallet-mon/service.ts",
"start:drippie-mon": "ts-node ./src/drippie-mon/service.ts", "start:drippie-mon": "ts-node ./src/drippie-mon/service.ts",
"start:wd-mon": "ts-node ./src/wd-mon/service.ts", "start:wd-mon": "ts-node ./src/wd-mon/service.ts",
"test:coverage": "echo 'No tests defined.'", "test:coverage": "echo 'No tests defined.'",
...@@ -35,6 +36,7 @@ ...@@ -35,6 +36,7 @@
"dependencies": { "dependencies": {
"@eth-optimism/common-ts": "0.8.1", "@eth-optimism/common-ts": "0.8.1",
"@eth-optimism/contracts-periphery": "1.0.8", "@eth-optimism/contracts-periphery": "1.0.8",
"@eth-optimism/contracts-bedrock": "0.14.0",
"@eth-optimism/core-utils": "0.12.0", "@eth-optimism/core-utils": "0.12.0",
"@eth-optimism/sdk": "2.1.0", "@eth-optimism/sdk": "2.1.0",
"ethers": "^5.7.0", "ethers": "^5.7.0",
......
export * from './balance-mon/service' export * from './balance-mon/service'
export * from './drippie-mon/service' export * from './drippie-mon/service'
export * from './wd-mon/service' export * from './wd-mon/service'
export * from './wallet-mon/service'
import {
BaseServiceV2,
StandardOptions,
Gauge,
Counter,
validators,
waitForProvider,
} from '@eth-optimism/common-ts'
import { getChainId, compareAddrs } from '@eth-optimism/core-utils'
import { Provider } from '@ethersproject/abstract-provider'
import mainnetConfig from '@eth-optimism/contracts-bedrock/deploy-config/mainnet.json'
import goerliConfig from '@eth-optimism/contracts-bedrock/deploy-config/goerli.json'
import l2OutputOracleArtifactsMainnet from '@eth-optimism/contracts-bedrock/deployments/mainnet/L2OutputOracleProxy.json'
import l2OutputOracleArtifactsGoerli from '@eth-optimism/contracts-bedrock/deployments/goerli/L2OutputOracleProxy.json'
import { version } from '../../package.json'
const networks = {
1: {
name: 'mainnet',
l1StartingBlockTag: mainnetConfig.l1StartingBlockTag,
accounts: [
{
label: 'Proposer',
wallet: mainnetConfig.l2OutputOracleProposer,
target: l2OutputOracleArtifactsMainnet.address,
},
{
label: 'Batcher',
wallet: mainnetConfig.batchSenderAddress,
target: mainnetConfig.batchInboxAddress,
},
],
},
10: {
name: 'goerli',
l1StartingBlockTag: goerliConfig.l1StartingBlockTag,
accounts: [
{
label: 'Proposer',
wallet: goerliConfig.l2OutputOracleProposer,
target: l2OutputOracleArtifactsGoerli.address,
},
{
label: 'Batcher',
wallet: goerliConfig.batchSenderAddress,
target: goerliConfig.batchInboxAddress,
},
],
},
}
type WalletMonOptions = {
rpc: Provider
startBlockNumber: number
}
type WalletMonMetrics = {
validatedCalls: Counter
unexpectedCalls: Counter
unexpectedRpcErrors: Counter
}
type WalletMonState = {
chainId: number
highestUncheckedBlockNumber: number
}
export class WalletMonService extends BaseServiceV2<
WalletMonOptions,
WalletMonMetrics,
WalletMonState
> {
constructor(options?: Partial<WalletMonOptions & StandardOptions>) {
super({
version,
name: 'wallet-mon',
loop: true,
options: {
loopIntervalMs: 1000,
...options,
},
optionsSpec: {
rpc: {
validator: validators.provider,
desc: 'Provider for network to monitor balances on',
},
startBlockNumber: {
validator: validators.num,
default: -1,
desc: 'L1 block number to start checking from',
public: true,
},
},
metricsSpec: {
validatedCalls: {
type: Gauge,
desc: 'Transactions from the account checked',
labels: ['wallet', 'target', 'nickname'],
},
unexpectedCalls: {
type: Counter,
desc: 'Number of unexpected wallets',
labels: ['wallet', 'target', 'nickname'],
},
unexpectedRpcErrors: {
type: Counter,
desc: 'Number of unexpected RPC errors',
labels: ['section', 'name'],
},
},
})
}
protected async init(): Promise<void> {
// Connect to L1.
await waitForProvider(this.options.rpc, {
logger: this.logger,
name: 'L1',
})
this.state.chainId = await getChainId(this.options.rpc)
const l1StartingBlockTag = networks[this.state.chainId].l1StartingBlockTag
if (this.options.startBlockNumber === -1) {
const block = await this.options.rpc.getBlock(l1StartingBlockTag)
this.state.highestUncheckedBlockNumber = block.number
} else {
this.state.highestUncheckedBlockNumber = this.options.startBlockNumber
}
}
protected async main(): Promise<void> {
if (
(await this.options.rpc.getBlockNumber()) <
this.state.highestUncheckedBlockNumber
) {
this.logger.info('Waiting for new blocks')
return
}
const network = networks[this.state.chainId]
const accounts = network.accounts
const block = await this.options.rpc.getBlock(
this.state.highestUncheckedBlockNumber
)
this.logger.info('Checking block', {
number: block.number,
})
const transactions = []
for (const txHash of block.transactions) {
const t = await this.options.rpc.getTransaction(txHash)
transactions.push(t)
}
for (const transaction of transactions) {
for (const account of accounts) {
if (compareAddrs(account.wallet, transaction.from)) {
if (compareAddrs(account.target, transaction.to)) {
this.metrics.validatedCalls.inc({
nickname: account.label,
wallet: account.address,
target: account.target,
})
this.logger.info('validated call', {
nickname: account.label,
wallet: account.address,
target: account.target,
})
} else {
this.metrics.unexpectedCalls.inc({
nickname: account.label,
wallet: account.address,
target: transaction.to,
})
this.logger.error('Unexpected call detected', {
nickname: account.label,
address: account.address,
target: transaction.to,
})
}
}
}
}
this.logger.info('Checked block', {
number: this.state.highestUncheckedBlockNumber,
})
this.state.highestUncheckedBlockNumber++
}
}
if (require.main === module) {
const service = new WalletMonService()
service.run()
}
...@@ -2,9 +2,9 @@ Bytes_slice_Test:test_slice_acrossMultipleWords_works() (gas: 9413) ...@@ -2,9 +2,9 @@ Bytes_slice_Test:test_slice_acrossMultipleWords_works() (gas: 9413)
Bytes_slice_Test:test_slice_acrossWords_works() (gas: 1430) Bytes_slice_Test:test_slice_acrossWords_works() (gas: 1430)
Bytes_slice_Test:test_slice_fromNonZeroIdx_works() (gas: 17240) Bytes_slice_Test:test_slice_fromNonZeroIdx_works() (gas: 17240)
Bytes_slice_Test:test_slice_fromZeroIdx_works() (gas: 20826) Bytes_slice_Test:test_slice_fromZeroIdx_works() (gas: 20826)
Bytes_toNibbles_Test:test_toNibbles_expectedResult128Bytes_works() (gas: 129874) Bytes_toNibbles_Test:test_toNibbles_expectedResult128Bytes_works() (gas: 78882)
Bytes_toNibbles_Test:test_toNibbles_expectedResult5Bytes_works() (gas: 6132) Bytes_toNibbles_Test:test_toNibbles_expectedResult5Bytes_works() (gas: 3992)
Bytes_toNibbles_Test:test_toNibbles_zeroLengthInput_works() (gas: 944) Bytes_toNibbles_Test:test_toNibbles_zeroLengthInput_works() (gas: 823)
CrossDomainMessenger_BaseGas_Test:test_baseGas_succeeds() (gas: 20412) CrossDomainMessenger_BaseGas_Test:test_baseGas_succeeds() (gas: 20412)
CrossDomainOwnable2_Test:test_onlyOwner_notMessenger_reverts() (gas: 8416) CrossDomainOwnable2_Test:test_onlyOwner_notMessenger_reverts() (gas: 8416)
CrossDomainOwnable2_Test:test_onlyOwner_notOwner2_reverts() (gas: 57515) CrossDomainOwnable2_Test:test_onlyOwner_notOwner2_reverts() (gas: 57515)
...@@ -27,11 +27,25 @@ CrossDomainOwnable_Test:test_onlyOwner_notOwner_reverts() (gas: 10597) ...@@ -27,11 +27,25 @@ CrossDomainOwnable_Test:test_onlyOwner_notOwner_reverts() (gas: 10597)
CrossDomainOwnable_Test:test_onlyOwner_succeeds() (gas: 34883) CrossDomainOwnable_Test:test_onlyOwner_succeeds() (gas: 34883)
DeployerWhitelist_Test:test_owner_succeeds() (gas: 7582) DeployerWhitelist_Test:test_owner_succeeds() (gas: 7582)
DeployerWhitelist_Test:test_storageSlots_succeeds() (gas: 33395) DeployerWhitelist_Test:test_storageSlots_succeeds() (gas: 33395)
DisputeGameFactory_Test:test_owner_succeeds() (gas: 7582) DisputeGameFactory_Test:test_owner_succeeds() (gas: 12610)
DisputeGameFactory_Test:test_setImplementation_notOwner_reverts() (gas: 11191) DisputeGameFactory_Test:test_setImplementation_notOwner_reverts() (gas: 16099)
DisputeGameFactory_Test:test_setImplementation_succeeds() (gas: 38765) DisputeGameFactory_Test:test_setImplementation_succeeds() (gas: 44302)
DisputeGameFactory_Test:test_transferOwnership_notOwner_reverts() (gas: 10979) DisputeGameFactory_Test:test_transferOwnership_notOwner_reverts() (gas: 15974)
DisputeGameFactory_Test:test_transferOwnership_succeeds() (gas: 13180) DisputeGameFactory_Test:test_transferOwnership_succeeds() (gas: 18694)
FaultDisputeGame_Test:test_clockTimeExceeded_reverts() (gas: 26413)
FaultDisputeGame_Test:test_defendRoot_reverts() (gas: 13258)
FaultDisputeGame_Test:test_duplicateClaim_reverts() (gas: 103259)
FaultDisputeGame_Test:test_extraData_succeeds() (gas: 17478)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17859)
FaultDisputeGame_Test:test_gameDepthExceeded_reverts() (gas: 5903426)
FaultDisputeGame_Test:test_gameStart_succeeds() (gas: 10337)
FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8194)
FaultDisputeGame_Test:test_initialRootClaimData_succeeds() (gas: 17580)
FaultDisputeGame_Test:test_moveAgainstNonexistentParent_reverts() (gas: 24587)
FaultDisputeGame_Test:test_move_gameNotInProgress_reverts() (gas: 10945)
FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8191)
FaultDisputeGame_Test:test_simpleAttack_succeeds() (gas: 107391)
FaultDisputeGame_Test:test_version_succeeds() (gas: 9780)
FeeVault_Test:test_constructor_succeeds() (gas: 18185) FeeVault_Test:test_constructor_succeeds() (gas: 18185)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 352135) GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 352135)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2950342) GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2950342)
...@@ -43,7 +57,7 @@ GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (g ...@@ -43,7 +57,7 @@ GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (g
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 88513) GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 88513)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 75225) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 75225)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 75662) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 75662)
GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 169237) GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 143113)
GasPriceOracle_Test:test_baseFee_succeeds() (gas: 8325) GasPriceOracle_Test:test_baseFee_succeeds() (gas: 8325)
GasPriceOracle_Test:test_decimals_succeeds() (gas: 6167) GasPriceOracle_Test:test_decimals_succeeds() (gas: 6167)
GasPriceOracle_Test:test_gasPrice_succeeds() (gas: 8294) GasPriceOracle_Test:test_gasPrice_succeeds() (gas: 8294)
...@@ -207,25 +221,25 @@ LegacyERC20ETH_Test:test_transferFrom_doesNotExist_reverts() (gas: 12957) ...@@ -207,25 +221,25 @@ LegacyERC20ETH_Test:test_transferFrom_doesNotExist_reverts() (gas: 12957)
LegacyERC20ETH_Test:test_transfer_doesNotExist_reverts() (gas: 10755) LegacyERC20ETH_Test:test_transfer_doesNotExist_reverts() (gas: 10755)
LegacyMessagePasser_Test:test_passMessageToL1_succeeds() (gas: 34524) LegacyMessagePasser_Test:test_passMessageToL1_succeeds() (gas: 34524)
MerkleTrie_get_Test:test_get_corruptedProof_reverts() (gas: 5736) MerkleTrie_get_Test:test_get_corruptedProof_reverts() (gas: 5736)
MerkleTrie_get_Test:test_get_extraProofElements_reverts() (gas: 60631) MerkleTrie_get_Test:test_get_extraProofElements_reverts() (gas: 58975)
MerkleTrie_get_Test:test_get_invalidDataRemainder_reverts() (gas: 35852) MerkleTrie_get_Test:test_get_invalidDataRemainder_reverts() (gas: 35852)
MerkleTrie_get_Test:test_get_invalidInternalNodeHash_reverts() (gas: 50810) MerkleTrie_get_Test:test_get_invalidInternalNodeHash_reverts() (gas: 49706)
MerkleTrie_get_Test:test_get_nonexistentKey1_reverts() (gas: 59671) MerkleTrie_get_Test:test_get_nonexistentKey1_reverts() (gas: 54839)
MerkleTrie_get_Test:test_get_nonexistentKey2_reverts() (gas: 23385) MerkleTrie_get_Test:test_get_nonexistentKey2_reverts() (gas: 16723)
MerkleTrie_get_Test:test_get_smallerPathThanKey1_reverts() (gas: 53525) MerkleTrie_get_Test:test_get_smallerPathThanKey1_reverts() (gas: 51472)
MerkleTrie_get_Test:test_get_smallerPathThanKey2_reverts() (gas: 55006) MerkleTrie_get_Test:test_get_smallerPathThanKey2_reverts() (gas: 52953)
MerkleTrie_get_Test:test_get_validProof10_succeeds() (gas: 50593) MerkleTrie_get_Test:test_get_validProof10_succeeds() (gas: 48937)
MerkleTrie_get_Test:test_get_validProof1_succeeds() (gas: 61688) MerkleTrie_get_Test:test_get_validProof1_succeeds() (gas: 56062)
MerkleTrie_get_Test:test_get_validProof2_succeeds() (gas: 71579) MerkleTrie_get_Test:test_get_validProof2_succeeds() (gas: 65953)
MerkleTrie_get_Test:test_get_validProof3_succeeds() (gas: 32827) MerkleTrie_get_Test:test_get_validProof3_succeeds() (gas: 27356)
MerkleTrie_get_Test:test_get_validProof4_succeeds() (gas: 23623) MerkleTrie_get_Test:test_get_validProof4_succeeds() (gas: 18152)
MerkleTrie_get_Test:test_get_validProof5_succeeds() (gas: 84262) MerkleTrie_get_Test:test_get_validProof5_succeeds() (gas: 80776)
MerkleTrie_get_Test:test_get_validProof6_succeeds() (gas: 73021) MerkleTrie_get_Test:test_get_validProof6_succeeds() (gas: 69535)
MerkleTrie_get_Test:test_get_validProof7_succeeds() (gas: 79719) MerkleTrie_get_Test:test_get_validProof7_succeeds() (gas: 76233)
MerkleTrie_get_Test:test_get_validProof8_succeeds() (gas: 50550) MerkleTrie_get_Test:test_get_validProof8_succeeds() (gas: 48894)
MerkleTrie_get_Test:test_get_validProof9_succeeds() (gas: 50550) MerkleTrie_get_Test:test_get_validProof9_succeeds() (gas: 48894)
MerkleTrie_get_Test:test_get_wrongKeyProof_reverts() (gas: 53871) MerkleTrie_get_Test:test_get_wrongKeyProof_reverts() (gas: 50782)
MerkleTrie_get_Test:test_get_zeroBranchValueLength_reverts() (gas: 43248) MerkleTrie_get_Test:test_get_zeroBranchValueLength_reverts() (gas: 41747)
MerkleTrie_get_Test:test_get_zeroLengthKey_reverts() (gas: 3632) MerkleTrie_get_Test:test_get_zeroLengthKey_reverts() (gas: 3632)
MintManager_constructor_Test:test_constructor_succeeds() (gas: 10579) MintManager_constructor_Test:test_constructor_succeeds() (gas: 10579)
MintManager_mint_Test:test_mint_afterPeriodElapsed_succeeds() (gas: 148117) MintManager_mint_Test:test_mint_afterPeriodElapsed_succeeds() (gas: 148117)
...@@ -265,26 +279,26 @@ OptimismPortalUpgradeable_Test:test_initialize_cannotInitImpl_reverts() (gas: 10 ...@@ -265,26 +279,26 @@ OptimismPortalUpgradeable_Test:test_initialize_cannotInitImpl_reverts() (gas: 10
OptimismPortalUpgradeable_Test:test_initialize_cannotInitProxy_reverts() (gas: 15918) OptimismPortalUpgradeable_Test:test_initialize_cannotInitProxy_reverts() (gas: 15918)
OptimismPortalUpgradeable_Test:test_params_initValuesOnProxy_succeeds() (gas: 21774) OptimismPortalUpgradeable_Test:test_params_initValuesOnProxy_succeeds() (gas: 21774)
OptimismPortalUpgradeable_Test:test_upgradeToAndCall_upgrading_succeeds() (gas: 180547) OptimismPortalUpgradeable_Test:test_upgradeToAndCall_upgrading_succeeds() (gas: 180547)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifOutputRootChanges_reverts() (gas: 204022) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifOutputRootChanges_reverts() (gas: 177898)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifOutputTimestampIsNotFinalized_reverts() (gas: 207475) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifOutputTimestampIsNotFinalized_reverts() (gas: 181351)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifWithdrawalNotProven_reverts() (gas: 41731) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifWithdrawalNotProven_reverts() (gas: 41731)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifWithdrawalProofNotOldEnough_reverts() (gas: 199419) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifWithdrawalProofNotOldEnough_reverts() (gas: 173295)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onInsufficientGas_reverts() (gas: 205848) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onInsufficientGas_reverts() (gas: 179724)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onRecentWithdrawal_reverts() (gas: 180184) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onRecentWithdrawal_reverts() (gas: 154060)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onReentrancy_reverts() (gas: 243823) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onReentrancy_reverts() (gas: 217699)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onReplay_reverts() (gas: 245561) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onReplay_reverts() (gas: 219437)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_paused_reverts() (gas: 53510) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_paused_reverts() (gas: 53510)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_provenWithdrawalHash_succeeds() (gas: 234996) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_provenWithdrawalHash_succeeds() (gas: 208872)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_targetFails_fails() (gas: 8797746687696163867) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_targetFails_fails() (gas: 8797746687696162662)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_timestampLessThanL2OracleStart_reverts() (gas: 196997) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_timestampLessThanL2OracleStart_reverts() (gas: 170873)
OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_onInvalidOutputRootProof_reverts() (gas: 85690) OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_onInvalidOutputRootProof_reverts() (gas: 85690)
OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_onInvalidWithdrawalProof_reverts() (gas: 137350) OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_onInvalidWithdrawalProof_reverts() (gas: 111226)
OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_onSelfCall_reverts() (gas: 52947) OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_onSelfCall_reverts() (gas: 52947)
OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_paused_reverts() (gas: 73673) OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_paused_reverts() (gas: 73673)
OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_replayProveChangedOutputRootAndOutputIndex_succeeds() (gas: 346739) OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_replayProveChangedOutputRootAndOutputIndex_succeeds() (gas: 294491)
OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_replayProveChangedOutputRoot_succeeds() (gas: 279571) OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_replayProveChangedOutputRoot_succeeds() (gas: 227323)
OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_replayProve_reverts() (gas: 192548) OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_replayProve_reverts() (gas: 166424)
OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_validWithdrawalProof_succeeds() (gas: 180486) OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_validWithdrawalProof_succeeds() (gas: 154362)
OptimismPortal_Test:test_constructor_succeeds() (gas: 19402) OptimismPortal_Test:test_constructor_succeeds() (gas: 19402)
OptimismPortal_Test:test_depositTransaction_contractCreation_reverts() (gas: 14342) OptimismPortal_Test:test_depositTransaction_contractCreation_reverts() (gas: 14342)
OptimismPortal_Test:test_depositTransaction_createWithZeroValueForContract_succeeds() (gas: 76726) OptimismPortal_Test:test_depositTransaction_createWithZeroValueForContract_succeeds() (gas: 76726)
......
...@@ -253,16 +253,13 @@ ...@@ -253,16 +253,13 @@
➡ contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory ➡ contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory
======================= =======================
| Name | Type | Slot | Offset | Bytes | Contract | | Name | Type | Slot | Offset | Bytes | Contract |
|--------------|-------------------------------------------------|------|--------|-------|-------------------------------------------------------------| |-----------------|--------------------------------------------|------|--------|-------|-------------------------------------------------------------|
| _owner | address | 0 | 0 | 20 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory | | _initialized | uint8 | 0 | 0 | 1 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| gameImpls | mapping(enum GameType => contract IDisputeGame) | 1 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory | | _initializing | bool | 0 | 1 | 1 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| disputeGames | mapping(Hash => contract IDisputeGame) | 2 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory | | __gap | uint256[50] | 1 | 0 | 1600 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| _owner | address | 51 | 0 | 20 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
======================= | __gap | uint256[49] | 52 | 0 | 1568 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
➡ contracts/dispute/BondManager.sol:BondManager | gameImpls | mapping(GameType => contract IDisputeGame) | 101 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
======================= | disputeGames | mapping(Hash => contract IDisputeGame) | 102 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| disputeGameList | contract IDisputeGame[] | 103 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| Name | Type | Slot | Offset | Bytes | Contract |
|-------|---------------------------------------------|------|--------|-------|-----------------------------------------------|
| bonds | mapping(bytes32 => struct BondManager.Bond) | 0 | 0 | 32 | contracts/dispute/BondManager.sol:BondManager |
...@@ -140,7 +140,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -140,7 +140,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
} }
/** /**
* @custom:semver 1.6.0 * @custom:semver 1.7.0
* *
* @param _l2Oracle Address of the L2OutputOracle contract. * @param _l2Oracle Address of the L2OutputOracle contract.
* @param _guardian Address that can pause deposits and withdrawals. * @param _guardian Address that can pause deposits and withdrawals.
...@@ -152,7 +152,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -152,7 +152,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
address _guardian, address _guardian,
bool _paused, bool _paused,
SystemConfig _config SystemConfig _config
) Semver(1, 6, 0) { ) Semver(1, 7, 0) {
L2_ORACLE = _l2Oracle; L2_ORACLE = _l2Oracle;
GUARDIAN = _guardian; GUARDIAN = _guardian;
SYSTEM_CONFIG = _config; SYSTEM_CONFIG = _config;
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { OptimismPortal } from "../L1/OptimismPortal.sol";
/**
* @title PortalSender
* @notice The PortalSender is a simple intermediate contract that will transfer the balance of the
* L1StandardBridge to the OptimismPortal during the Bedrock migration.
*/
contract PortalSender {
/**
* @notice Address of the OptimismPortal contract.
*/
OptimismPortal public immutable PORTAL;
/**
* @param _portal Address of the OptimismPortal contract.
*/
constructor(OptimismPortal _portal) {
PORTAL = _portal;
}
/**
* @notice Sends balance of this contract to the OptimismPortal.
*/
function donate() public {
PORTAL.donateETH{ value: address(this).balance }();
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
import { L1ERC721Bridge } from "../L1/L1ERC721Bridge.sol";
import { L1StandardBridge } from "../L1/L1StandardBridge.sol";
import { L1ChugSplashProxy } from "../legacy/L1ChugSplashProxy.sol";
import { AddressManager } from "../legacy/AddressManager.sol";
import { Proxy } from "../universal/Proxy.sol";
import { ProxyAdmin } from "../universal/ProxyAdmin.sol";
import { OptimismMintableERC20Factory } from "../universal/OptimismMintableERC20Factory.sol";
import { PortalSender } from "./PortalSender.sol";
import { SystemConfig } from "../L1/SystemConfig.sol";
import { ResourceMetering } from "../L1/ResourceMetering.sol";
import { Constants } from "../libraries/Constants.sol";
/**
* @title SystemDictator
* @notice The SystemDictator is responsible for coordinating the deployment of a full Bedrock
* system. The SystemDictator is designed to support both fresh network deployments and
* upgrades to existing pre-Bedrock systems.
*/
contract SystemDictator is OwnableUpgradeable {
/**
* @notice Basic system configuration.
*/
struct GlobalConfig {
AddressManager addressManager;
ProxyAdmin proxyAdmin;
address controller;
address finalOwner;
}
/**
* @notice Set of proxy addresses.
*/
struct ProxyAddressConfig {
address l2OutputOracleProxy;
address optimismPortalProxy;
address l1CrossDomainMessengerProxy;
address l1StandardBridgeProxy;
address optimismMintableERC20FactoryProxy;
address l1ERC721BridgeProxy;
address systemConfigProxy;
}
/**
* @notice Set of implementation addresses.
*/
struct ImplementationAddressConfig {
L2OutputOracle l2OutputOracleImpl;
OptimismPortal optimismPortalImpl;
L1CrossDomainMessenger l1CrossDomainMessengerImpl;
L1StandardBridge l1StandardBridgeImpl;
OptimismMintableERC20Factory optimismMintableERC20FactoryImpl;
L1ERC721Bridge l1ERC721BridgeImpl;
PortalSender portalSenderImpl;
SystemConfig systemConfigImpl;
}
/**
* @notice Dynamic L2OutputOracle config.
*/
struct L2OutputOracleDynamicConfig {
uint256 l2OutputOracleStartingBlockNumber;
uint256 l2OutputOracleStartingTimestamp;
}
/**
* @notice Values for the system config contract.
*/
struct SystemConfigConfig {
address owner;
uint256 overhead;
uint256 scalar;
bytes32 batcherHash;
uint64 gasLimit;
address unsafeBlockSigner;
ResourceMetering.ResourceConfig resourceConfig;
}
/**
* @notice Combined system configuration.
*/
struct DeployConfig {
GlobalConfig globalConfig;
ProxyAddressConfig proxyAddressConfig;
ImplementationAddressConfig implementationAddressConfig;
SystemConfigConfig systemConfigConfig;
}
/**
* @notice Step after which exit 1 can no longer be used.
*/
uint8 public constant EXIT_1_NO_RETURN_STEP = 3;
/**
* @notice Step where proxy ownership is transferred.
*/
uint8 public constant PROXY_TRANSFER_STEP = 4;
/**
* @notice System configuration.
*/
DeployConfig public config;
/**
* @notice Dynamic configuration for the L2OutputOracle.
*/
L2OutputOracleDynamicConfig public l2OutputOracleDynamicConfig;
/**
* @notice Dynamic configuration for the OptimismPortal. Determines
* if the system should be paused when initialized.
*/
bool public optimismPortalDynamicConfig;
/**
* @notice Current step;
*/
uint8 public currentStep;
/**
* @notice Whether or not dynamic config has been set.
*/
bool public dynamicConfigSet;
/**
* @notice Whether or not the deployment is finalized.
*/
bool public finalized;
/**
* @notice Whether or not the deployment has been exited.
*/
bool public exited;
/**
* @notice Address of the old L1CrossDomainMessenger implementation.
*/
address public oldL1CrossDomainMessenger;
/**
* @notice Checks that the current step is the expected step, then bumps the current step.
*
* @param _step Current step.
*/
modifier step(uint8 _step) {
require(!finalized, "SystemDictator: already finalized");
require(!exited, "SystemDictator: already exited");
require(currentStep == _step, "SystemDictator: incorrect step");
_;
currentStep++;
}
/**
* @notice Constructor required to ensure that the implementation of the SystemDictator is
* initialized upon deployment.
*/
constructor() {
ResourceMetering.ResourceConfig memory rcfg = Constants.DEFAULT_RESOURCE_CONFIG();
// Using this shorter variable as an alias for address(0) just prevents us from having to
// to use a new line for every single parameter.
address zero = address(0);
initialize(
DeployConfig(
GlobalConfig(AddressManager(zero), ProxyAdmin(zero), zero, zero),
ProxyAddressConfig(zero, zero, zero, zero, zero, zero, zero),
ImplementationAddressConfig(
L2OutputOracle(zero),
OptimismPortal(payable(zero)),
L1CrossDomainMessenger(zero),
L1StandardBridge(payable(zero)),
OptimismMintableERC20Factory(zero),
L1ERC721Bridge(zero),
PortalSender(zero),
SystemConfig(zero)
),
SystemConfigConfig(zero, 0, 0, bytes32(0), 0, zero, rcfg)
)
);
}
/**
* @param _config System configuration.
*/
function initialize(DeployConfig memory _config) public initializer {
config = _config;
currentStep = 1;
__Ownable_init();
_transferOwnership(config.globalConfig.controller);
}
/**
* @notice Allows the owner to update dynamic config.
*
* @param _l2OutputOracleDynamicConfig Dynamic L2OutputOracle config.
* @param _optimismPortalDynamicConfig Dynamic OptimismPortal config.
*/
function updateDynamicConfig(
L2OutputOracleDynamicConfig memory _l2OutputOracleDynamicConfig,
bool _optimismPortalDynamicConfig
) external onlyOwner {
l2OutputOracleDynamicConfig = _l2OutputOracleDynamicConfig;
optimismPortalDynamicConfig = _optimismPortalDynamicConfig;
dynamicConfigSet = true;
}
/**
* @notice Configures the ProxyAdmin contract.
*/
function step1() public onlyOwner step(1) {
// Set the AddressManager in the ProxyAdmin.
config.globalConfig.proxyAdmin.setAddressManager(config.globalConfig.addressManager);
// Set the L1CrossDomainMessenger to the RESOLVED proxy type.
config.globalConfig.proxyAdmin.setProxyType(
config.proxyAddressConfig.l1CrossDomainMessengerProxy,
ProxyAdmin.ProxyType.RESOLVED
);
// Set the implementation name for the L1CrossDomainMessenger.
config.globalConfig.proxyAdmin.setImplementationName(
config.proxyAddressConfig.l1CrossDomainMessengerProxy,
"OVM_L1CrossDomainMessenger"
);
// Set the L1StandardBridge to the CHUGSPLASH proxy type.
config.globalConfig.proxyAdmin.setProxyType(
config.proxyAddressConfig.l1StandardBridgeProxy,
ProxyAdmin.ProxyType.CHUGSPLASH
);
// Upgrade and initialize the SystemConfig so the Sequencer can start up.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.systemConfigProxy),
address(config.implementationAddressConfig.systemConfigImpl),
abi.encodeCall(
SystemConfig.initialize,
(
config.systemConfigConfig.owner,
config.systemConfigConfig.overhead,
config.systemConfigConfig.scalar,
config.systemConfigConfig.batcherHash,
config.systemConfigConfig.gasLimit,
config.systemConfigConfig.unsafeBlockSigner,
config.systemConfigConfig.resourceConfig
)
)
);
}
/**
* @notice Pauses the system by shutting down the L1CrossDomainMessenger and setting the
* deposit halt flag to tell the Sequencer's DTL to stop accepting deposits.
*/
function step2() public onlyOwner step(2) {
// Store the address of the old L1CrossDomainMessenger implementation. We will need this
// address in the case that we have to exit early.
oldL1CrossDomainMessenger = config.globalConfig.addressManager.getAddress(
"OVM_L1CrossDomainMessenger"
);
// Temporarily brick the L1CrossDomainMessenger by setting its implementation address to
// address(0) which will cause the ResolvedDelegateProxy to revert. Better than pausing
// the L1CrossDomainMessenger via pause() because it can be easily reverted.
config.globalConfig.addressManager.setAddress("OVM_L1CrossDomainMessenger", address(0));
// Set the DTL shutoff block, which will tell the DTL to stop syncing new deposits from the
// CanonicalTransactionChain. We do this by setting an address in the AddressManager
// because the DTL already has a reference to the AddressManager and this way we don't also
// need to give it a reference to the SystemDictator.
config.globalConfig.addressManager.setAddress(
"DTL_SHUTOFF_BLOCK",
address(uint160(block.number))
);
}
/**
* @notice Removes deprecated addresses from the AddressManager.
*/
function step3() public onlyOwner step(EXIT_1_NO_RETURN_STEP) {
// Remove all deprecated addresses from the AddressManager
string[17] memory deprecated = [
"OVM_CanonicalTransactionChain",
"OVM_L2CrossDomainMessenger",
"OVM_DecompressionPrecompileAddress",
"OVM_Sequencer",
"OVM_Proposer",
"OVM_ChainStorageContainer-CTC-batches",
"OVM_ChainStorageContainer-CTC-queue",
"OVM_CanonicalTransactionChain",
"OVM_StateCommitmentChain",
"OVM_BondManager",
"OVM_ExecutionManager",
"OVM_FraudVerifier",
"OVM_StateManagerFactory",
"OVM_StateTransitionerFactory",
"OVM_SafetyChecker",
"OVM_L1MultiMessageRelayer",
"BondManager"
];
for (uint256 i = 0; i < deprecated.length; i++) {
config.globalConfig.addressManager.setAddress(deprecated[i], address(0));
}
}
/**
* @notice Transfers system ownership to the ProxyAdmin.
*/
function step4() public onlyOwner step(PROXY_TRANSFER_STEP) {
// Transfer ownership of the AddressManager to the ProxyAdmin.
config.globalConfig.addressManager.transferOwnership(
address(config.globalConfig.proxyAdmin)
);
// Transfer ownership of the L1StandardBridge to the ProxyAdmin.
L1ChugSplashProxy(payable(config.proxyAddressConfig.l1StandardBridgeProxy)).setOwner(
address(config.globalConfig.proxyAdmin)
);
// Transfer ownership of the L1ERC721Bridge to the ProxyAdmin.
Proxy(payable(config.proxyAddressConfig.l1ERC721BridgeProxy)).changeAdmin(
address(config.globalConfig.proxyAdmin)
);
}
/**
* @notice Upgrades and initializes proxy contracts.
*/
function step5() public onlyOwner step(5) {
// Dynamic config must be set before we can initialize the L2OutputOracle.
require(dynamicConfigSet, "SystemDictator: dynamic oracle config is not yet initialized");
// Upgrade and initialize the L2OutputOracle.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.l2OutputOracleProxy),
address(config.implementationAddressConfig.l2OutputOracleImpl),
abi.encodeCall(
L2OutputOracle.initialize,
(
l2OutputOracleDynamicConfig.l2OutputOracleStartingBlockNumber,
l2OutputOracleDynamicConfig.l2OutputOracleStartingTimestamp
)
)
);
// Upgrade and initialize the OptimismPortal.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.optimismPortalProxy),
address(config.implementationAddressConfig.optimismPortalImpl),
abi.encodeCall(OptimismPortal.initialize, (optimismPortalDynamicConfig))
);
// Upgrade the L1CrossDomainMessenger.
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.l1CrossDomainMessengerProxy),
address(config.implementationAddressConfig.l1CrossDomainMessengerImpl)
);
// Try to initialize the L1CrossDomainMessenger, only fail if it's already been initialized.
try
L1CrossDomainMessenger(config.proxyAddressConfig.l1CrossDomainMessengerProxy)
.initialize()
{
// L1CrossDomainMessenger is the one annoying edge case difference between existing
// networks and fresh networks because in existing networks it'll already be
// initialized but in fresh networks it won't be. Try/catch is the easiest and most
// consistent way to handle this because initialized() is not exposed publicly.
} catch Error(string memory reason) {
require(
keccak256(abi.encodePacked(reason)) ==
keccak256("Initializable: contract is already initialized"),
string.concat("SystemDictator: unexpected error initializing L1XDM: ", reason)
);
} catch {
revert("SystemDictator: unexpected error initializing L1XDM (no reason)");
}
// Transfer ETH from the L1StandardBridge to the OptimismPortal.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.l1StandardBridgeProxy),
address(config.implementationAddressConfig.portalSenderImpl),
abi.encodeCall(PortalSender.donate, ())
);
// Upgrade the L1StandardBridge (no initializer).
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.l1StandardBridgeProxy),
address(config.implementationAddressConfig.l1StandardBridgeImpl)
);
// Upgrade the OptimismMintableERC20Factory (no initializer).
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.optimismMintableERC20FactoryProxy),
address(config.implementationAddressConfig.optimismMintableERC20FactoryImpl)
);
// Upgrade the L1ERC721Bridge (no initializer).
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.l1ERC721BridgeProxy),
address(config.implementationAddressConfig.l1ERC721BridgeImpl)
);
}
/**
* @notice Calls the first 2 steps of the migration process.
*/
function phase1() external onlyOwner {
step1();
step2();
}
/**
* @notice Calls the remaining steps of the migration process, and finalizes.
*/
function phase2() external onlyOwner {
step3();
step4();
step5();
finalize();
}
/**
* @notice Tranfers admin ownership to the final owner.
*/
function finalize() public onlyOwner {
// Transfer ownership of the ProxyAdmin to the final owner.
config.globalConfig.proxyAdmin.transferOwnership(config.globalConfig.finalOwner);
// Optionally also transfer AddressManager and L1StandardBridge if we still own it. Might
// happen if we're exiting early.
if (currentStep <= PROXY_TRANSFER_STEP) {
// Transfer ownership of the AddressManager to the final owner.
config.globalConfig.addressManager.transferOwnership(
address(config.globalConfig.finalOwner)
);
// Transfer ownership of the L1StandardBridge to the final owner.
L1ChugSplashProxy(payable(config.proxyAddressConfig.l1StandardBridgeProxy)).setOwner(
address(config.globalConfig.finalOwner)
);
// Transfer ownership of the L1ERC721Bridge to the final owner.
Proxy(payable(config.proxyAddressConfig.l1ERC721BridgeProxy)).changeAdmin(
address(config.globalConfig.finalOwner)
);
}
// Mark the deployment as finalized.
finalized = true;
}
/**
* @notice First exit point, can only be called before step 3 is executed.
*/
function exit1() external onlyOwner {
require(
currentStep == EXIT_1_NO_RETURN_STEP,
"SystemDictator: can only exit1 before step 3 is executed"
);
// Reset the L1CrossDomainMessenger to the old implementation.
config.globalConfig.addressManager.setAddress(
"OVM_L1CrossDomainMessenger",
oldL1CrossDomainMessenger
);
// Unset the DTL shutoff block which will allow the DTL to sync again.
config.globalConfig.addressManager.setAddress("DTL_SHUTOFF_BLOCK", address(0));
// Mark the deployment as exited.
exited = true;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "../libraries/DisputeTypes.sol";
import { SafeCall } from "../libraries/SafeCall.sol";
import { IDisputeGame } from "./interfaces/IDisputeGame.sol";
import { IDisputeGameFactory } from "./interfaces/IDisputeGameFactory.sol";
import { IBondManager } from "./interfaces/IBondManager.sol";
/**
* @title BondManager
* @notice The Bond Manager serves as an escrow for permissionless output proposal bonds.
*/
contract BondManager is IBondManager {
/**
* @notice The Bond Type
*/
struct Bond {
address owner;
bytes32 id;
uint128 expiration;
uint128 amount;
}
/**
* @notice The permissioned dispute game factory.
* @dev Used to verify the status of bonds.
*/
IDisputeGameFactory public immutable DISPUTE_GAME_FACTORY;
/**
* @notice Amount of gas used to transfer ether when splitting the bond.
* This is a reasonable amount of gas for a transfer, even to a smart contract.
* The number of participants is bound of by the block gas limit.
*/
uint256 private constant TRANSFER_GAS = 30_000;
/**
* @notice Mapping from bondId to bond.
*/
mapping(bytes32 => Bond) public bonds;
/**
* @notice Instantiates the bond maanger with the registered dispute game factory.
* @param _disputeGameFactory is the dispute game factory.
*/
constructor(IDisputeGameFactory _disputeGameFactory) {
DISPUTE_GAME_FACTORY = _disputeGameFactory;
}
/**
* @inheritdoc IBondManager
*/
function post(
bytes32 _bondId,
address _bondOwner,
uint128 _minClaimHold
) external payable {
require(bonds[_bondId].owner == address(0), "BondManager: BondId already posted.");
require(_bondOwner != address(0), "BondManager: Owner cannot be the zero address.");
require(msg.value > 0, "BondManager: Value must be non-zero.");
uint128 expiration = uint128(_minClaimHold + block.timestamp);
bonds[_bondId] = Bond({
owner: _bondOwner,
id: _bondId,
expiration: expiration,
amount: uint128(msg.value)
});
emit BondPosted(_bondId, _bondOwner, expiration, msg.value);
}
/**
* @inheritdoc IBondManager
*/
function seize(bytes32 _bondId) external {
Bond memory b = bonds[_bondId];
require(b.owner != address(0), "BondManager: The bond does not exist.");
require(b.expiration >= block.timestamp, "BondManager: Bond expired.");
IDisputeGame caller = IDisputeGame(msg.sender);
IDisputeGame game = DISPUTE_GAME_FACTORY.games(
GameType.ATTESTATION,
caller.rootClaim(),
caller.extraData()
);
require(msg.sender == address(game), "BondManager: Unauthorized seizure.");
require(game.status() == GameStatus.CHALLENGER_WINS, "BondManager: Game incomplete.");
delete bonds[_bondId];
emit BondSeized(_bondId, b.owner, msg.sender, b.amount);
bool success = SafeCall.send(payable(msg.sender), gasleft(), b.amount);
require(success, "BondManager: Failed to send Ether.");
}
/**
* @inheritdoc IBondManager
*/
function seizeAndSplit(bytes32 _bondId, address[] calldata _claimRecipients) external {
Bond memory b = bonds[_bondId];
require(b.owner != address(0), "BondManager: The bond does not exist.");
require(b.expiration >= block.timestamp, "BondManager: Bond expired.");
IDisputeGame caller = IDisputeGame(msg.sender);
IDisputeGame game = DISPUTE_GAME_FACTORY.games(
GameType.ATTESTATION,
caller.rootClaim(),
caller.extraData()
);
require(msg.sender == address(game), "BondManager: Unauthorized seizure.");
require(game.status() == GameStatus.CHALLENGER_WINS, "BondManager: Game incomplete.");
delete bonds[_bondId];
emit BondSeized(_bondId, b.owner, msg.sender, b.amount);
uint256 len = _claimRecipients.length;
uint256 proportionalAmount = b.amount / len;
// Send the proportional amount to each recipient. Do not revert if a send fails as that
// will prevent other recipients from receiving their share.
for (uint256 i; i < len; i++) {
SafeCall.send({
_target: payable(_claimRecipients[i]),
_gas: TRANSFER_GAS,
_value: proportionalAmount
});
}
}
/**
* @inheritdoc IBondManager
*/
function reclaim(bytes32 _bondId) external {
Bond memory b = bonds[_bondId];
require(b.owner == msg.sender, "BondManager: Unauthorized claimant.");
require(b.expiration <= block.timestamp, "BondManager: Bond isn't claimable yet.");
delete bonds[_bondId];
emit BondReclaimed(_bondId, msg.sender, b.amount);
bool success = SafeCall.send(payable(msg.sender), gasleft(), b.amount);
require(success, "BondManager: Failed to send Ether.");
}
}
...@@ -4,17 +4,20 @@ pragma solidity ^0.8.15; ...@@ -4,17 +4,20 @@ pragma solidity ^0.8.15;
import "../libraries/DisputeTypes.sol"; import "../libraries/DisputeTypes.sol";
import "../libraries/DisputeErrors.sol"; import "../libraries/DisputeErrors.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ClonesWithImmutableArgs } from "@cwia/ClonesWithImmutableArgs.sol"; import { ClonesWithImmutableArgs } from "@cwia/ClonesWithImmutableArgs.sol";
import {
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.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 { IVersioned } from "./interfaces/IVersioned.sol";
/** /**
* @title DisputeGameFactory * @title DisputeGameFactory
* @notice A factory contract for creating `IDisputeGame` contracts. * @notice A factory contract for creating `IDisputeGame` contracts.
*/ */
contract DisputeGameFactory is Ownable, IDisputeGameFactory { contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersioned {
/** /**
* @dev Allows for the creation of clone proxies with immutable arguments. * @dev Allows for the creation of clone proxies with immutable arguments.
*/ */
...@@ -32,12 +35,41 @@ contract DisputeGameFactory is Ownable, IDisputeGameFactory { ...@@ -32,12 +35,41 @@ contract DisputeGameFactory is Ownable, IDisputeGameFactory {
*/ */
mapping(Hash => IDisputeGame) internal disputeGames; mapping(Hash => IDisputeGame) internal disputeGames;
/**
* @notice An append-only array of disputeGames that have been created.
* @dev This accessor is used by offchain game solvers to efficiently
* track dispute games
*/
IDisputeGame[] public disputeGameList;
/** /**
* @notice Constructs a new DisputeGameFactory contract. * @notice Constructs a new DisputeGameFactory contract.
*/
constructor() OwnableUpgradeable() {
initialize(address(0));
}
/**
* @notice Initializes the contract.
* @param _owner The owner of the contract. * @param _owner The owner of the contract.
*/ */
constructor(address _owner) Ownable() { function initialize(address _owner) public initializer {
transferOwnership(_owner); __Ownable_init();
_transferOwnership(_owner);
}
/**
* @inheritdoc IVersioned
*/
function version() external pure returns (string memory) {
return "0.0.1";
}
/**
* @inheritdoc IDisputeGameFactory
*/
function gameCount() external view returns (uint256 _gameCount) {
_gameCount = disputeGameList.length;
} }
/** /**
...@@ -81,6 +113,7 @@ contract DisputeGameFactory is Ownable, IDisputeGameFactory { ...@@ -81,6 +113,7 @@ contract DisputeGameFactory is Ownable, IDisputeGameFactory {
// Store the dispute game in the mapping & emit the `DisputeGameCreated` event. // Store the dispute game in the mapping & emit the `DisputeGameCreated` event.
disputeGames[uuid] = proxy; disputeGames[uuid] = proxy;
disputeGameList.push(proxy);
emit DisputeGameCreated(address(proxy), gameType, rootClaim); emit DisputeGameCreated(address(proxy), gameType, rootClaim);
} }
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { IDisputeGame } from "./interfaces/IDisputeGame.sol";
import { IVersioned } from "./interfaces/IVersioned.sol";
import { IFaultDisputeGame } from "./interfaces/IFaultDisputeGame.sol";
import { IInitializable } from "./interfaces/IInitializable.sol";
import { IBondManager } from "./interfaces/IBondManager.sol";
import { Clone } from "../libraries/Clone.sol";
import { LibHashing } from "./lib/LibHashing.sol";
import { LibPosition } from "./lib/LibPosition.sol";
import { LibClock } from "./lib/LibClock.sol";
import "../libraries/DisputeTypes.sol";
import "../libraries/DisputeErrors.sol";
/**
* @title FaultDisputeGame
* @notice An implementation of the `IFaultDisputeGame` interface.
*/
contract FaultDisputeGame is IFaultDisputeGame, Clone {
////////////////////////////////////////////////////////////////
// State Vars //
////////////////////////////////////////////////////////////////
/**
* @notice The current Semver of the FaultDisputeGame implementation.
*/
string internal constant VERSION = "0.0.1";
/**
* @notice The max depth of the game.
*/
uint256 internal constant MAX_GAME_DEPTH = 63;
/**
* @notice The duration of the game.
* @dev TODO: Account for resolution buffer. (?)
*/
Duration internal constant GAME_DURATION = Duration.wrap(7 days);
/**
* @notice The root claim's position is always at depth 0; index 0.
*/
Position internal constant ROOT_POSITION = Position.wrap(0);
/**
* @notice The starting timestamp of the game
*/
Timestamp public gameStart;
/**
* @inheritdoc IDisputeGame
*/
GameStatus public status;
/**
* @inheritdoc IDisputeGame
*/
IBondManager public bondManager;
/**
* @notice An append-only array of all claims made during the dispute game.
*/
ClaimData[] public claimData;
/**
* @notice An internal mapping to allow for constant-time lookups of existing claims.
*/
mapping(ClaimHash => bool) internal claims;
////////////////////////////////////////////////////////////////
// External Logic //
////////////////////////////////////////////////////////////////
/**
* @inheritdoc IFaultDisputeGame
*/
function attack(uint256 _parentIndex, Claim _pivot) external payable {
_move(_parentIndex, _pivot, true);
}
/**
* @inheritdoc IFaultDisputeGame
*/
function defend(uint256 _parentIndex, Claim _pivot) external payable {
_move(_parentIndex, _pivot, false);
}
/**
* @inheritdoc IFaultDisputeGame
*/
function step(
uint256 _prestateIndex,
uint256 _parentIndex,
bytes calldata _stateData,
bytes calldata _proof
) external {
// TODO - Call the VM to perform the execution step.
}
////////////////////////////////////////////////////////////////
// Internal Logic //
////////////////////////////////////////////////////////////////
/**
* @notice Internal move function, used by both `attack` and `defend`.
* @param _challengeIndex The index of the claim being moved against.
* @param _pivot The claim at the next logical position in the game.
* @param _isAttack Whether or not the move is an attack or defense.
*/
function _move(
uint256 _challengeIndex,
Claim _pivot,
bool _isAttack
) internal {
// Moves cannot be made unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) {
revert GameNotInProgress();
}
// The only move that can be made against a root claim is an attack. This is because the
// root claim commits to the entire state; Therefore, the only valid defense is to do
// nothing if it is agreed with.
if (_challengeIndex == 0 && !_isAttack) {
revert CannotDefendRootClaim();
}
// Get the parent. If it does not exist, the call will revert with OOB.
ClaimData memory parent = claimData[_challengeIndex];
// Set the parent claim as countered.
claimData[_challengeIndex].countered = true;
// Compute the position that the claim commits to. Because the parent's position is already
// known, we can compute the next position by moving left or right depending on whether
// or not the move is an attack or defense.
Position nextPosition = _isAttack
? LibPosition.attack(parent.position)
: LibPosition.defend(parent.position);
// At the leaf nodes of the game, the only option is to run a step to prove or disprove
// the above claim. At this depth, the parent claim commits to the state after a single
// instruction step.
if (LibPosition.depth(nextPosition) >= MAX_GAME_DEPTH) {
revert GameDepthExceeded();
}
// Fetch the grandparent clock, if it exists.
// The grandparent clock should always exist unless the parent is the root claim.
Clock grandparentClock;
if (parent.parentIndex != type(uint32).max) {
grandparentClock = claimData[parent.parentIndex].clock;
}
// Compute the duration of the next clock. This is done by adding the duration of the
// grandparent claim to the difference between the current block timestamp and the
// parent's clock timestamp.
Duration nextDuration = Duration.wrap(
uint64(
// First, fetch the duration of the grandparent claim.
Duration.unwrap(LibClock.duration(grandparentClock)) +
// Second, add the difference between the current block timestamp and the
// parent's clock timestamp.
block.timestamp -
Timestamp.unwrap(LibClock.timestamp(parent.clock))
)
);
// Enforce the clock time rules. If the new clock duration is greater than half of the game
// duration, then the move is invalid and cannot be made.
if (Duration.unwrap(nextDuration) > Duration.unwrap(GAME_DURATION) >> 1) {
revert ClockTimeExceeded();
}
// Construct the next clock with the new duration and the current block timestamp.
Clock nextClock = LibClock.wrap(nextDuration, Timestamp.wrap(uint64(block.timestamp)));
// Do not allow for a duplicate claim to be made.
ClaimHash claimHash = LibHashing.hashClaimPos(_pivot, nextPosition);
if (claims[claimHash]) {
revert ClaimAlreadyExists();
}
claims[claimHash] = true;
// Create the new claim.
claimData.push(
ClaimData({
parentIndex: uint32(_challengeIndex),
claim: _pivot,
position: nextPosition,
clock: nextClock,
countered: false
})
);
// Emit the appropriate event for the attack or defense.
emit Move(_challengeIndex, _pivot, msg.sender);
}
////////////////////////////////////////////////////////////////
// `IDisputeGame` impl //
////////////////////////////////////////////////////////////////
/**
* @inheritdoc IDisputeGame
*/
function gameType() public pure override returns (GameType gameType_) {
gameType_ = GameTypes.FAULT;
}
/**
* @inheritdoc IDisputeGame
*/
function createdAt() external view returns (Timestamp createdAt_) {
createdAt_ = gameStart;
}
/**
* @inheritdoc IDisputeGame
*/
function resolve() external returns (GameStatus status_) {
// TODO - Resolve the game
status = GameStatus.IN_PROGRESS;
status_ = status;
}
/**
* @inheritdoc IDisputeGame
*/
function rootClaim() public pure returns (Claim rootClaim_) {
rootClaim_ = Claim.wrap(_getArgFixedBytes(0x00));
}
/**
* @inheritdoc IDisputeGame
*/
function extraData() public pure returns (bytes memory extraData_) {
// The extra data starts at the second word within the cwia calldata.
// TODO: What data do we need to pass along to this contract from the factory?
// Block hash, preimage data, etc.?
extraData_ = _getArgDynBytes(0x20, 0x20);
}
/**
* @inheritdoc IDisputeGame
*/
function gameData()
external
pure
returns (
GameType gameType_,
Claim rootClaim_,
bytes memory extraData_
)
{
gameType_ = gameType();
rootClaim_ = rootClaim();
extraData_ = extraData();
}
/**
* @inheritdoc IInitializable
*/
function initialize() external {
// Set the game start
gameStart = Timestamp.wrap(uint64(block.timestamp));
// Set the game status
status = GameStatus.IN_PROGRESS;
// Set the root claim
claimData.push(
ClaimData({
parentIndex: type(uint32).max,
claim: rootClaim(),
position: ROOT_POSITION,
clock: LibClock.wrap(Duration.wrap(0), Timestamp.wrap(uint64(block.timestamp))),
countered: false
})
);
}
/**
* @inheritdoc IVersioned
*/
function version() external pure override returns (string memory version_) {
version_ = VERSION;
}
}
...@@ -20,44 +20,44 @@ interface IDisputeGame is IInitializable, IVersioned { ...@@ -20,44 +20,44 @@ interface IDisputeGame is IInitializable, IVersioned {
/** /**
* @notice Returns the timestamp that the DisputeGame contract was created at. * @notice Returns the timestamp that the DisputeGame contract was created at.
* @return _createdAt The timestamp that the DisputeGame contract was created at. * @return createdAt_ The timestamp that the DisputeGame contract was created at.
*/ */
function createdAt() external view returns (Timestamp _createdAt); function createdAt() external view returns (Timestamp createdAt_);
/** /**
* @notice Returns the current status of the game. * @notice Returns the current status of the game.
* @return _status The current status of the game. * @return status_ The current status of the game.
*/ */
function status() external view returns (GameStatus _status); function status() external view returns (GameStatus status_);
/** /**
* @notice Getter for the game type. * @notice Getter for the game type.
* @dev `clones-with-immutable-args` argument #1 * @dev `clones-with-immutable-args` argument #1
* @dev The reference impl should be entirely different depending on the type (fault, validity) * @dev The reference impl should be entirely different depending on the type (fault, validity)
* i.e. The game type should indicate the security model. * i.e. The game type should indicate the security model.
* @return _gameType The type of proof system being used. * @return gameType_ The type of proof system being used.
*/ */
function gameType() external pure returns (GameType _gameType); function gameType() external pure returns (GameType gameType_);
/** /**
* @notice Getter for the root claim. * @notice Getter for the root claim.
* @dev `clones-with-immutable-args` argument #2 * @dev `clones-with-immutable-args` argument #2
* @return _rootClaim The root claim of the DisputeGame. * @return rootClaim_ The root claim of the DisputeGame.
*/ */
function rootClaim() external pure returns (Claim _rootClaim); function rootClaim() external pure returns (Claim rootClaim_);
/** /**
* @notice Getter for the extra data. * @notice Getter for the extra data.
* @dev `clones-with-immutable-args` argument #3 * @dev `clones-with-immutable-args` argument #3
* @return _extraData Any extra data supplied to the dispute game contract by the creator. * @return extraData_ Any extra data supplied to the dispute game contract by the creator.
*/ */
function extraData() external pure returns (bytes memory _extraData); function extraData() external pure returns (bytes memory extraData_);
/** /**
* @notice Returns the address of the `BondManager` used. * @notice Returns the address of the `BondManager` used.
* @return _bondManager The address of the `BondManager` used. * @return bondManager_ The address of the `BondManager` used.
*/ */
function bondManager() external view returns (IBondManager _bondManager); function bondManager() external view returns (IBondManager bondManager_);
/** /**
* @notice If all necessary information has been gathered, this function should mark the game * @notice If all necessary information has been gathered, this function should mark the game
...@@ -65,22 +65,25 @@ interface IDisputeGame is IInitializable, IVersioned { ...@@ -65,22 +65,25 @@ interface IDisputeGame is IInitializable, IVersioned {
* the resolved game. It is at this stage that the bonds should be awarded to the * the resolved game. It is at this stage that the bonds should be awarded to the
* necessary parties. * necessary parties.
* @dev May only be called if the `status` is `IN_PROGRESS`. * @dev May only be called if the `status` is `IN_PROGRESS`.
* @return _status The status of the game after resolution. * @return status_ The status of the game after resolution.
*/ */
function resolve() external returns (GameStatus _status); function resolve() external returns (GameStatus status_);
/** /**
* @notice A compliant implementation of this interface should return the components of the * @notice A compliant implementation of this interface should return the components of the
* game UUID's preimage provided in the cwia payload. The preimage of the UUID is * game UUID's preimage provided in the cwia payload. The preimage of the UUID is
* constructed as `keccak256(gameType . rootClaim . extraData)` where `.` denotes * constructed as `keccak256(gameType . rootClaim . extraData)` where `.` denotes
* concatenation. * concatenation.
* @return gameType_ The type of proof system being used.
* @return rootClaim_ The root claim of the DisputeGame.
* @return extraData_ Any extra data supplied to the dispute game contract by the creator.
*/ */
function gameData() function gameData()
external external
pure pure
returns ( returns (
GameType _gameType, GameType gameType_,
Claim _rootClaim, Claim rootClaim_,
bytes memory _extraData bytes memory extraData_
); );
} }
...@@ -29,6 +29,12 @@ interface IDisputeGameFactory { ...@@ -29,6 +29,12 @@ 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.
* @return _gameCount The total number of dispute games created by this factory.
*/
function gameCount() external view returns (uint256 _gameCount);
/** /**
* @notice `games` queries an internal a mapping that maps the hash of * @notice `games` queries an internal a mapping that maps the hash of
* `gameType ++ rootClaim ++ extraData` to the deployed `DisputeGame` clone. * `gameType ++ rootClaim ++ extraData` to the deployed `DisputeGame` clone.
......
...@@ -11,85 +11,39 @@ import { IDisputeGame } from "./IDisputeGame.sol"; ...@@ -11,85 +11,39 @@ import { IDisputeGame } from "./IDisputeGame.sol";
*/ */
interface IFaultDisputeGame is IDisputeGame { interface IFaultDisputeGame is IDisputeGame {
/** /**
* @notice Emitted when a subclaim is disagreed upon by `claimant` * @notice The `ClaimData` struct represents the data associated with a Claim.
* @dev Disagreeing with a subclaim is akin to attacking it. * @dev TODO: Pack `Clock` and `Position` into the same slot. Should require 4 64 bit arms.
* @param claimHash The unique ClaimHash that is being disagreed upon * @dev TODO: Add bond ID information.
* @param pivot The claim for the following pivot (disagreement = go left)
* @param claimant The address of the claimant
*/ */
event Attack(ClaimHash indexed claimHash, Claim indexed pivot, address indexed claimant); struct ClaimData {
uint32 parentIndex;
bool countered;
Claim claim;
Position position;
Clock clock;
}
/** /**
* @notice Emitted when a subclaim is agreed upon by `claimant` * @notice Emitted when a new claim is added to the DAG by `claimant`
* @dev Agreeing with a subclaim is akin to defending it. * @param parentIndex The index within the `claimData` array of the parent claim
* @param claimHash The unique ClaimHash that is being agreed upon * @param pivot The claim being added
* @param pivot The claim for the following pivot (agreement = go right)
* @param claimant The address of the claimant * @param claimant The address of the claimant
*/ */
event Defend(ClaimHash indexed claimHash, Claim indexed pivot, address indexed claimant); event Move(uint256 indexed parentIndex, Claim indexed pivot, address indexed claimant);
/**
* @notice Maps a unique ClaimHash to a Claim.
* @param claimHash The unique ClaimHash
* @return claim The Claim associated with the ClaimHash
*/
function claims(ClaimHash claimHash) external view returns (Claim claim);
/**
* @notice Maps a unique ClaimHash to its parent.
* @param claimHash The unique ClaimHash
* @return parent The parent ClaimHash of the passed ClaimHash
*/
function parents(ClaimHash claimHash) external view returns (ClaimHash parent);
/**
* @notice Maps a unique ClaimHash to its Position.
* @param claimHash The unique ClaimHash
* @return position The Position associated with the ClaimHash
*/
function positions(ClaimHash claimHash) external view returns (Position position);
/**
* @notice Maps a unique ClaimHash to a Bond.
* @param claimHash The unique ClaimHash
* @return bond The Bond associated with the ClaimHash
*/
function bonds(ClaimHash claimHash) external view returns (BondAmount bond);
/**
* @notice Maps a unique ClaimHash its chess clock.
* @param claimHash The unique ClaimHash
* @return clock The chess clock associated with the ClaimHash
*/
function clocks(ClaimHash claimHash) external view returns (Clock clock);
/**
* @notice Maps a unique ClaimHash to its reference counter.
* @param claimHash The unique ClaimHash
* @return _rc The reference counter associated with the ClaimHash
*/
function rc(ClaimHash claimHash) external view returns (uint64 _rc);
/**
* @notice Maps a unique ClaimHash to a boolean indicating whether or not it has been countered.
* @param claimHash The unique claimHash
* @return _countered Whether or not `claimHash` has been countered
*/
function countered(ClaimHash claimHash) external view returns (bool _countered);
/** /**
* @notice Disagree with a subclaim * @notice Attack a disagreed upon `Claim`.
* @param disagreement The ClaimHash of the disagreement * @param _parentIndex Index of the `Claim` to attack in `claimData`.
* @param pivot The claimed pivot * @param _pivot The `Claim` at the relative attack position.
*/ */
function attack(ClaimHash disagreement, Claim pivot) external; function attack(uint256 _parentIndex, Claim _pivot) external payable;
/** /**
* @notice Agree with a subclaim * @notice Defend an agreed upon `Claim`.
* @param agreement The ClaimHash of the agreement * @param _parentIndex Index of the claim to defend in `claimData`.
* @param pivot The claimed pivot * @param _pivot The `Claim` at the relative defense position.
*/ */
function defend(ClaimHash agreement, Claim pivot) external; function defend(uint256 _parentIndex, Claim _pivot) external payable;
/** /**
* @notice Perform the final step via an on-chain fault proof processor * @notice Perform the final step via an on-chain fault proof processor
...@@ -97,7 +51,15 @@ interface IFaultDisputeGame is IDisputeGame { ...@@ -97,7 +51,15 @@ interface IFaultDisputeGame is IDisputeGame {
* a step in the fault proof program on-chain. The interface of the fault proof * a step in the fault proof program on-chain. The interface of the fault proof
* processor contract should be generic enough such that we can use different * processor contract should be generic enough such that we can use different
* fault proof VMs (MIPS, RiscV5, etc.) * fault proof VMs (MIPS, RiscV5, etc.)
* @param disagreement The ClaimHash of the disagreement * @param _prestateIndex The index of the prestate of the step within `claimData`.
* @param _parentIndex The index of the parent claim within `claimData`.
* @param _stateData The stateData of the step is the preimage of the claim @ `prestateIndex`
* @param _proof Proof to access memory leaf nodes in the VM.
*/ */
function step(ClaimHash disagreement) external; function step(
uint256 _prestateIndex,
uint256 _parentIndex,
bytes calldata _stateData,
bytes calldata _proof
) external;
} }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "../../libraries/DisputeTypes.sol";
/**
* @title LibClock
* @notice This library contains helper functions for working with the `Clock` type.
*/
library LibClock {
/**
* @notice Packs a `Duration` and `Timestamp` into a `Clock` type.
* @param _duration The `Duration` to pack into the `Clock` type.
* @param _timestamp The `Timestamp` to pack into the `Clock` type.
* @return clock_ The `Clock` containing the `_duration` and `_timestamp`.
*/
function wrap(Duration _duration, Timestamp _timestamp) internal pure returns (Clock clock_) {
assembly {
clock_ := or(shl(0x40, _duration), _timestamp)
}
}
/**
* @notice Pull the `Duration` out of a `Clock` type.
* @param _clock The `Clock` type to pull the `Duration` out of.
* @return duration_ The `Duration` pulled out of `_clock`.
*/
function duration(Clock _clock) internal pure returns (Duration duration_) {
// Shift the high-order 64 bits into the low-order 64 bits, leaving only the `duration`.
assembly {
duration_ := shr(0x40, _clock)
}
}
/**
* @notice Pull the `Timestamp` out of a `Clock` type.
* @param _clock The `Clock` type to pull the `Timestamp` out of.
* @return timestamp_ The `Timestamp` pulled out of `_clock`.
*/
function timestamp(Clock _clock) internal pure returns (Timestamp timestamp_) {
// Clean the high-order 192 bits by shifting the clock left and then right again, leaving
// only the `timestamp`.
assembly {
timestamp_ := shr(0xC0, shl(0xC0, _clock))
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "../../libraries/DisputeTypes.sol";
/**
* @title Hashing
* @notice This library contains all of the hashing utilities used in the Cannon contracts.
*/
library LibHashing {
/**
* @notice Hashes a claim and a position together.
* @param _claim A Claim type.
* @param _position The position of `claim`.
* @return claimHash_ A hash of abi.encodePacked(claim, position);
*/
function hashClaimPos(Claim _claim, Position _position) internal pure returns (ClaimHash claimHash_) {
assembly {
mstore(0x00, _claim)
mstore(0x20, _position)
claimHash_ := keccak256(0x00, 0x40)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "../../libraries/DisputeTypes.sol";
/**
* @title LibPosition
* @notice This library contains helper functions for working with the `Position` type.
*/
library LibPosition {
function wrap(uint64 _depth, uint64 _indexAtDepth) internal pure returns (Position position_) {
assembly {
position_ := or(shl(0x40, _depth), _indexAtDepth)
}
}
/**
* @notice Pulls the `depth` out of a packed `Position` type.
* @param _position The position to get the `depth` of.
* @return depth_ The `depth` of the `position`.
*/
function depth(Position _position) internal pure returns (uint64 depth_) {
// Shift the high-order 64 bits into the low-order 64 bits, leaving only the `depth`.
assembly {
depth_ := shr(0x40, _position)
}
}
/**
* @notice Pulls the `indexAtDepth` out of a packed `Position` type.
* @param _position The position to get the `indexAtDepth` of.
* @return indexAtDepth_ The `indexAtDepth` of the `position`.
*/
function indexAtDepth(Position _position) internal pure returns (uint64 indexAtDepth_) {
// Clean the high-order 192 bits by shifting the position left and then right again, leaving
// only the `indexAtDepth`.
assembly {
indexAtDepth_ := shr(0xC0, shl(0xC0, _position))
}
}
/**
* @notice Get the position to the left of `position`.
* @param _position The position to get the left position of.
* @return left_ The position to the left of `position`.
*/
function left(Position _position) internal pure returns (Position left_) {
uint64 _depth = depth(_position);
uint64 _indexAtDepth = indexAtDepth(_position);
// Left = { depth: position.depth + 1, indexAtDepth: position.indexAtDepth * 2 }
assembly {
left_ := or(shl(0x40, add(_depth, 0x01)), shl(0x01, _indexAtDepth))
}
}
/**
* @notice Get the position to the right of `position`.
* @param _position The position to get the right position of.
* @return right_ The position to the right of `position`.
*/
function right(Position _position) internal pure returns (Position right_) {
uint64 _depth = depth(_position);
uint64 _indexAtDepth = indexAtDepth(_position);
// Right = { depth: position.depth + 1, indexAtDepth: position.indexAtDepth * 2 + 1 }
assembly {
right_ := or(shl(0x40, add(_depth, 0x01)), add(shl(0x01, _indexAtDepth), 0x01))
}
}
/**
* @notice Get the parent position of `position`.
* @param _position The position to get the parent position of.
* @return parent_ The parent position of `position`.
*/
function parent(Position _position) internal pure returns (Position parent_) {
uint64 _depth = depth(_position);
uint64 _indexAtDepth = indexAtDepth(_position);
// Parent = { depth: position.depth - 1, indexAtDepth: position.indexAtDepth / 2 }
assembly {
parent_ := or(shl(0x40, sub(_depth, 0x01)), shr(0x01, _indexAtDepth))
}
}
/**
* @notice Get the deepest, right most index relative to the `position`.
* @param _position The position to get the relative deepest, right most index of.
* @param _maxDepth The maximum depth of the game.
* @return rightIndex_ The deepest, right most index relative to the `position`.
*/
function rightIndex(Position _position, uint256 _maxDepth) internal pure returns (uint64 rightIndex_) {
assembly {
rightIndex_ := shr(0xC0, shl(0xC0, _position))
// Walk down to the max depth by moving right
for { let i := shr(0x40, _position) } lt(i, sub(_maxDepth, 0x01)) { i := add(i, 0x01) } {
rightIndex_ := add(0x01, shl(0x01, rightIndex_))
}
}
}
/**
* @notice Get the attack position relative to `position`.
* @param _position The position to get the relative attack position of.
* @return attack_ The attack position relative to `position`.
*/
function attack(Position _position) internal pure returns (Position attack_) {
return left(_position);
}
/**
* @notice Get the defense position relative to `position`.
* @param _position The position to get the relative defense position of.
* @return defense_ The defense position relative to `position`.
*/
function defend(Position _position) internal pure returns (Position defense_) {
uint64 _depth = depth(_position);
uint64 _indexAtDepth = indexAtDepth(_position);
// Defend = { depth: position.depth + 1, indexAtDepth: ((position.indexAtDepth / 2) * 2 + 1) * 2 }
assembly {
defense_ := or(shl(0x40, add(_depth, 0x01)), shl(0x01, add(0x01, shl(0x01, shr(0x01, _indexAtDepth)))))
}
}
}
...@@ -112,20 +112,50 @@ library Bytes { ...@@ -112,20 +112,50 @@ library Bytes {
* @return Resulting nibble array. * @return Resulting nibble array.
*/ */
function toNibbles(bytes memory _bytes) internal pure returns (bytes memory) { function toNibbles(bytes memory _bytes) internal pure returns (bytes memory) {
uint256 bytesLength = _bytes.length; bytes memory _nibbles;
bytes memory nibbles = new bytes(bytesLength * 2); assembly {
bytes1 b; // Grab a free memory offset for the new array
_nibbles := mload(0x40)
for (uint256 i = 0; i < bytesLength; ) {
b = _bytes[i]; // Load the length of the passed bytes array from memory
nibbles[i * 2] = b >> 4; let bytesLength := mload(_bytes)
nibbles[i * 2 + 1] = b & 0x0f;
unchecked { // Calculate the length of the new nibble array
++i; // This is the length of the input array times 2
let nibblesLength := shl(0x01, bytesLength)
// Update the free memory pointer to allocate memory for the new array.
// To do this, we add the length of the new array + 32 bytes for the array length
// rounded up to the nearest 32 byte boundary to the current free memory pointer.
mstore(0x40, add(_nibbles, and(not(0x1F), add(nibblesLength, 0x3F))))
// Store the length of the new array in memory
mstore(_nibbles, nibblesLength)
// Store the memory offset of the _bytes array's contents on the stack
let bytesStart := add(_bytes, 0x20)
// Store the memory offset of the nibbles array's contents on the stack
let nibblesStart := add(_nibbles, 0x20)
// Loop through each byte in the input array
for {
let i := 0x00
} lt(i, bytesLength) {
i := add(i, 0x01)
} {
// Get the starting offset of the next 2 bytes in the nibbles array
let offset := add(nibblesStart, shl(0x01, i))
// Load the byte at the current index within the `_bytes` array
let b := byte(0x00, mload(add(bytesStart, i)))
// Pull out the first nibble and store it in the new array
mstore8(offset, shr(0x04, b))
// Pull out the second nibble and store it in the new array
mstore8(add(offset, 0x01), and(b, 0x0F))
} }
} }
return _nibbles;
return nibbles;
} }
/** /**
......
...@@ -39,6 +39,27 @@ error CannotDefendRootClaim(); ...@@ -39,6 +39,27 @@ error CannotDefendRootClaim();
*/ */
error ClaimAlreadyExists(); error ClaimAlreadyExists();
/**
* @notice Thrown when a given claim is invalid (0).
*/
error InvalidClaim();
/**
* @notice Thrown when an action that requires the game to be `IN_PROGRESS` is invoked when
* the game is not in progress.
*/
error GameNotInProgress();
/**
* @notice Thrown when a move is attempted to be made after the clock has timed out.
*/
error ClockTimeExceeded();
/**
* @notice Thrown when a move is attempted to be made at or greater than the max depth of the game.
*/
error GameDepthExceeded();
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// `AttestationDisputeGame` Errors // // `AttestationDisputeGame` Errors //
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
......
...@@ -39,11 +39,11 @@ type Duration is uint64; ...@@ -39,11 +39,11 @@ type Duration is uint64;
* ┌────────────┬────────────────┐ * ┌────────────┬────────────────┐
* │ Bits │ Value │ * │ Bits │ Value │
* ├────────────┼────────────────┤ * ├────────────┼────────────────┤
* │ [0, 128) │ Duration │ * │ [0, 64) │ Duration │
* │ [128, 256) │ Timestamp │ * │ [64, 128) │ Timestamp │
* └────────────┴────────────────┘ * └────────────┴────────────────┘
*/ */
type Clock is uint256; type Clock is uint128;
/** /**
* @notice A `Position` represents a position of a claim within the game tree. * @notice A `Position` represents a position of a claim within the game tree.
...@@ -51,11 +51,16 @@ type Clock is uint256; ...@@ -51,11 +51,16 @@ type Clock is uint256;
* ┌────────────┬────────────────┐ * ┌────────────┬────────────────┐
* │ Bits │ Value │ * │ Bits │ Value │
* ├────────────┼────────────────┤ * ├────────────┼────────────────┤
* │ [0, 128) │ Depth │ * │ [0, 64) │ Depth │
* │ [128, 256) │ Index at depth │ * │ [64, 128) │ Index at depth │
* └────────────┴────────────────┘ * └────────────┴────────────────┘
*/ */
type Position is uint256; type Position is uint128;
/**
* @notice A `GameType` represents the type of game being played.
*/
type GameType is uint8;
/** /**
* @notice The current status of the dispute game. * @notice The current status of the dispute game.
...@@ -70,13 +75,22 @@ enum GameStatus { ...@@ -70,13 +75,22 @@ enum GameStatus {
} }
/** /**
* @notice The type of proof system being used. * @title GameTypes
* @notice A library that defines the IDs of games that can be played.
*/ */
enum GameType { library GameTypes {
// The game will use a `IDisputeGame` implementation that utilizes fault proofs. /**
FAULT, * @dev The game will use a `IDisputeGame` implementation that utilizes fault proofs.
// The game will use a `IDisputeGame` implementation that utilizes validity proofs. */
VALIDITY, GameType internal constant FAULT = GameType.wrap(0);
// The game will use a `IDisputeGame` implementation that utilizes attestation proofs.
ATTESTATION /**
* @dev The game will use a `IDisputeGame` implementation that utilizes validity proofs.
*/
GameType internal constant VALIDITY = GameType.wrap(1);
/**
* @dev The game will use a `IDisputeGame` implementation that utilizes attestation proofs.
*/
GameType internal constant ATTESTATION = GameType.wrap(2);
} }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "forge-std/Test.sol";
import "../libraries/DisputeTypes.sol";
import { IDisputeGame } from "../dispute/interfaces/IDisputeGame.sol";
import { IBondManager } from "../dispute/interfaces/IBondManager.sol";
import { DisputeGameFactory } from "../dispute/DisputeGameFactory.sol";
import { BondManager } from "../dispute/BondManager.sol";
contract BondManager_Test is Test {
DisputeGameFactory factory;
BondManager bm;
// DisputeGameFactory events
event DisputeGameCreated(
address indexed disputeProxy,
GameType indexed gameType,
Claim indexed rootClaim
);
// BondManager events
event BondPosted(bytes32 bondId, address owner, uint256 expiration, uint256 amount);
event BondSeized(bytes32 bondId, address owner, address seizer, uint256 amount);
event BondReclaimed(bytes32 bondId, address claiment, uint256 amount);
function setUp() public {
factory = new DisputeGameFactory(address(this));
bm = new BondManager(factory);
}
/**
* -------------------------------------------
* Test Bond Posting
* -------------------------------------------
*/
/**
* @notice Tests that posting a bond succeeds.
*/
function testFuzz_post_succeeds(
bytes32 bondId,
address owner,
uint128 minClaimHold,
uint128 amount
) public {
vm.assume(owner != address(0));
vm.assume(owner != address(bm));
vm.assume(owner != address(this));
// Create2Deployer
vm.assume(owner != address(0x4e59b44847b379578588920cA78FbF26c0B4956C));
amount = uint128(bound(amount, 1, type(uint128).max));
minClaimHold = uint128(bound(minClaimHold, 0, type(uint128).max - block.timestamp));
vm.deal(address(this), amount);
vm.expectEmit(true, true, true, true);
uint128 expiration = uint128(block.timestamp + minClaimHold);
emit BondPosted(bondId, owner, expiration, amount);
bm.post{ value: amount }(bondId, owner, minClaimHold);
// Validate the bond
(
address newFetchedOwner,
bytes32 fetchedBondId,
uint128 fetchedExpiration,
uint128 bondAmount
) = bm.bonds(bondId);
assertEq(newFetchedOwner, owner);
assertEq(fetchedExpiration, block.timestamp + minClaimHold);
assertEq(fetchedBondId, bondId);
assertEq(bondAmount, amount);
}
/**
* @notice Tests that posting a bond with the same id twice reverts.
*/
function testFuzz_post_duplicates_reverts(
bytes32 bondId,
address owner,
uint128 minClaimHold,
uint128 amount
) public {
vm.assume(owner != address(0));
amount = amount / 2;
amount = uint128(bound(amount, 1, type(uint128).max));
minClaimHold = uint128(bound(minClaimHold, 0, type(uint128).max - block.timestamp));
vm.deal(address(this), amount);
bm.post{ value: amount }(bondId, owner, minClaimHold);
vm.deal(address(this), amount);
vm.expectRevert("BondManager: BondId already posted.");
bm.post{ value: amount }(bondId, owner, minClaimHold);
}
/**
* @notice Posting with the zero address as the owner fails.
*/
function testFuzz_post_zeroAddress_reverts(
bytes32 bondId,
uint128 minClaimHold,
uint128 amount
) public {
address owner = address(0);
vm.deal(address(this), amount);
vm.expectRevert("BondManager: Owner cannot be the zero address.");
bm.post{ value: amount }(bondId, owner, minClaimHold);
}
/**
* @notice Posting zero value bonds should revert.
*/
function testFuzz_post_zeroAddress_reverts(
bytes32 bondId,
address owner,
uint128 minClaimHold
) public {
vm.assume(owner != address(0));
uint128 amount = 0;
vm.deal(address(this), amount);
vm.expectRevert("BondManager: Value must be non-zero.");
bm.post{ value: amount }(bondId, owner, minClaimHold);
}
/**
* -------------------------------------------
* Test Bond Seizing
* -------------------------------------------
*/
/**
* @notice Non-existing bonds shouldn't be seizable.
*/
function testFuzz_seize_missingBond_reverts(bytes32 bondId) public {
vm.expectRevert("BondManager: The bond does not exist.");
bm.seize(bondId);
}
/**
* @notice Bonds that expired cannot be seized.
*/
function testFuzz_seize_expired_reverts(
bytes32 bondId,
address owner,
uint128 minClaimHold,
uint128 amount
) public {
vm.assume(owner != address(0));
vm.assume(owner != address(bm));
vm.assume(owner != address(this));
amount = uint128(bound(amount, 1, type(uint128).max));
minClaimHold = uint128(bound(minClaimHold, 0, type(uint128).max - block.timestamp));
vm.deal(address(this), amount);
bm.post{ value: amount }(bondId, owner, minClaimHold);
vm.warp(block.timestamp + minClaimHold + 1);
vm.expectRevert("BondManager: Bond expired.");
bm.seize(bondId);
}
/**
* @notice Bonds cannot be seized by unauthorized parties.
*/
function testFuzz_seize_unauthorized_reverts(
bytes32 bondId,
address owner,
uint128 minClaimHold,
uint128 amount
) public {
vm.assume(owner != address(0));
vm.assume(owner != address(bm));
vm.assume(owner != address(this));
amount = uint128(bound(amount, 1, type(uint128).max));
minClaimHold = uint128(bound(minClaimHold, 0, type(uint128).max - block.timestamp));
vm.deal(address(this), amount);
bm.post{ value: amount }(bondId, owner, minClaimHold);
MockAttestationDisputeGame game = new MockAttestationDisputeGame();
vm.prank(address(game));
vm.expectRevert("BondManager: Unauthorized seizure.");
bm.seize(bondId);
}
/**
* @notice Seizing a bond should succeed if the game resolves.
*/
function testFuzz_seize_succeeds(
bytes32 bondId,
uint128 minClaimHold,
bytes calldata extraData
) public {
minClaimHold = uint128(bound(minClaimHold, 0, type(uint128).max - block.timestamp));
vm.deal(address(this), 1 ether);
bm.post{ value: 1 ether }(bondId, address(0xba5ed), minClaimHold);
// Create a mock dispute game in the factory
IDisputeGame proxy;
Claim rootClaim;
bytes memory ed = extraData;
{
rootClaim = Claim.wrap(bytes32(""));
MockAttestationDisputeGame implementation = new MockAttestationDisputeGame();
GameType gt = GameType.ATTESTATION;
factory.setImplementation(gt, IDisputeGame(address(implementation)));
vm.expectEmit(false, true, true, false);
emit DisputeGameCreated(address(0), gt, rootClaim);
proxy = factory.create(gt, rootClaim, extraData);
assertEq(address(factory.games(gt, rootClaim, extraData)), address(proxy));
}
// Update the game fields
MockAttestationDisputeGame spawned = MockAttestationDisputeGame(payable(address(proxy)));
spawned.setBondManager(bm);
spawned.setRootClaim(rootClaim);
spawned.setGameStatus(GameStatus.CHALLENGER_WINS);
spawned.setBondId(bondId);
spawned.setExtraData(ed);
// Seize the bond by calling resolve
vm.expectEmit(true, true, true, true);
emit BondSeized(bondId, address(0xba5ed), address(spawned), 1 ether);
spawned.resolve();
assertEq(address(spawned).balance, 1 ether);
// Validate that the bond was deleted
(address newFetchedOwner, , , ) = bm.bonds(bondId);
assertEq(newFetchedOwner, address(0));
}
/**
* -------------------------------------------
* Test Bond Split and Seizing
* -------------------------------------------
*/
/**
* @notice Seizing and splitting a bond should succeed if the game resolves.
*/
function testFuzz_seizeAndSplit_succeeds(
bytes32 bondId,
uint128 minClaimHold,
bytes calldata extraData
) public {
minClaimHold = uint128(bound(minClaimHold, 0, type(uint128).max - block.timestamp));
vm.deal(address(this), 1 ether);
bm.post{ value: 1 ether }(bondId, address(0xba5ed), minClaimHold);
// Create a mock dispute game in the factory
IDisputeGame proxy;
Claim rootClaim;
bytes memory ed = extraData;
{
rootClaim = Claim.wrap(bytes32(""));
MockAttestationDisputeGame implementation = new MockAttestationDisputeGame();
GameType gt = GameType.ATTESTATION;
factory.setImplementation(gt, IDisputeGame(address(implementation)));
vm.expectEmit(false, true, true, false);
emit DisputeGameCreated(address(0), gt, rootClaim);
proxy = factory.create(gt, rootClaim, extraData);
assertEq(address(factory.games(gt, rootClaim, extraData)), address(proxy));
}
// Update the game fields
MockAttestationDisputeGame spawned = MockAttestationDisputeGame(payable(address(proxy)));
spawned.setBondManager(bm);
spawned.setRootClaim(rootClaim);
spawned.setGameStatus(GameStatus.CHALLENGER_WINS);
spawned.setBondId(bondId);
spawned.setExtraData(ed);
// Seize the bond by calling resolve
vm.expectEmit(true, true, true, true);
emit BondSeized(bondId, address(0xba5ed), address(spawned), 1 ether);
spawned.splitResolve();
assertEq(address(spawned).balance, 0);
address[] memory challengers = spawned.getChallengers();
uint256 proportionalAmount = 1 ether / challengers.length;
for (uint256 i = 0; i < challengers.length; i++) {
assertEq(address(challengers[i]).balance, proportionalAmount);
}
// Validate that the bond was deleted
(address newFetchedOwner, , , ) = bm.bonds(bondId);
assertEq(newFetchedOwner, address(0));
}
/**
* -------------------------------------------
* Test Bond Reclaiming
* -------------------------------------------
*/
/**
* @notice Bonds can be reclaimed after the specified amount of time.
*/
function testFuzz_reclaim_succeeds(
bytes32 bondId,
address owner,
uint128 minClaimHold,
uint128 amount
) public {
vm.assume(owner != address(factory));
vm.assume(owner != address(bm));
vm.assume(owner != address(this));
vm.assume(owner != address(0));
vm.assume(owner.code.length == 0);
amount = uint128(bound(amount, 1, type(uint128).max));
minClaimHold = uint128(bound(minClaimHold, 0, type(uint128).max - block.timestamp));
assumeNoPrecompiles(owner);
// Post the bond
vm.deal(address(this), amount);
bm.post{ value: amount }(bondId, owner, minClaimHold);
// We can't claim if the block.timestamp is less than the bond expiration.
(, , uint256 expiration, ) = bm.bonds(bondId);
if (expiration > block.timestamp) {
vm.prank(owner);
vm.expectRevert("BondManager: Bond isn't claimable yet.");
bm.reclaim(bondId);
}
// Past expiration, the owner can reclaim
vm.warp(expiration);
vm.prank(owner);
bm.reclaim(bondId);
assertEq(owner.balance, amount);
}
}
/**
* @title MockAttestationDisputeGame
* @dev A mock dispute game for testing bond seizures.
*/
contract MockAttestationDisputeGame {
GameStatus internal gameStatus;
BondManager bm;
Claim internal rc;
bytes internal ed;
bytes32 internal bondId;
address[] internal challengers;
function getChallengers() public view returns (address[] memory) {
return challengers;
}
function setBondId(bytes32 bid) external {
bondId = bid;
}
function setBondManager(BondManager _bm) external {
bm = _bm;
}
function setGameStatus(GameStatus _gs) external {
gameStatus = _gs;
}
function setRootClaim(Claim _rc) external {
rc = _rc;
}
function setExtraData(bytes memory _ed) external {
ed = _ed;
}
receive() external payable {}
fallback() external payable {}
function splitResolve() public {
challengers = [address(1), address(2)];
bm.seizeAndSplit(bondId, challengers);
}
/**
* -------------------------------------------
* Initializable Functions
* -------------------------------------------
*/
function initialize() external {
/* noop */
}
/**
* -------------------------------------------
* IVersioned Functions
* -------------------------------------------
*/
function version() external pure returns (string memory _version) {
return "0.1.0";
}
/**
* -------------------------------------------
* IDisputeGame Functions
* -------------------------------------------
*/
function createdAt() external pure returns (Timestamp _createdAt) {
return Timestamp.wrap(uint64(0));
}
function status() external view returns (GameStatus _status) {
return gameStatus;
}
function gameType() external pure returns (GameType _gameType) {
return GameType.ATTESTATION;
}
function rootClaim() external view returns (Claim _rootClaim) {
return rc;
}
function extraData() external view returns (bytes memory _extraData) {
return ed;
}
function gameData()
external
pure
returns (
GameType,
Claim,
bytes memory
)
{
assembly {
revert(0, 0)
}
}
function bondManager() external view returns (IBondManager _bondManager) {
return IBondManager(address(bm));
}
function resolve() external returns (GameStatus _status) {
bm.seize(bondId);
return gameStatus;
}
}
...@@ -182,49 +182,6 @@ contract Bytes_slice_Test is Test { ...@@ -182,49 +182,6 @@ contract Bytes_slice_Test is Test {
} }
contract Bytes_toNibbles_Test is Test { contract Bytes_toNibbles_Test is Test {
/**
* @notice Diffs the test Solidity version of `toNibbles` against the Yul version.
*
* @param _bytes The `bytes` array to convert to nibbles.
*
* @return Yul version of `toNibbles` applied to `_bytes`.
*/
function _toNibblesYul(bytes memory _bytes) internal pure returns (bytes memory) {
// Allocate memory for the `nibbles` array.
bytes memory nibbles = new bytes(_bytes.length << 1);
assembly {
// Load the length of the passed bytes array from memory
let bytesLength := mload(_bytes)
// Store the memory offset of the _bytes array's contents on the stack
let bytesStart := add(_bytes, 0x20)
// Store the memory offset of the nibbles array's contents on the stack
let nibblesStart := add(nibbles, 0x20)
// Loop through each byte in the input array
for {
let i := 0x00
} lt(i, bytesLength) {
i := add(i, 0x01)
} {
// Get the starting offset of the next 2 bytes in the nibbles array
let offset := add(nibblesStart, shl(0x01, i))
// Load the byte at the current index within the `_bytes` array
let b := byte(0x00, mload(add(bytesStart, i)))
// Pull out the first nibble and store it in the new array
mstore8(offset, shr(0x04, b))
// Pull out the second nibble and store it in the new array
mstore8(add(offset, 0x01), and(b, 0x0F))
}
}
return nibbles;
}
/** /**
* @notice Tests that, given an input of 5 bytes, the `toNibbles` function returns an array of * @notice Tests that, given an input of 5 bytes, the `toNibbles` function returns an array of
* 10 nibbles corresponding to the input data. * 10 nibbles corresponding to the input data.
...@@ -272,11 +229,49 @@ contract Bytes_toNibbles_Test is Test { ...@@ -272,11 +229,49 @@ contract Bytes_toNibbles_Test is Test {
} }
/** /**
* @notice Test that the `toNibbles` function in the `Bytes` library is equivalent to the Yul * @notice Tests that the `toNibbles` function correctly updates the free memory pointer depending
* implementation. * on the length of the resulting array.
*/ */
function testDiff_toNibbles_succeeds(bytes memory _input) public { function testFuzz_toNibbles_memorySafety_succeeds(bytes memory _input) public {
assertEq(Bytes.toNibbles(_input), _toNibblesYul(_input)); // Grab the free memory pointer before the `toNibbles` operation
uint64 initPtr;
assembly {
initPtr := mload(0x40)
}
uint64 expectedPtr = uint64(initPtr + 0x20 + ((_input.length * 2 + 0x1F) & ~uint256(0x1F)));
// Ensure that all memory outside of the expected range is safe.
vm.expectSafeMemory(initPtr, expectedPtr);
// Pull out each individual nibble from the input bytes array
bytes memory nibbles = Bytes.toNibbles(_input);
// Grab the free memory pointer after the `toNibbles` operation
uint64 finalPtr;
assembly {
finalPtr := mload(0x40)
}
// The free memory pointer should have been updated properly
if (_input.length == 0) {
// If the input length is zero, only 32 bytes of memory should have been allocated.
assertEq(finalPtr, initPtr + 0x20);
} else {
// If the input length is greater than zero, the memory allocated should be the
// length of the input * 2 + 32 bytes for the length field.
//
// Note that we use a slightly less efficient, but equivalent method of rounding
// up `_length` to the next multiple of 32 than is used in the `toNibbles` function.
// This is to diff test the method used in `toNibbles`.
uint64 _expectedPtr = uint64(initPtr + 0x20 + (((_input.length * 2 + 0x1F) >> 5) << 5));
assertEq(finalPtr, _expectedPtr);
// Sanity check for equivalence of the rounding methods.
assertEq(_expectedPtr, expectedPtr);
}
// The nibbles length should be equal to `_length * 2`
assertEq(nibbles.length, _input.length << 1);
} }
} }
......
...@@ -7,10 +7,10 @@ import "../libraries/DisputeErrors.sol"; ...@@ -7,10 +7,10 @@ import "../libraries/DisputeErrors.sol";
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { DisputeGameFactory } from "../dispute/DisputeGameFactory.sol"; import { DisputeGameFactory } from "../dispute/DisputeGameFactory.sol";
import { IDisputeGame } from "../dispute/interfaces/IDisputeGame.sol"; import { IDisputeGame } from "../dispute/interfaces/IDisputeGame.sol";
import { Proxy } from "../universal/Proxy.sol";
contract DisputeGameFactory_Test is Test { contract DisputeGameFactory_Initializer is Test {
DisputeGameFactory factory; DisputeGameFactory factory;
FakeClone fakeClone;
event DisputeGameCreated( event DisputeGameCreated(
address indexed disputeProxy, address indexed disputeProxy,
...@@ -20,8 +20,24 @@ contract DisputeGameFactory_Test is Test { ...@@ -20,8 +20,24 @@ contract DisputeGameFactory_Test is Test {
event ImplementationSet(address indexed impl, GameType indexed gameType); event ImplementationSet(address indexed impl, GameType indexed gameType);
function setUp() public { function setUp() public virtual {
factory = new DisputeGameFactory(address(this)); Proxy proxy = new Proxy(address(this));
DisputeGameFactory impl = new DisputeGameFactory();
proxy.upgradeToAndCall({
_implementation: address(impl),
_data: abi.encodeCall(impl.initialize, (address(this)))
});
factory = DisputeGameFactory(address(proxy));
vm.label(address(factory), "DisputeGameFactoryProxy");
}
}
contract DisputeGameFactory_Test is DisputeGameFactory_Initializer {
FakeClone fakeClone;
function setUp() public override {
super.setUp();
fakeClone = new FakeClone(); fakeClone = new FakeClone();
} }
...@@ -35,11 +51,11 @@ contract DisputeGameFactory_Test is Test { ...@@ -35,11 +51,11 @@ contract DisputeGameFactory_Test is Test {
bytes calldata extraData bytes calldata extraData
) public { ) public {
// Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values. // Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values.
GameType gt = GameType(uint8(bound(gameType, 0, 2))); GameType gt = GameType.wrap(uint8(bound(gameType, 0, 2)));
// Set all three implementations to the same `FakeClone` contract. // Set all three implementations to the same `FakeClone` contract.
for (uint8 i; i < 3; i++) { for (uint8 i; i < 3; i++) {
factory.setImplementation(GameType(i), IDisputeGame(address(fakeClone))); factory.setImplementation(GameType.wrap(i), IDisputeGame(address(fakeClone)));
} }
vm.expectEmit(false, true, true, false); vm.expectEmit(false, true, true, false);
...@@ -48,6 +64,8 @@ contract DisputeGameFactory_Test is Test { ...@@ -48,6 +64,8 @@ contract DisputeGameFactory_Test is Test {
// Ensure that the dispute game was assigned to the `disputeGames` mapping. // Ensure that the dispute game was assigned to the `disputeGames` mapping.
assertEq(address(factory.games(gt, rootClaim, extraData)), address(proxy)); assertEq(address(factory.games(gt, rootClaim, extraData)), address(proxy));
assertEq(factory.gameCount(), 1);
assertEq(address(factory.disputeGameList(0)), address(proxy));
} }
/** /**
...@@ -60,7 +78,7 @@ contract DisputeGameFactory_Test is Test { ...@@ -60,7 +78,7 @@ contract DisputeGameFactory_Test is Test {
bytes calldata extraData bytes calldata extraData
) public { ) public {
// Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values. // Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values.
GameType gt = GameType(uint8(bound(gameType, 0, 2))); GameType gt = GameType.wrap(uint8(bound(gameType, 0, 2)));
vm.expectRevert(abi.encodeWithSelector(NoImplementation.selector, gt)); vm.expectRevert(abi.encodeWithSelector(NoImplementation.selector, gt));
factory.create(gt, rootClaim, extraData); factory.create(gt, rootClaim, extraData);
...@@ -75,11 +93,11 @@ contract DisputeGameFactory_Test is Test { ...@@ -75,11 +93,11 @@ contract DisputeGameFactory_Test is Test {
bytes calldata extraData bytes calldata extraData
) public { ) public {
// Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values. // Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values.
GameType gt = GameType(uint8(bound(gameType, 0, 2))); GameType gt = GameType.wrap(uint8(bound(gameType, 0, 2)));
// Set all three implementations to the same `FakeClone` contract. // Set all three implementations to the same `FakeClone` contract.
for (uint8 i; i < 3; i++) { for (uint8 i; i < 3; i++) {
factory.setImplementation(GameType(i), IDisputeGame(address(fakeClone))); factory.setImplementation(GameType.wrap(i), IDisputeGame(address(fakeClone)));
} }
// Create our first dispute game - this should succeed. // Create our first dispute game - this should succeed.
...@@ -104,17 +122,17 @@ contract DisputeGameFactory_Test is Test { ...@@ -104,17 +122,17 @@ contract DisputeGameFactory_Test is Test {
* @dev Tests that the `setImplementation` function properly sets the implementation for a given `GameType`. * @dev Tests that the `setImplementation` function properly sets the implementation for a given `GameType`.
*/ */
function test_setImplementation_succeeds() public { function test_setImplementation_succeeds() public {
// There should be no implementation for the `GameType.FAULT` enum value, it has not been set. // There should be no implementation for the `GameTypes.FAULT` enum value, it has not been set.
assertEq(address(factory.gameImpls(GameType.FAULT)), address(0)); assertEq(address(factory.gameImpls(GameTypes.FAULT)), address(0));
vm.expectEmit(true, true, true, true, address(factory)); vm.expectEmit(true, true, true, true, address(factory));
emit ImplementationSet(address(1), GameType.FAULT); emit ImplementationSet(address(1), GameTypes.FAULT);
// Set the implementation for the `GameType.FAULT` enum value. // Set the implementation for the `GameTypes.FAULT` enum value.
factory.setImplementation(GameType.FAULT, IDisputeGame(address(1))); factory.setImplementation(GameTypes.FAULT, IDisputeGame(address(1)));
// Ensure that the implementation for the `GameType.FAULT` enum value is set. // Ensure that the implementation for the `GameTypes.FAULT` enum value is set.
assertEq(address(factory.gameImpls(GameType.FAULT)), address(1)); assertEq(address(factory.gameImpls(GameTypes.FAULT)), address(1));
} }
/** /**
...@@ -124,7 +142,7 @@ contract DisputeGameFactory_Test is Test { ...@@ -124,7 +142,7 @@ contract DisputeGameFactory_Test is Test {
// Ensure that the `setImplementation` function reverts when called by a non-owner. // Ensure that the `setImplementation` function reverts when called by a non-owner.
vm.prank(address(0)); vm.prank(address(0));
vm.expectRevert("Ownable: caller is not the owner"); vm.expectRevert("Ownable: caller is not the owner");
factory.setImplementation(GameType.FAULT, IDisputeGame(address(1))); factory.setImplementation(GameTypes.FAULT, IDisputeGame(address(1)));
} }
/** /**
...@@ -137,7 +155,7 @@ contract DisputeGameFactory_Test is Test { ...@@ -137,7 +155,7 @@ contract DisputeGameFactory_Test is Test {
bytes calldata extraData bytes calldata extraData
) public { ) public {
// Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values. // Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values.
GameType gt = GameType(uint8(bound(gameType, 0, 2))); GameType gt = GameType.wrap(uint8(bound(gameType, 0, 2)));
assertEq( assertEq(
Hash.unwrap(factory.getGameUUID(gt, rootClaim, extraData)), Hash.unwrap(factory.getGameUUID(gt, rootClaim, extraData)),
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { Test } from "forge-std/Test.sol";
import { DisputeGameFactory_Initializer } from "./DisputeGameFactory.t.sol";
import { DisputeGameFactory } from "../dispute/DisputeGameFactory.sol";
import { FaultDisputeGame } from "../dispute/FaultDisputeGame.sol";
import "../libraries/DisputeTypes.sol";
import "../libraries/DisputeErrors.sol";
import { LibClock } from "../dispute/lib/LibClock.sol";
import { LibPosition } from "../dispute/lib/LibPosition.sol";
contract FaultDisputeGame_Test is DisputeGameFactory_Initializer {
/**
* @dev The root claim of the game.
*/
Claim internal constant ROOT_CLAIM = Claim.wrap(bytes32(uint256(10)));
/**
* @dev The extra data passed to the game for initialization.
*/
bytes internal constant EXTRA_DATA = abi.encode(1);
/**
* @dev The type of the game being tested.
*/
GameType internal constant GAME_TYPE = GameType.wrap(0);
/**
* @dev The current version of the `FaultDisputeGame` contract.
*/
string internal constant VERSION = "0.0.1";
/**
* @dev The implementation of the game.
*/
FaultDisputeGame internal gameImpl;
/**
* @dev The `Clone` proxy of the game.
*/
FaultDisputeGame internal gameProxy;
event Move(uint256 indexed parentIndex, Claim indexed pivot, address indexed claimant);
function setUp() public override {
super.setUp();
// Deploy an implementation of the fault game
gameImpl = new FaultDisputeGame();
// Register the game implementation with the factory.
factory.setImplementation(GAME_TYPE, gameImpl);
// Create a new game.
gameProxy = FaultDisputeGame(address(factory.create(GAME_TYPE, ROOT_CLAIM, EXTRA_DATA)));
// Label the proxy
vm.label(address(gameProxy), "FaultDisputeGame_Clone");
}
////////////////////////////////////////////////////////////////
// `IDisputeGame` Implementation Tests //
////////////////////////////////////////////////////////////////
/**
* @dev Tests that the game's root claim is set correctly.
*/
function test_rootClaim_succeeds() public {
assertEq(Claim.unwrap(gameProxy.rootClaim()), Claim.unwrap(ROOT_CLAIM));
}
/**
* @dev Tests that the game's extra data is set correctly.
*/
function test_extraData_succeeds() public {
assertEq(gameProxy.extraData(), EXTRA_DATA);
}
/**
* @dev Tests that the game's version is set correctly.
*/
function test_version_succeeds() public {
assertEq(gameProxy.version(), VERSION);
}
/**
* @dev Tests that the game's status is set correctly.
*/
function test_gameStart_succeeds() public {
assertEq(Timestamp.unwrap(gameProxy.gameStart()), block.timestamp);
}
/**
* @dev Tests that the game's type is set correctly.
*/
function test_gameType_succeeds() public {
assertEq(GameType.unwrap(gameProxy.gameType()), GameType.unwrap(GAME_TYPE));
}
/**
* @dev Tests that the game's data is set correctly.
*/
function test_gameData_succeeds() public {
(GameType gameType, Claim rootClaim, bytes memory extraData) = gameProxy.gameData();
assertEq(GameType.unwrap(gameType), GameType.unwrap(GAME_TYPE));
assertEq(Claim.unwrap(rootClaim), Claim.unwrap(ROOT_CLAIM));
assertEq(extraData, EXTRA_DATA);
}
////////////////////////////////////////////////////////////////
// `IFaultDisputeGame` Implementation Tests //
////////////////////////////////////////////////////////////////
/**
* @dev Tests that the root claim's data is set correctly when the game is initialized.
*/
function test_initialRootClaimData_succeeds() public {
(
uint32 parentIndex,
bool countered,
Claim claim,
Position position,
Clock clock
) = gameProxy.claimData(0);
assertEq(parentIndex, type(uint32).max);
assertEq(countered, false);
assertEq(Claim.unwrap(claim), Claim.unwrap(ROOT_CLAIM));
assertEq(Position.unwrap(position), 0);
assertEq(
Clock.unwrap(clock),
Clock.unwrap(LibClock.wrap(Duration.wrap(0), Timestamp.wrap(uint64(block.timestamp))))
);
}
/**
* @dev Tests that a move while the game status is not `IN_PROGRESS` causes the call to revert
* with the `GameNotInProgress` error
*/
function test_move_gameNotInProgress_reverts() public {
uint256 chalWins = uint256(GameStatus.CHALLENGER_WINS);
// Replace the game status in storage. It exists in slot 0 at offset 8.
uint256 slot = uint256(vm.load(address(gameProxy), bytes32(0)));
uint256 offset = (8 << 3);
uint256 mask = 0xFF << offset;
// Replace the byte in the slot value with the challenger wins status.
slot = (slot & ~mask) | (chalWins << offset);
vm.store(address(gameProxy), bytes32(uint256(0)), bytes32(slot));
// Ensure that the game status was properly updated.
GameStatus status = gameProxy.status();
assertEq(uint256(status), chalWins);
// Attempt to make a move. Should revert.
vm.expectRevert(GameNotInProgress.selector);
gameProxy.attack(0, Claim.wrap(0));
}
/**
* @dev Tests that an attempt to defend the root claim reverts with the `CannotDefendRootClaim` error.
*/
function test_defendRoot_reverts() public {
vm.expectRevert(CannotDefendRootClaim.selector);
gameProxy.defend(0, Claim.wrap(bytes32(uint256(5))));
}
/**
* @dev Tests that an attempt to move against a claim that does not exist reverts with the
* `ParentDoesNotExist` error.
*/
function test_moveAgainstNonexistentParent_reverts() public {
Claim claim = Claim.wrap(bytes32(uint256(5)));
// Expect an out of bounds revert for an attack
vm.expectRevert(abi.encodeWithSignature("Panic(uint256)", 0x32));
gameProxy.attack(1, claim);
// Expect an out of bounds revert for an attack
vm.expectRevert(abi.encodeWithSignature("Panic(uint256)", 0x32));
gameProxy.defend(1, claim);
}
/**
* @dev Tests that an attempt to move at the maximum game depth reverts with the
* `GameDepthExceeded` error.
*/
function test_gameDepthExceeded_reverts() public {
Claim claim = Claim.wrap(bytes32(uint256(5)));
for (uint256 i = 0; i < 63; i++) {
// At the max game depth, the `_move` function should revert with
// the `GameDepthExceeded` error.
if (i == 62) {
vm.expectRevert(GameDepthExceeded.selector);
}
gameProxy.attack(i, claim);
}
}
/**
* @dev Tests that a move made after the clock time has exceeded reverts with the
* `ClockTimeExceeded` error.
*/
function test_clockTimeExceeded_reverts() public {
// Warp ahead past the clock time for the first move (3 1/2 days)
vm.warp(block.timestamp + 3 days + 12 hours + 1);
vm.expectRevert(ClockTimeExceeded.selector);
gameProxy.attack(0, Claim.wrap(bytes32(uint256(5))));
}
/**
* @dev Tests that an identical claim cannot be made twice. The duplicate claim attempt should
* revert with the `ClaimAlreadyExists` error.
*/
function test_duplicateClaim_reverts() public {
Claim claim = Claim.wrap(bytes32(uint256(5)));
// Make the first move. This should succeed.
gameProxy.attack(0, claim);
// Attempt to make the same move again.
vm.expectRevert(ClaimAlreadyExists.selector);
gameProxy.attack(0, claim);
}
/**
* @dev Static unit test for the correctness of an opening attack.
*/
function test_simpleAttack_succeeds() public {
// Warp ahead 5 seconds.
vm.warp(block.timestamp + 5);
Claim counter = Claim.wrap(bytes32(uint256(5)));
// Perform the attack.
vm.expectEmit(true, true, true, false);
emit Move(0, counter, address(this));
gameProxy.attack(0, counter);
// Grab the claim data of the attack.
(
uint32 parentIndex,
bool countered,
Claim claim,
Position position,
Clock clock
) = gameProxy.claimData(1);
// Assert correctness of the attack claim's data.
assertEq(parentIndex, 0);
assertEq(countered, false);
assertEq(Claim.unwrap(claim), Claim.unwrap(counter));
assertEq(Position.unwrap(position), Position.unwrap(LibPosition.attack(Position.wrap(0))));
assertEq(
Clock.unwrap(clock),
Clock.unwrap(LibClock.wrap(Duration.wrap(5), Timestamp.wrap(uint64(block.timestamp))))
);
// Grab the claim data of the parent.
(parentIndex, countered, claim, position, clock) = gameProxy.claimData(0);
// Assert correctness of the parent claim's data.
assertEq(parentIndex, type(uint32).max);
assertEq(countered, true);
assertEq(Claim.unwrap(claim), Claim.unwrap(ROOT_CLAIM));
assertEq(Position.unwrap(position), 0);
assertEq(
Clock.unwrap(clock),
Clock.unwrap(
LibClock.wrap(Duration.wrap(0), Timestamp.wrap(uint64(block.timestamp - 5)))
)
);
}
}
/**
* @title BigStepper
* @notice A mock fault proof processor contract for testing purposes.
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⠶⢅⠒⢄⢔⣶⡦⣤⡤⠄⣀⠀⠀⠀⠀⠀⠀⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠨⡏⠀⠀⠈⠢⣙⢯⣄⠀⢨⠯⡺⡘⢄⠀⠀⠀⠀⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣶⡆⠀⠀⠀⠀⠈⠓⠬⡒⠡⣀⢙⡜⡀⠓⠄⠀⠀⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡷⠿⣧⣀⡀⠀⠀⠀⠀⠀⠀⠉⠣⣞⠩⠥⠀⠼⢄⠀⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⠉⢹⣶⠒⠒⠂⠈⠉⠁⠘⡆⠀⣿⣿⠫⡄⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⢶⣤⣀⡀⠀⠀⢸⡿⠀⠀⠀⠀⠀⢀⠞⠀⠀⢡⢨⢀⡄⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⡒⣿⢿⡤⠝⡣⠉⠁⠚⠛⠀⠤⠤⣄⡰⠁⠀⠀⠀⠉⠙⢸⠀⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡤⢯⡌⡿⡇⠘⡷⠀⠁⠀⠀⢀⣰⠢⠲⠛⣈⣸⠦⠤⠶⠴⢬⣐⣊⡂⠀
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⡪⡗⢫⠞⠀⠆⣀⠻⠤⠴⠐⠚⣉⢀⠦⠂⠋⠁⠀⠁⠀⠀⠀⠀⢋⠉⠇⠀
*⠀⠀⠀⠀⣀⡤⠐⠒⠘⡹⠉⢸⠇⠸⠀⠀⠀⠀⣀⣤⠴⠚⠉⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠼⠀⣾⠀
*⠀⠀⠀⡰⠀⠉⠉⠀⠁⠀⠀⠈⢇⠈⠒⠒⠘⠈⢀⢡⡂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠀⢸⡄
*⠀⠀⠸⣿⣆⠤⢀⡀⠀⠀⠀⠀⢘⡌⠀⠀⣀⣀⣀⡈⣤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⢸⡇
*⠀⠀⢸⣀⠀⠉⠒⠐⠛⠋⠭⠭⠍⠉⠛⠒⠒⠒⠀⠒⠚⠛⠛⠛⠩⠭⠭⠭⠭⠤⠤⠤⠤⠤⠭⠭⠉⠓⡆
*⠀⠀⠘⠿⣷⣶⣤⣤⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇
*⠀⠀⠀⠀⠀⠉⠙⠛⠛⠻⠿⢿⣿⣿⣷⣶⣶⣶⣤⣤⣀⣁⣛⣃⣒⠿⠿⠿⠤⠠⠄⠤⠤⢤⣛⣓⣂⣻⡇
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠙⠛⠻⠿⠿⠿⢿⣿⣿⣿⣷⣶⣶⣾⣿⣿⣿⣿⠿⠟⠁
*⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠈⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀
*/
contract BigStepper {
/**
* @notice Steps from the `preState` to the `postState` by adding 1 to the `preState`.
* @param preState The pre state to start from
* @return postState The state stepped to
*/
function step(Claim preState) external pure returns (Claim postState) {
postState = Claim.wrap(bytes32(uint256(Claim.unwrap(preState)) + 1));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { Test } from "forge-std/Test.sol";
import { LibClock } from "../dispute/lib/LibClock.sol";
import "../libraries/DisputeTypes.sol";
/**
* @notice Tests for `LibClock`
*/
contract LibClock_Test is Test {
/**
* @notice Tests that the `duration` function correctly shifts out the `Duration` from a packed `Clock` type.
*/
function testFuzz_duration_succeeds(Duration _duration, Timestamp _timestamp) public {
Clock clock = LibClock.wrap(_duration, _timestamp);
assertEq(Duration.unwrap(LibClock.duration(clock)), Duration.unwrap(_duration));
}
/**
* @notice Tests that the `timestamp` function correctly shifts out the `Timestamp` from a packed `Clock` type.
*/
function testFuzz_timestamp_succeeds(Duration _duration, Timestamp _timestamp) public {
Clock clock = LibClock.wrap(_duration, _timestamp);
assertEq(Timestamp.unwrap(LibClock.timestamp(clock)), Timestamp.unwrap(_timestamp));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { Test } from "forge-std/Test.sol";
import { LibPosition } from "../dispute/lib/LibPosition.sol";
import "../libraries/DisputeTypes.sol";
/**
* @notice Tests for `LibPosition`
*/
contract LibPosition_Test is Test {
/**
* @dev Assumes a MAX depth of 63 for the Position type. Any greater depth can cause overflows.
* @dev At the lowest level of the tree, this allows for 2 ** 63 leaves. In reality, the max game depth
* will likely be much lower.
*/
uint8 internal constant MAX_DEPTH = 63;
/**
* @notice Tests that the `depth` function correctly shifts out the `depth` from a packed `Position` type.
*/
function testFuzz_depth_correctness(uint64 _depth, uint64 _indexAtDepth) public {
Position position = LibPosition.wrap(_depth, _indexAtDepth);
assertEq(LibPosition.depth(position), _depth);
}
/**
* @notice Tests that the `indexAtDepth` function correctly shifts out the `indexAtDepth` from a packed `Position` type.
*/
function testFuzz_indexAtDepth_correctness(uint64 _depth, uint64 _indexAtDepth) public {
Position position = LibPosition.wrap(_depth, _indexAtDepth);
assertEq(LibPosition.indexAtDepth(position), _indexAtDepth);
}
/**
* @notice Tests that the `left` function correctly computes the position of the left child.
*/
function testFuzz_left_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [0, 63]
_depth = uint8(bound(_depth, 0, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position left = LibPosition.left(position);
assertEq(LibPosition.depth(left), uint64(_depth) + 1);
assertEq(LibPosition.indexAtDepth(left), _indexAtDepth * 2);
}
/**
* @notice Tests that the `right` function correctly computes the position of the right child.
*/
function testFuzz_right_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [0, 63]
_depth = uint8(bound(_depth, 0, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position right = LibPosition.right(position);
assertEq(LibPosition.depth(right), _depth + 1);
assertEq(LibPosition.indexAtDepth(right), _indexAtDepth * 2 + 1);
}
/**
* @notice Tests that the `parent` function correctly computes the position of the parent.
*/
function testFuzz_parent_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [1, 63]
_depth = uint8(bound(_depth, 1, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position parent = LibPosition.parent(position);
assertEq(LibPosition.depth(parent), _depth - 1);
assertEq(LibPosition.indexAtDepth(parent), _indexAtDepth / 2);
}
/**
* @notice Tests that the `rightIndex` function correctly computes the deepest, right most index relative
* to a given position.
*/
function testFuzz_rightIndex_correctness(
uint8 _maxDepth,
uint8 _depth,
uint64 _indexAtDepth
) public {
// Max depth bound: [1, 63]
// The max game depth MUST be at least 1.
_maxDepth = uint8(bound(_maxDepth, 1, MAX_DEPTH));
// Depth bound: [0, _maxDepth]
_depth = uint8(bound(_depth, 0, _maxDepth));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
uint64 rightIndex = LibPosition.rightIndex(position, _maxDepth);
// Find the deepest, rightmost index in Solidity rather than Yul
for (uint256 i = _depth; i < _maxDepth - 1; ++i) {
position = LibPosition.right(position);
}
uint64 _rightIndex = LibPosition.indexAtDepth(position);
assertEq(rightIndex, _rightIndex);
}
/**
* @notice Tests that the `attack` function correctly computes the position of the attack relative to
* a given position.
* @dev `attack` is an alias for `left`, but we test it separately for completeness.
*/
function testFuzz_attack_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [0, 63]
_depth = uint8(bound(_depth, 0, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position attack = LibPosition.attack(position);
assertEq(LibPosition.depth(attack), _depth + 1);
assertEq(LibPosition.indexAtDepth(attack), _indexAtDepth * 2);
}
/**
* @notice Tests that the `defend` function correctly computes the position of the defense relative to
* a given position.
* @dev A defense can only be given if the position does not belong to the root claim, hence the bound of [1, 127]
* on the depth.
*/
function testFuzz_defend_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [1, 63]
_depth = uint8(bound(_depth, 1, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position defend = LibPosition.defend(position);
assertEq(LibPosition.depth(defend), _depth + 1);
assertEq(LibPosition.indexAtDepth(defend), ((_indexAtDepth / 2) * 2 + 1) * 2);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol";
import { StdUtils } from "forge-std/StdUtils.sol";
import { StdInvariant } from "forge-std/StdInvariant.sol";
import { Arithmetic } from "../../libraries/Arithmetic.sol";
import { ResourceMetering } from "../../L1/ResourceMetering.sol";
import { Proxy } from "../../universal/Proxy.sol";
import { Constants } from "../../libraries/Constants.sol";
contract ResourceMetering_User is StdUtils, ResourceMetering {
bool public failedMaxGasPerBlock;
bool public failedRaiseBaseFee;
bool public failedLowerBaseFee;
bool public failedNeverBelowMinBaseFee;
bool public failedMaxRaiseBaseFeePerBlock;
bool public failedMaxLowerBaseFeePerBlock;
// Used as a special flag for the purpose of identifying unchecked math errors specifically
// in the test contracts, not the target contracts themselves.
bool public underflow;
constructor() {
initialize();
}
function initialize() internal initializer {
__ResourceMetering_init();
}
function resourceConfig() public pure returns (ResourceMetering.ResourceConfig memory) {
return _resourceConfig();
}
function _resourceConfig()
internal
pure
override
returns (ResourceMetering.ResourceConfig memory)
{
ResourceMetering.ResourceConfig memory rcfg = Constants.DEFAULT_RESOURCE_CONFIG();
return rcfg;
}
/**
* @notice Takes the necessary parameters to allow us to burn arbitrary amounts of gas to test
* the underlying resource metering/gas market logic
*/
function burn(uint256 _gasToBurn, bool _raiseBaseFee) public {
// Part 1: we cache the current param values and do some basic checks on them.
uint256 cachedPrevBaseFee = uint256(params.prevBaseFee);
uint256 cachedPrevBoughtGas = uint256(params.prevBoughtGas);
uint256 cachedPrevBlockNum = uint256(params.prevBlockNum);
ResourceMetering.ResourceConfig memory rcfg = resourceConfig();
uint256 targetResourceLimit = uint256(rcfg.maxResourceLimit) /
uint256(rcfg.elasticityMultiplier);
// check that the last block's base fee hasn't dropped below the minimum
if (cachedPrevBaseFee < uint256(rcfg.minimumBaseFee)) {
failedNeverBelowMinBaseFee = true;
}
// check that the last block didn't consume more than the max amount of gas
if (cachedPrevBoughtGas > uint256(rcfg.maxResourceLimit)) {
failedMaxGasPerBlock = true;
}
// Part2: we perform the gas burn
// force the gasToBurn into the correct range based on whether we intend to
// raise or lower the baseFee after this block, respectively
uint256 gasToBurn;
if (_raiseBaseFee) {
gasToBurn = bound(
_gasToBurn,
uint256(targetResourceLimit),
uint256(rcfg.maxResourceLimit)
);
} else {
gasToBurn = bound(_gasToBurn, 0, targetResourceLimit);
}
_burnInternal(uint64(gasToBurn));
// Part 3: we run checks and modify our invariant flags based on the updated params values
// Calculate the maximum allowed baseFee change (per block)
uint256 maxBaseFeeChange = cachedPrevBaseFee / uint256(rcfg.baseFeeMaxChangeDenominator);
// If the last block used more than the target amount of gas (and there were no
// empty blocks in between), ensure this block's baseFee increased, but not by
// more than the max amount per block
if (
(cachedPrevBoughtGas > uint256(targetResourceLimit)) &&
(uint256(params.prevBlockNum) - cachedPrevBlockNum == 1)
) {
failedRaiseBaseFee = failedRaiseBaseFee || (params.prevBaseFee <= cachedPrevBaseFee);
failedMaxRaiseBaseFeePerBlock =
failedMaxRaiseBaseFeePerBlock ||
((uint256(params.prevBaseFee) - cachedPrevBaseFee) < maxBaseFeeChange);
}
// If the last block used less than the target amount of gas, (or was empty),
// ensure that: this block's baseFee was decreased, but not by more than the max amount
if (
(cachedPrevBoughtGas < uint256(targetResourceLimit)) ||
(uint256(params.prevBlockNum) - cachedPrevBlockNum > 1)
) {
// Invariant: baseFee should decrease
failedLowerBaseFee =
failedLowerBaseFee ||
(uint256(params.prevBaseFee) > cachedPrevBaseFee);
if (params.prevBlockNum - cachedPrevBlockNum == 1) {
// No empty blocks
// Invariant: baseFee should not have decreased by more than the maximum amount
failedMaxLowerBaseFeePerBlock =
failedMaxLowerBaseFeePerBlock ||
((cachedPrevBaseFee - uint256(params.prevBaseFee)) <= maxBaseFeeChange);
} else if (params.prevBlockNum - cachedPrevBlockNum > 1) {
// We have at least one empty block
// Update the maxBaseFeeChange to account for multiple blocks having passed
unchecked {
maxBaseFeeChange = uint256(
int256(cachedPrevBaseFee) -
Arithmetic.clamp(
Arithmetic.cdexp(
int256(cachedPrevBaseFee),
int256(uint256(rcfg.baseFeeMaxChangeDenominator)),
int256(uint256(params.prevBlockNum) - cachedPrevBlockNum)
),
int256(uint256(rcfg.minimumBaseFee)),
int256(uint256(rcfg.maximumBaseFee))
)
);
}
// Detect an underflow in the previous calculation.
// Without using unchecked above, and detecting the underflow here, fuzzer would
// otherwise ignore the revert.
underflow = underflow || maxBaseFeeChange > cachedPrevBaseFee;
// Invariant: baseFee should not have decreased by more than the maximum amount
failedMaxLowerBaseFeePerBlock =
failedMaxLowerBaseFeePerBlock ||
((cachedPrevBaseFee - uint256(params.prevBaseFee)) <= maxBaseFeeChange);
}
}
}
function _burnInternal(uint64 _gasToBurn) private metered(_gasToBurn) {}
}
contract ResourceMetering_Invariant is StdInvariant, Test {
ResourceMetering_User internal actor;
function setUp() public {
// Create a actor.
actor = new ResourceMetering_User();
targetContract(address(actor));
bytes4[] memory selectors = new bytes4[](1);
selectors[0] = actor.burn.selector;
FuzzSelector memory selector = FuzzSelector({ addr: address(actor), selectors: selectors });
targetSelector(selector);
}
/**
* @custom:invariant The base fee should increase if the last block used more
* than the target amount of gas
*
* If the last block used more than the target amount of gas (and there were no
* empty blocks in between), ensure this block's baseFee increased, but not by
* more than the max amount per block.
*/
function invariant_high_usage_raise_baseFee() external {
assertFalse(actor.failedRaiseBaseFee());
}
/**
* @custom:invariant The base fee should decrease if the last block used less
* than the target amount of gas
*
* If the previous block used less than the target amount of gas, the base fee should decrease,
* but not more than the max amount.
*/
function invariant_low_usage_lower_baseFee() external {
assertFalse(actor.failedLowerBaseFee());
}
/**
* @custom:invariant A block's base fee should never be below `MINIMUM_BASE_FEE`
*
* This test asserts that a block's base fee can never drop below the
* `MINIMUM_BASE_FEE` threshold.
*/
function invariant_never_below_min_baseFee() external {
assertFalse(actor.failedNeverBelowMinBaseFee());
}
/**
* @custom:invariant A block can never consume more than `MAX_RESOURCE_LIMIT` gas.
*
* This test asserts that a block can never consume more than the `MAX_RESOURCE_LIMIT`
* gas threshold.
*/
function invariant_never_above_max_gas_limit() external {
assertFalse(actor.failedMaxGasPerBlock());
}
/**
* @custom:invariant The base fee can never be raised more than the max base fee change.
*
* After a block consumes more gas than the target gas, the base fee cannot be raised
* more than the maximum amount allowed. The max base fee change (per-block) is derived
* as follows: `prevBaseFee / BASE_FEE_MAX_CHANGE_DENOMINATOR`
*/
function invariant_never_exceed_max_increase() external {
assertFalse(actor.failedMaxRaiseBaseFeePerBlock());
}
/**
* @custom:invariant The base fee can never be lowered more than the max base fee change.
*
* After a block consumes less than the target gas, the base fee cannot be lowered more
* than the maximum amount allowed. The max base fee change (per-block) is derived as
*follows: `prevBaseFee / BASE_FEE_MAX_CHANGE_DENOMINATOR`
*/
function invariant_never_exceed_max_decrease() external {
assertFalse(actor.failedMaxLowerBaseFeePerBlock());
}
/**
* @custom:invariant The `maxBaseFeeChange` calculation over multiple blocks can never
* underflow.
*
* When calculating the `maxBaseFeeChange` after multiple empty blocks, the calculation
* should never be allowed to underflow.
*/
function invariant_never_underflow() external {
assertFalse(actor.underflow());
}
}
...@@ -34,12 +34,13 @@ ...@@ -34,12 +34,13 @@
"l2GenesisBlockBaseFeePerGas": "0x3B9ACA00", "l2GenesisBlockBaseFeePerGas": "0x3B9ACA00",
"gasPriceOracleOverhead": 2100, "gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000, "gasPriceOracleScalar": 1000000,
"enableGovernance": true,
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenName": "Optimism", "governanceTokenName": "Optimism",
"governanceTokenOwner": "0xBcd4042DE499D14e55001CcbB24a551F3b954096", "governanceTokenOwner": "0xBcd4042DE499D14e55001CcbB24a551F3b954096",
"eip1559Denominator": 8, "eip1559Denominator": 8,
"eip1559Elasticity": 2, "eip1559Elasticity": 2,
"l1GenesisBlockTimestamp": "0x638a4554", "l1GenesisBlockTimestamp": "0x648a0943",
"l1StartingBlockTag": "earliest", "l1StartingBlockTag": "earliest",
"l2GenesisRegolithTimeOffset": "0x0" "l2GenesisRegolithTimeOffset": "0x0"
} }
\ No newline at end of file
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
"gasPriceOracleOverhead": 2100, "gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000, "gasPriceOracleScalar": 1000000,
"enableGovernance": true,
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenName": "Optimism", "governanceTokenName": "Optimism",
"governanceTokenOwner": "ADMIN", "governanceTokenOwner": "ADMIN",
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
"proxyAdminOwner": "0x62790eFcB3a5f3A5D398F95B47930A9Addd83807", "proxyAdminOwner": "0x62790eFcB3a5f3A5D398F95B47930A9Addd83807",
"enableGovernance": true,
"governanceTokenName": "Optimism", "governanceTokenName": "Optimism",
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenOwner": "0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76", "governanceTokenOwner": "0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76",
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
"gasPriceOracleOverhead": 2100, "gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000, "gasPriceOracleScalar": 1000000,
"enableGovernance": true,
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenName": "Optimism", "governanceTokenName": "Optimism",
"governanceTokenOwner": "0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76", "governanceTokenOwner": "0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76",
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
"baseFeeVaultWithdrawalNetwork": 0, "baseFeeVaultWithdrawalNetwork": 0,
"l1FeeVaultWithdrawalNetwork": 0, "l1FeeVaultWithdrawalNetwork": 0,
"sequencerFeeVaultWithdrawalNetwork": 0, "sequencerFeeVaultWithdrawalNetwork": 0,
"enableGovernance": true,
"governanceTokenName": "Optimism", "governanceTokenName": "Optimism",
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "governanceTokenOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
"l1FeeVaultWithdrawalNetwork": 0, "l1FeeVaultWithdrawalNetwork": 0,
"sequencerFeeVaultWithdrawalNetwork": 0, "sequencerFeeVaultWithdrawalNetwork": 0,
"enableGovernance": true,
"governanceTokenName": "Optimism", "governanceTokenName": "Optimism",
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenOwner": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF", "governanceTokenOwner": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF",
......
...@@ -37,6 +37,7 @@ const config: DeployConfig = { ...@@ -37,6 +37,7 @@ const config: DeployConfig = {
l1FeeVaultWithdrawalNetwork: 0, l1FeeVaultWithdrawalNetwork: 0,
sequencerFeeVaultWithdrawalNetwork: 0, sequencerFeeVaultWithdrawalNetwork: 0,
enableGovernance: true,
governanceTokenName: 'Optimism', governanceTokenName: 'Optimism',
governanceTokenSymbol: 'OP', governanceTokenSymbol: 'OP',
governanceTokenOwner: '0x90F79bf6EB2c4f870365E785982E1f101E93b906', governanceTokenOwner: '0x90F79bf6EB2c4f870365E785982E1f101E93b906',
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
"baseFeeVaultWithdrawalNetwork": 0, "baseFeeVaultWithdrawalNetwork": 0,
"l1FeeVaultWithdrawalNetwork": 0, "l1FeeVaultWithdrawalNetwork": 0,
"sequencerFeeVaultWithdrawalNetwork": 0, "sequencerFeeVaultWithdrawalNetwork": 0,
"enableGovernance": true,
"governanceTokenName": "Optimism", "governanceTokenName": "Optimism",
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenOwner": "0x5C4e7Ba1E219E47948e6e3F55019A647bA501005", "governanceTokenOwner": "0x5C4e7Ba1E219E47948e6e3F55019A647bA501005",
......
...@@ -11,7 +11,6 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -11,7 +11,6 @@ const deployFn: DeployFunction = async (hre) => {
contract: 'AddressManager', contract: 'AddressManager',
args: [], args: [],
postDeployAction: async (contract) => { postDeployAction: async (contract) => {
// Owner is temporarily set to the deployer.
await assertContractVariable(contract, 'owner', deployer) await assertContractVariable(contract, 'owner', deployer)
}, },
}) })
......
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
await deploy({
hre,
name: 'ProxyAdmin',
args: [deployer],
postDeployAction: async (contract) => {
// Owner is temporarily set to the deployer. We transfer ownership of the ProxyAdmin to the
// SystemDictator before we trigger the dictator steps.
await assertContractVariable(contract, 'owner', deployer)
},
})
}
deployFn.tags = ['ProxyAdmin', 'setup', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
getContractsFromArtifacts,
deploy,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [addressManager] = await getContractsFromArtifacts(hre, [
{
name: 'Lib_AddressManager',
signerOrProvider: deployer,
},
])
const proxyAdmin = await deploy({
hre,
name: 'ProxyAdmin',
args: [deployer],
postDeployAction: async (contract) => {
// Owner is temporarily set to the deployer.
await assertContractVariable(contract, 'owner', deployer)
},
})
let addressManagerOnProxy = await proxyAdmin.callStatic.addressManager()
if (addressManagerOnProxy !== addressManager.address) {
// Set the address manager on the proxy admin
console.log(
`ProxyAdmin(${proxyAdmin.address}).setAddressManager(${addressManager.address})`
)
const tx = await proxyAdmin.setAddressManager(addressManager.address)
await tx.wait()
}
// Validate the address manager was set correctly.
addressManagerOnProxy = await proxyAdmin.callStatic.addressManager()
assert(
addressManagerOnProxy === addressManager.address,
'AddressManager not set on ProxyAdmin'
)
}
deployFn.tags = ['ProxyAdmin', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { assertContractVariable, deploy } from '../src/deploy-utils' import {
assertContractVariable,
getDeploymentAddress,
deploy,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts() const proxyAdmin = await getDeploymentAddress(hre, 'ProxyAdmin')
await deploy({ await deploy({
hre, hre,
name: 'Proxy__OVM_L1StandardBridge', name: 'Proxy__OVM_L1StandardBridge',
contract: 'L1ChugSplashProxy', contract: 'L1ChugSplashProxy',
args: [deployer], args: [proxyAdmin],
postDeployAction: async (contract) => { postDeployAction: async (contract) => {
await assertContractVariable(contract, 'getOwner', deployer) await assertContractVariable(contract, 'getOwner', proxyAdmin)
}, },
}) })
} }
......
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { deploy, getDeploymentAddress } from '../src/deploy-utils' import { deploy, getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const addressManager = await getDeploymentAddress(hre, 'Lib_AddressManager') const { deployer } = await hre.getNamedAccounts()
const [addressManager] = await getContractsFromArtifacts(hre, [
{
name: 'Lib_AddressManager',
signerOrProvider: deployer,
},
])
// The name in the address manager for this contract
const name = 'OVM_L1CrossDomainMessenger'
console.log(
`Setting up ResolvedDelegateProxy with AddressManager(${addressManager.address})`
)
await deploy({ const l1CrossDomainMessengerProxy = await deploy({
hre, hre,
name: 'Proxy__OVM_L1CrossDomainMessenger', name: 'Proxy__OVM_L1CrossDomainMessenger',
contract: 'ResolvedDelegateProxy', contract: 'ResolvedDelegateProxy',
args: [addressManager, 'OVM_L1CrossDomainMessenger'], args: [addressManager.address, name],
}) })
let addr = await addressManager.getAddress(name)
if (addr !== l1CrossDomainMessengerProxy.address) {
console.log(
`AddressManager(${addressManager.address}).setAddress(${name}, ${l1CrossDomainMessengerProxy.address})`
)
const tx = await addressManager.setAddress(
name,
l1CrossDomainMessengerProxy.address
)
await tx.wait()
}
addr = await addressManager.getAddress(name)
assert(
addr === l1CrossDomainMessengerProxy.address,
`${name} not set correctly`
)
} }
deployFn.tags = ['L1CrossDomainMessengerProxy', 'setup', 'l1'] deployFn.tags = ['L1CrossDomainMessengerProxy', 'setup', 'l1']
......
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { assertContractVariable, deploy } from '../src/deploy-utils' import {
assertContractVariable,
getDeploymentAddress,
deploy,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts() const proxyAdmin = await getDeploymentAddress(hre, 'ProxyAdmin')
await deploy({ await deploy({
hre, hre,
name: 'L1ERC721BridgeProxy', name: 'L1ERC721BridgeProxy',
contract: 'Proxy', contract: 'Proxy',
args: [deployer], args: [proxyAdmin],
postDeployAction: async (contract) => { postDeployAction: async (contract) => {
await assertContractVariable(contract, 'admin', deployer) await assertContractVariable(contract, 'admin', proxyAdmin)
}, },
}) })
} }
......
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { deploy, getDeploymentAddress } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const proxyAdmin = await getDeploymentAddress(hre, 'ProxyAdmin')
// We only want to deploy the dgf on devnet for now
const network = await hre.ethers.provider.getNetwork()
const chainId = network.chainId
if (chainId === 900) {
console.log('Devnet detected, deploying DisputeGameFactoryProxy')
const disputeGameFactoryProxy = await deploy({
hre,
name: 'DisputeGameFactoryProxy',
contract: 'Proxy',
args: [proxyAdmin],
})
console.log(
`DisputeGameFactoryProxy deployed at ${disputeGameFactoryProxy.address}`
)
} else {
console.log('Skipping deployment of DisputeGameFactoryProxy')
}
}
deployFn.tags = ['DisputeGameFactoryProxy', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
await deploy({
hre,
name: 'SystemDictatorProxy',
contract: 'Proxy',
args: [deployer],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'admin', deployer)
},
})
}
deployFn.tags = ['SystemDictatorProxy', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
// We only want to deploy the dgf on devnet for now
const network = await hre.ethers.provider.getNetwork()
const chainId = network.chainId
if (chainId === 900) {
const disputeGameFactory = await deploy({
hre,
name: 'DisputeGameFactory',
args: [],
})
console.log(`DisputeGameFactory deployed at ${disputeGameFactory.address}`)
} else {
console.log('Skipping deployment of DisputeGameFactory implementation')
}
}
deployFn.tags = ['DisputeGameFactoryImpl', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
deploy,
getContractFromArtifact,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const OptimismPortalProxy = await getContractFromArtifact(
hre,
'OptimismPortalProxy'
)
await deploy({
hre,
name: 'PortalSender',
args: [OptimismPortalProxy.address],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'PORTAL',
OptimismPortalProxy.address
)
},
})
}
deployFn.tags = ['PortalSenderImpl', 'setup', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, addressManager] = await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'Lib_AddressManager',
signerOrProvider: deployer,
},
])
let addressManagerOwner = await addressManager.callStatic.owner()
if (addressManagerOwner !== proxyAdmin.address) {
console.log(
`AddressManager(${addressManager.address}).transferOwnership(${proxyAdmin.address})`
)
const tx = await addressManager.transferOwnership(proxyAdmin.address)
await tx.wait()
}
addressManagerOwner = await addressManager.callStatic.owner()
assert(
addressManagerOwner === proxyAdmin.address,
'AddressManager owner not set correctly'
)
}
deployFn.tags = ['AddressManager', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
await deploy({
hre,
name: 'SystemDictator',
args: [],
})
}
deployFn.tags = ['SystemDictatorImpl', 'setup', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
// We only want to deploy the dgf on devnet for now
const network = await hre.ethers.provider.getNetwork()
const chainId = network.chainId
// The DisputeGameFactory is only deployed on devnet
if (chainId === 900) {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, disputeGameFactoryProxy, disputeGameFactoryImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'DisputeGameFactoryProxy',
iface: 'DisputeGameFactory',
signerOrProvider: deployer,
},
{
name: 'DisputeGameFactory',
},
])
const finalOwner = hre.deployConfig.finalSystemOwner
try {
const tx = await proxyAdmin.upgradeAndCall(
disputeGameFactoryProxy.address,
disputeGameFactoryImpl.address,
disputeGameFactoryProxy.interface.encodeFunctionData('initialize', [
finalOwner,
])
)
await tx.wait()
} catch (e) {
console.log('DisputeGameFactory already initialized')
}
const fetchedOwner = await disputeGameFactoryProxy.callStatic.owner()
assert(fetchedOwner === finalOwner)
console.log('Updgraded and initialized DisputeGameFactory')
} else {
console.log('Skipping initialization of DisputeGameFactory')
}
}
deployFn.tags = ['DisputeGameFactoryInitialize', 'l1']
export default deployFn
import assert from 'assert'
import { ethers } from 'ethers'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { awaitCondition } from '@eth-optimism/core-utils'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import {
getContractsFromArtifacts,
getDeploymentAddress,
} from '../src/deploy-utils'
import { defaultResourceConfig } from '../src/constants'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
// Load the contracts we need to interact with.
const [
SystemDictator,
SystemDictatorProxy,
SystemDictatorProxyWithSigner,
SystemDictatorImpl,
] = await getContractsFromArtifacts(hre, [
{
name: 'SystemDictatorProxy',
iface: 'SystemDictator',
signerOrProvider: deployer,
},
{
name: 'SystemDictatorProxy',
},
{
name: 'SystemDictatorProxy',
signerOrProvider: deployer,
},
{
name: 'SystemDictator',
signerOrProvider: deployer,
},
])
// Load the dictator configuration.
const config = {
globalConfig: {
proxyAdmin: await getDeploymentAddress(hre, 'ProxyAdmin'),
controller: hre.deployConfig.controller,
finalOwner: hre.deployConfig.finalSystemOwner,
addressManager: await getDeploymentAddress(hre, 'Lib_AddressManager'),
},
proxyAddressConfig: {
l2OutputOracleProxy: await getDeploymentAddress(
hre,
'L2OutputOracleProxy'
),
optimismPortalProxy: await getDeploymentAddress(
hre,
'OptimismPortalProxy'
),
l1CrossDomainMessengerProxy: await getDeploymentAddress(
hre,
'Proxy__OVM_L1CrossDomainMessenger'
),
l1StandardBridgeProxy: await getDeploymentAddress(
hre,
'Proxy__OVM_L1StandardBridge'
),
optimismMintableERC20FactoryProxy: await getDeploymentAddress(
hre,
'OptimismMintableERC20FactoryProxy'
),
l1ERC721BridgeProxy: await getDeploymentAddress(
hre,
'L1ERC721BridgeProxy'
),
systemConfigProxy: await getDeploymentAddress(hre, 'SystemConfigProxy'),
},
implementationAddressConfig: {
l2OutputOracleImpl: await getDeploymentAddress(hre, 'L2OutputOracle'),
optimismPortalImpl: await getDeploymentAddress(hre, 'OptimismPortal'),
l1CrossDomainMessengerImpl: await getDeploymentAddress(
hre,
'L1CrossDomainMessenger'
),
l1StandardBridgeImpl: await getDeploymentAddress(hre, 'L1StandardBridge'),
optimismMintableERC20FactoryImpl: await getDeploymentAddress(
hre,
'OptimismMintableERC20Factory'
),
l1ERC721BridgeImpl: await getDeploymentAddress(hre, 'L1ERC721Bridge'),
portalSenderImpl: await getDeploymentAddress(hre, 'PortalSender'),
systemConfigImpl: await getDeploymentAddress(hre, 'SystemConfig'),
},
systemConfigConfig: {
owner: hre.deployConfig.finalSystemOwner,
overhead: hre.deployConfig.gasPriceOracleOverhead,
scalar: hre.deployConfig.gasPriceOracleScalar,
batcherHash: hre.ethers.utils.hexZeroPad(
hre.deployConfig.batchSenderAddress,
32
),
gasLimit: hre.deployConfig.l2GenesisBlockGasLimit,
unsafeBlockSigner: hre.deployConfig.p2pSequencerAddress,
// The resource config is not exposed to the end user
// to simplify deploy config. It may be introduced in the future.
resourceConfig: defaultResourceConfig,
},
}
// Update the implementation if necessary.
if (
(await SystemDictatorProxy.callStatic.implementation({
from: ethers.constants.AddressZero,
})) !== SystemDictatorImpl.address
) {
console.log('Upgrading the SystemDictator proxy...')
// Upgrade and initialize the proxy.
await SystemDictatorProxyWithSigner.upgradeToAndCall(
SystemDictatorImpl.address,
SystemDictatorImpl.interface.encodeFunctionData('initialize', [config])
)
// Wait for the transaction to execute properly.
await awaitCondition(
async () => {
return (
(await SystemDictatorProxy.callStatic.implementation({
from: ethers.constants.AddressZero,
})) === SystemDictatorImpl.address
)
},
30000,
1000
)
// Verify that the contract was initialized correctly.
const dictatorConfig = await SystemDictator.config()
for (const [outerConfigKey, outerConfigValue] of Object.entries(config)) {
for (const [innerConfigKey, innerConfigValue] of Object.entries(
outerConfigValue
)) {
let have = dictatorConfig[outerConfigKey][innerConfigKey]
let want = innerConfigValue as any
if (ethers.utils.isAddress(want)) {
want = want.toLowerCase()
have = have.toLowerCase()
} else if (typeof want === 'number') {
want = ethers.BigNumber.from(want)
have = ethers.BigNumber.from(have)
assert(
want.eq(have),
`incorrect config for ${outerConfigKey}.${innerConfigKey}. Want: ${want}, have: ${have}`
)
return
}
assert(
want === have,
`incorrect config for ${outerConfigKey}.${innerConfigKey}. Want: ${want}, have: ${have}`
)
}
}
}
// Update the owner if necessary.
if (
(await SystemDictatorProxy.callStatic.admin({
from: ethers.constants.AddressZero,
})) !== hre.deployConfig.controller
) {
console.log('Transferring ownership of the SystemDictator proxy...')
// Transfer ownership to the controller address.
await SystemDictatorProxyWithSigner.changeAdmin(hre.deployConfig.controller)
// Wait for the transaction to execute properly.
await awaitCondition(
async () => {
return (
(await SystemDictatorProxy.callStatic.admin({
from: ethers.constants.AddressZero,
})) === hre.deployConfig.controller
)
},
30000,
1000
)
}
}
deployFn.tags = ['SystemDictatorImpl', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { BigNumber } from 'ethers'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { defaultResourceConfig } from '../src/constants'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, systemConfigProxy, systemConfigImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'SystemConfigProxy',
iface: 'SystemConfig',
signerOrProvider: deployer,
},
{
name: 'SystemConfig',
},
])
const batcherHash = hre.ethers.utils
.hexZeroPad(hre.deployConfig.batchSenderAddress, 32)
.toLowerCase()
const l2GenesisBlockGasLimit = BigNumber.from(
hre.deployConfig.l2GenesisBlockGasLimit
)
const l2GasLimitLowerBound = BigNumber.from(
defaultResourceConfig.systemTxMaxGas +
defaultResourceConfig.maxResourceLimit
)
if (l2GenesisBlockGasLimit.lt(l2GasLimitLowerBound)) {
throw new Error(
`L2 genesis block gas limit must be at least ${l2GasLimitLowerBound}`
)
}
try {
const tx = await proxyAdmin.upgradeAndCall(
systemConfigProxy.address,
systemConfigImpl.address,
systemConfigImpl.interface.encodeFunctionData('initialize', [
hre.deployConfig.finalSystemOwner,
hre.deployConfig.gasPriceOracleOverhead,
hre.deployConfig.gasPriceOracleScalar,
batcherHash,
l2GenesisBlockGasLimit,
hre.deployConfig.p2pSequencerAddress,
defaultResourceConfig,
])
)
await tx.wait()
} catch (e) {
console.log('SystemConfig already initialized')
console.log(e)
}
const version = await systemConfigProxy.callStatic.version()
console.log(`SystemConfig version: ${version}`)
console.log('Upgraded SystemConfig')
}
deployFn.tags = ['SystemConfigInitialize', 'l1']
export default deployFn
import assert from 'assert'
import { ethers } from 'ethers'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { awaitCondition } from '@eth-optimism/core-utils'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import '@nomiclabs/hardhat-ethers'
import {
assertContractVariable,
getContractsFromArtifacts,
getDeploymentAddress,
doOwnershipTransfer,
doPhase,
liveDeployer,
} from '../src/deploy-utils'
const uint128Max = ethers.BigNumber.from('0xffffffffffffffffffffffffffffffff')
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
// Set up required contract references.
const [
SystemDictator,
ProxyAdmin,
AddressManager,
L1StandardBridgeProxy,
L1StandardBridgeProxyWithSigner,
L1ERC721BridgeProxy,
L1ERC721BridgeProxyWithSigner,
SystemConfigProxy,
] = await getContractsFromArtifacts(hre, [
{
name: 'SystemDictatorProxy',
iface: 'SystemDictator',
signerOrProvider: deployer,
},
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'Lib_AddressManager',
signerOrProvider: deployer,
},
{
name: 'Proxy__OVM_L1StandardBridge',
},
{
name: 'Proxy__OVM_L1StandardBridge',
signerOrProvider: deployer,
},
{
name: 'L1ERC721BridgeProxy',
},
{
name: 'L1ERC721BridgeProxy',
signerOrProvider: deployer,
},
{
name: 'SystemConfigProxy',
iface: 'SystemConfig',
signerOrProvider: deployer,
},
])
// If we have the key for the controller then we don't need to wait for external txns.
// Set the DISABLE_LIVE_DEPLOYER=true in the env to ensure the script will pause to simulate scenarios
// where the controller is not the deployer.
const isLiveDeployer = await liveDeployer({
hre,
disabled: process.env.DISABLE_LIVE_DEPLOYER,
})
// Transfer ownership of the ProxyAdmin to the SystemDictator.
if ((await ProxyAdmin.owner()) !== SystemDictator.address) {
await doOwnershipTransfer({
isLiveDeployer,
proxy: ProxyAdmin,
name: 'ProxyAdmin',
transferFunc: 'transferOwnership',
dictator: SystemDictator,
})
}
// We don't need to transfer proxy addresses if we're already beyond the proxy transfer step.
const needsProxyTransfer =
(await SystemDictator.currentStep()) <=
(await SystemDictator.PROXY_TRANSFER_STEP())
// Transfer ownership of the AddressManager to SystemDictator.
if (
needsProxyTransfer &&
(await AddressManager.owner()) !== SystemDictator.address
) {
await doOwnershipTransfer({
isLiveDeployer,
proxy: AddressManager,
name: 'AddressManager',
transferFunc: 'transferOwnership',
dictator: SystemDictator,
})
} else {
console.log(`AddressManager already owned by the SystemDictator`)
}
// Transfer ownership of the L1StandardBridge (proxy) to SystemDictator.
if (
needsProxyTransfer &&
(await L1StandardBridgeProxy.callStatic.getOwner({
from: ethers.constants.AddressZero,
})) !== SystemDictator.address
) {
await doOwnershipTransfer({
isLiveDeployer,
proxy: L1StandardBridgeProxyWithSigner,
name: 'L1StandardBridgeProxy',
transferFunc: 'setOwner',
dictator: SystemDictator,
})
} else {
console.log(`L1StandardBridge already owned by MSD`)
}
// Transfer ownership of the L1ERC721Bridge (proxy) to SystemDictator.
if (
needsProxyTransfer &&
(await L1ERC721BridgeProxy.callStatic.admin({
from: ethers.constants.AddressZero,
})) !== SystemDictator.address
) {
await doOwnershipTransfer({
isLiveDeployer,
proxy: L1ERC721BridgeProxyWithSigner,
name: 'L1ERC721BridgeProxy',
transferFunc: 'changeAdmin',
dictator: SystemDictator,
})
} else {
console.log(`L1ERC721Bridge already owned by MSD`)
}
// Wait for the ownership transfers to complete before continuing.
await awaitCondition(
async (): Promise<boolean> => {
const proxyAdminOwner = await ProxyAdmin.owner()
const addressManagerOwner = await AddressManager.owner()
const l1StandardBridgeOwner =
await L1StandardBridgeProxy.callStatic.getOwner({
from: ethers.constants.AddressZero,
})
const l1Erc721BridgeOwner = await L1ERC721BridgeProxy.callStatic.admin({
from: ethers.constants.AddressZero,
})
return (
proxyAdminOwner === SystemDictator.address &&
addressManagerOwner === SystemDictator.address &&
l1StandardBridgeOwner === SystemDictator.address &&
l1Erc721BridgeOwner === SystemDictator.address
)
},
5000,
1000
)
await doPhase({
isLiveDeployer,
SystemDictator,
phase: 1,
message: `
Phase 1 includes the following steps:
Step 1 will configure the ProxyAdmin contract, you can safely execute this step at any time
without impacting the functionality of the rest of the system.
Step 2 will stop deposits and withdrawals via the L1CrossDomainMessenger and will stop the
DTL from syncing new deposits via the CTC, effectively shutting down the legacy system. Once
this step has been executed, you should immediately begin the L2 migration process. If you
need to restart the system, run exit1() followed by finalize().
`,
checks: async () => {
// Step 1 checks
await assertContractVariable(
ProxyAdmin,
'addressManager',
AddressManager.address
)
assert(
(await ProxyAdmin.implementationName(
getDeploymentAddress(hre, 'Proxy__OVM_L1CrossDomainMessenger')
)) === 'OVM_L1CrossDomainMessenger'
)
assert(
(await ProxyAdmin.proxyType(
getDeploymentAddress(hre, 'Proxy__OVM_L1CrossDomainMessenger')
)) === 2
)
assert(
(await ProxyAdmin.proxyType(
getDeploymentAddress(hre, 'Proxy__OVM_L1StandardBridge')
)) === 1
)
// Check the SystemConfig was initialized properly.
await assertContractVariable(
SystemConfigProxy,
'owner',
hre.deployConfig.finalSystemOwner
)
await assertContractVariable(
SystemConfigProxy,
'overhead',
hre.deployConfig.gasPriceOracleOverhead
)
await assertContractVariable(
SystemConfigProxy,
'scalar',
hre.deployConfig.gasPriceOracleScalar
)
await assertContractVariable(
SystemConfigProxy,
'batcherHash',
ethers.utils.hexZeroPad(
hre.deployConfig.batchSenderAddress.toLowerCase(),
32
)
)
await assertContractVariable(
SystemConfigProxy,
'gasLimit',
hre.deployConfig.l2GenesisBlockGasLimit
)
const config = await SystemConfigProxy.resourceConfig()
assert(config.maxResourceLimit === 20_000_000)
assert(config.elasticityMultiplier === 10)
assert(config.baseFeeMaxChangeDenominator === 8)
assert(config.systemTxMaxGas === 1_000_000)
assert(ethers.utils.parseUnits('1', 'gwei').eq(config.minimumBaseFee))
assert(config.maximumBaseFee.eq(uint128Max))
// Step 2 checks
const messenger = await AddressManager.getAddress(
'OVM_L1CrossDomainMessenger'
)
assert(messenger === ethers.constants.AddressZero)
},
})
}
deployFn.tags = ['SystemDictatorSteps', 'phase1', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, l1StandardBridgeProxy, l1StandardBridgeImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'Proxy__OVM_L1StandardBridge',
iface: 'L1StandardBridge',
signerOrProvider: deployer,
},
{
name: 'L1StandardBridge',
},
])
const proxyType = await proxyAdmin.callStatic.proxyType(
l1StandardBridgeProxy.address
)
if (proxyType !== 1) {
console.log(
`ProxyAdmin(${proxyAdmin.address}).setProxyType(${l1StandardBridgeProxy.address}, 1)`
)
// Set the L1StandardBridge to the UPGRADEABLE proxy type.
const tx = await proxyAdmin.setProxyType(l1StandardBridgeProxy.address, 1)
await tx.wait()
}
try {
const tx = await proxyAdmin.upgrade(
l1StandardBridgeProxy.address,
l1StandardBridgeImpl.address
)
await tx.wait()
} catch (e) {
console.log('L1StandardBridge already initialized')
}
const version = await l1StandardBridgeProxy.callStatic.version()
console.log(`L1StandardBridge version: ${version}`)
console.log('Upgraded L1StandardBridge')
}
deployFn.tags = ['L1StandardBridgeInitialize', 'l1']
export default deployFn
import assert from 'assert'
import { ethers } from 'ethers'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { awaitCondition } from '@eth-optimism/core-utils'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import '@nomiclabs/hardhat-ethers'
import {
assertContractVariable,
getContractsFromArtifacts,
printJsonTransaction,
isStep,
printTenderlySimulationLink,
printCastCommand,
liveDeployer,
doPhase,
isStartOfPhase,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
// Set up required contract references.
const [
SystemDictator,
ProxyAdmin,
AddressManager,
L1CrossDomainMessenger,
L1StandardBridgeProxy,
L1StandardBridge,
L2OutputOracle,
OptimismPortal,
OptimismMintableERC20Factory,
L1ERC721BridgeProxy,
L1ERC721Bridge,
] = await getContractsFromArtifacts(hre, [
{
name: 'SystemDictatorProxy',
iface: 'SystemDictator',
signerOrProvider: deployer,
},
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'Lib_AddressManager',
signerOrProvider: deployer,
},
{
name: 'Proxy__OVM_L1CrossDomainMessenger',
iface: 'L1CrossDomainMessenger',
signerOrProvider: deployer,
},
{
name: 'Proxy__OVM_L1StandardBridge',
},
{
name: 'Proxy__OVM_L1StandardBridge',
iface: 'L1StandardBridge',
signerOrProvider: deployer,
},
{
name: 'L2OutputOracleProxy',
iface: 'L2OutputOracle',
signerOrProvider: deployer,
},
{
name: 'OptimismPortalProxy',
iface: 'OptimismPortal',
signerOrProvider: deployer,
},
{
name: 'OptimismMintableERC20FactoryProxy',
iface: 'OptimismMintableERC20Factory',
signerOrProvider: deployer,
},
{
name: 'L1ERC721BridgeProxy',
},
{
name: 'L1ERC721BridgeProxy',
iface: 'L1ERC721Bridge',
signerOrProvider: deployer,
},
])
// If we have the key for the controller then we don't need to wait for external txns.
// Set the DISABLE_LIVE_DEPLOYER=true in the env to ensure the script will pause to simulate scenarios
// where the controller is not the deployer.
const isLiveDeployer = await liveDeployer({
hre,
disabled: process.env.DISABLE_LIVE_DEPLOYER,
})
// Make sure the dynamic system configuration has been set.
if (
(await isStartOfPhase(SystemDictator, 2)) &&
!(await SystemDictator.dynamicConfigSet())
) {
console.log(`
You must now set the dynamic L2OutputOracle configuration by calling the function
updateL2OutputOracleDynamicConfig. You will need to provide the
l2OutputOracleStartingBlockNumber and the l2OutputOracleStartingTimestamp which can both be
found by querying the last finalized block in the L2 node.
`)
if (isLiveDeployer) {
console.log(`Updating dynamic oracle config...`)
// Use default starting time if not provided
let deployL2StartingTimestamp =
hre.deployConfig.l2OutputOracleStartingTimestamp
if (deployL2StartingTimestamp < 0) {
const l1StartingBlock = await hre.ethers.provider.getBlock(
hre.deployConfig.l1StartingBlockTag
)
if (l1StartingBlock === null) {
throw new Error(
`Cannot fetch block tag ${hre.deployConfig.l1StartingBlockTag}`
)
}
deployL2StartingTimestamp = l1StartingBlock.timestamp
}
await SystemDictator.updateDynamicConfig(
{
l2OutputOracleStartingBlockNumber:
hre.deployConfig.l2OutputOracleStartingBlockNumber,
l2OutputOracleStartingTimestamp: deployL2StartingTimestamp,
},
false // do not pause the the OptimismPortal when initializing
)
} else {
// pause the OptimismPortal when initializing
const optimismPortalPaused = true
const tx = await SystemDictator.populateTransaction.updateDynamicConfig(
{
l2OutputOracleStartingBlockNumber:
hre.deployConfig.l2OutputOracleStartingBlockNumber,
l2OutputOracleStartingTimestamp:
hre.deployConfig.l2OutputOracleStartingTimestamp,
},
optimismPortalPaused
)
console.log(`Please update dynamic oracle config...`)
console.log(
JSON.stringify(
{
l2OutputOracleStartingBlockNumber:
hre.deployConfig.l2OutputOracleStartingBlockNumber,
l2OutputOracleStartingTimestamp:
hre.deployConfig.l2OutputOracleStartingTimestamp,
optimismPortalPaused,
},
null,
2
)
)
console.log(`MSD address: ${SystemDictator.address}`)
printJsonTransaction(tx)
printCastCommand(tx)
await printTenderlySimulationLink(SystemDictator.provider, tx)
}
await awaitCondition(
async () => {
return SystemDictator.dynamicConfigSet()
},
5000,
1000
)
}
await doPhase({
isLiveDeployer,
SystemDictator,
phase: 2,
message: `
Phase 2 includes the following steps:
Step 3 will clear out some legacy state from the AddressManager. Once you execute this step,
you WILL NOT BE ABLE TO RESTART THE SYSTEM using exit1(). You should confirm that the L2
system is entirely operational before executing this step.
Step 4 will transfer ownership of the AddressManager and L1StandardBridge to the ProxyAdmin.
Step 5 will initialize all Bedrock contracts. After this step is executed, the OptimismPortal
will be open for deposits but withdrawals will be paused if deploying a production network.
The Proposer will also be able to submit L2 outputs to the L2OutputOracle.
Lastly the finalize step will be executed. This will transfer ownership of the ProxyAdmin to
the final system owner as specified in the deployment configuration.
`,
checks: async () => {
// Step 3 checks
const deads = [
'OVM_CanonicalTransactionChain',
'OVM_L2CrossDomainMessenger',
'OVM_DecompressionPrecompileAddress',
'OVM_Sequencer',
'OVM_Proposer',
'OVM_ChainStorageContainer-CTC-batches',
'OVM_ChainStorageContainer-CTC-queue',
'OVM_CanonicalTransactionChain',
'OVM_StateCommitmentChain',
'OVM_BondManager',
'OVM_ExecutionManager',
'OVM_FraudVerifier',
'OVM_StateManagerFactory',
'OVM_StateTransitionerFactory',
'OVM_SafetyChecker',
'OVM_L1MultiMessageRelayer',
'BondManager',
]
for (const dead of deads) {
const addr = await AddressManager.getAddress(dead)
assert(addr === ethers.constants.AddressZero)
}
// Step 4 checks
await assertContractVariable(AddressManager, 'owner', ProxyAdmin.address)
assert(
(await L1StandardBridgeProxy.callStatic.getOwner({
from: ethers.constants.AddressZero,
})) === ProxyAdmin.address
)
assert(
(await L1ERC721BridgeProxy.callStatic.admin({
from: ProxyAdmin.address,
})) === ProxyAdmin.address
)
// Step 5 checks
// Check L2OutputOracle was initialized properly.
await assertContractVariable(
L2OutputOracle,
'latestBlockNumber',
hre.deployConfig.l2OutputOracleStartingBlockNumber
)
// Check OptimismPortal was initialized properly.
await assertContractVariable(
OptimismPortal,
'l2Sender',
'0x000000000000000000000000000000000000dEaD'
)
const resourceParams = await OptimismPortal.params()
assert(
resourceParams.prevBaseFee.eq(ethers.utils.parseUnits('1', 'gwei')),
`OptimismPortal was not initialized with the correct initial base fee`
)
assert(
resourceParams.prevBoughtGas.eq(0),
`OptimismPortal was not initialized with the correct initial bought gas`
)
assert(
!resourceParams.prevBlockNum.eq(0),
`OptimismPortal was not initialized with the correct initial block number`
)
assert(
(await hre.ethers.provider.getBalance(L1StandardBridge.address)).eq(0)
)
if (isLiveDeployer) {
await assertContractVariable(OptimismPortal, 'paused', false)
} else {
await assertContractVariable(OptimismPortal, 'paused', true)
}
// Check L1CrossDomainMessenger was initialized properly.
try {
await L1CrossDomainMessenger.xDomainMessageSender()
assert(false, `L1CrossDomainMessenger was not initialized properly`)
} catch (err) {
// Expected.
}
// Check L1StandardBridge was initialized properly.
await assertContractVariable(
L1StandardBridge,
'messenger',
L1CrossDomainMessenger.address
)
assert(
(await hre.ethers.provider.getBalance(L1StandardBridge.address)).eq(0)
)
// Check OptimismMintableERC20Factory was initialized properly.
await assertContractVariable(
OptimismMintableERC20Factory,
'BRIDGE',
L1StandardBridge.address
)
// Check L1ERC721Bridge was initialized properly.
await assertContractVariable(
L1ERC721Bridge,
'messenger',
L1CrossDomainMessenger.address
)
// finalize checks
await assertContractVariable(
ProxyAdmin,
'owner',
hre.deployConfig.finalSystemOwner
)
},
})
// Step 6 unpauses the OptimismPortal.
if (await isStep(SystemDictator, 6)) {
console.log(`
Unpause the OptimismPortal. The GUARDIAN account should be used. In practice
this is the multisig. In test networks, the OptimismPortal is initialized
without being paused.
`)
if (isLiveDeployer) {
console.log('WARNING: OptimismPortal configured to not be paused')
console.log('This should only happen for test environments')
await assertContractVariable(OptimismPortal, 'paused', false)
} else {
const tx = await OptimismPortal.populateTransaction.unpause()
console.log(`Please unpause the OptimismPortal...`)
console.log(`OptimismPortal address: ${OptimismPortal.address}`)
printJsonTransaction(tx)
printCastCommand(tx)
await printTenderlySimulationLink(SystemDictator.provider, tx)
}
await awaitCondition(
async () => {
const paused = await OptimismPortal.paused()
return !paused
},
5000,
1000
)
await assertContractVariable(OptimismPortal, 'paused', false)
await awaitCondition(
async () => {
return SystemDictator.finalized()
},
5000,
1000
)
}
}
deployFn.tags = ['SystemDictatorSteps', 'phase2', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, l1ERC721BridgeProxy, l1ERC721BridgeImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'L1ERC721BridgeProxy',
iface: 'L1ERC721Bridge',
signerOrProvider: deployer,
},
{
name: 'L1ERC721Bridge',
},
])
try {
const tx = await proxyAdmin.upgrade(
l1ERC721BridgeProxy.address,
l1ERC721BridgeImpl.address
)
await tx.wait()
} catch (e) {
console.log('L1ERC721Bridge already initialized')
}
const version = await l1ERC721BridgeProxy.callStatic.version()
console.log(`L1ERC721Bridge version: ${version}`)
console.log('Upgraded L1ERC721Bridge')
}
deployFn.tags = ['L1ERC721BridgeInitialize', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, mintableERC20FactoryProxy, mintableERC20FactoryImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'OptimismMintableERC20FactoryProxy',
iface: 'OptimismMintableERC20Factory',
signerOrProvider: deployer,
},
{
name: 'OptimismMintableERC20Factory',
},
])
try {
const tx = await proxyAdmin.upgrade(
mintableERC20FactoryProxy.address,
mintableERC20FactoryImpl.address
)
await tx.wait()
} catch (e) {
console.log('OptimismMintableERC20Factory already initialized')
}
const version = await mintableERC20FactoryProxy.callStatic.version()
console.log(`OptimismMintableERC20Factory version: ${version}`)
console.log('Upgraded OptimismMintableERC20Factory')
}
deployFn.tags = ['OptimismMintableERC20FactoryInitialize', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, l1CrossDomainMessengerProxy, l1CrossDomainMessengerImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'Proxy__OVM_L1CrossDomainMessenger',
iface: 'L1CrossDomainMessenger',
signerOrProvider: deployer,
},
{
name: 'L1CrossDomainMessenger',
},
])
const proxyType = await proxyAdmin.callStatic.proxyType(
l1CrossDomainMessengerProxy.address
)
if (proxyType !== 2) {
console.log(
`ProxyAdmin(${proxyAdmin.address}).setProxyType(${l1CrossDomainMessengerProxy.address}, 2)`
)
// Set the L1CrossDomainMessenger to the RESOLVED proxy type.
const tx = await proxyAdmin.setProxyType(
l1CrossDomainMessengerProxy.address,
2
)
await tx.wait()
}
const name = 'OVM_L1CrossDomainMessenger'
const implementationName = proxyAdmin.implementationName(
l1CrossDomainMessengerImpl.address
)
if (implementationName !== name) {
console.log(
`ProxyAdmin(${proxyAdmin.address}).setImplementationName(${l1CrossDomainMessengerImpl.address}, 'OVM_L1CrossDomainMessenger')`
)
const tx = await proxyAdmin.setImplementationName(
l1CrossDomainMessengerProxy.address,
name
)
await tx.wait()
}
try {
const tx = await proxyAdmin.upgradeAndCall(
l1CrossDomainMessengerProxy.address,
l1CrossDomainMessengerImpl.address,
l1CrossDomainMessengerImpl.interface.encodeFunctionData('initialize')
)
await tx.wait()
} catch (e) {
console.log('L1CrossDomainMessenger already initialized')
}
const version = await l1CrossDomainMessengerProxy.callStatic.version()
console.log(`L1CrossDomainMessenger version: ${version}`)
console.log('Upgraded L1CrossDomainMessenger')
}
deployFn.tags = ['L1CrossDomainMessengerInitialize', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, l2OutputOracleProxy, l2OutputOracleImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'L2OutputOracleProxy',
iface: 'L2OutputOracle',
signerOrProvider: deployer,
},
{
name: 'L2OutputOracle',
},
])
const startingBlockNumber = hre.deployConfig.l2OutputOracleStartingBlockNumber
let startingTimestamp = hre.deployConfig.l2OutputOracleStartingTimestamp
if (startingTimestamp < 0) {
const l1StartingBlock = await hre.ethers.provider.getBlock(
hre.deployConfig.l1StartingBlockTag
)
if (l1StartingBlock === null) {
throw new Error(
`Cannot fetch block tag ${hre.deployConfig.l1StartingBlockTag}`
)
}
startingTimestamp = l1StartingBlock.timestamp
}
try {
const tx = await proxyAdmin.upgradeAndCall(
l2OutputOracleProxy.address,
l2OutputOracleImpl.address,
l2OutputOracleProxy.interface.encodeFunctionData('initialize', [
startingBlockNumber,
startingTimestamp,
])
)
await tx.wait()
} catch (e) {
console.log('L2OutputOracle already initialized')
}
const fetchedStartingBlockNumber =
await l2OutputOracleProxy.callStatic.startingBlockNumber()
const fetchedStartingTimestamp =
await l2OutputOracleProxy.callStatic.startingTimestamp()
assert(fetchedStartingBlockNumber.toNumber() === startingBlockNumber)
assert(fetchedStartingTimestamp.toNumber() === startingTimestamp)
console.log('Updgraded and initialized L2OutputOracle')
const version = await l2OutputOracleProxy.callStatic.version()
console.log(`L2OutputOracle version: ${version}`)
}
deployFn.tags = ['L2OutputOracleInitialize', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, optimismPortalProxy, optimismPortalImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'OptimismPortalProxy',
iface: 'OptimismPortal',
signerOrProvider: deployer,
},
{
name: 'OptimismPortal',
},
])
// Initialize the portal, setting paused to false
try {
const tx = await proxyAdmin.upgradeAndCall(
optimismPortalProxy.address,
optimismPortalImpl.address,
optimismPortalProxy.interface.encodeFunctionData('initialize', [false])
)
await tx.wait()
} catch (e) {
console.log('OptimismPortal already initialized')
}
const isPaused = await optimismPortalProxy.callStatic.paused()
assert(isPaused === false)
console.log('Upgraded and initialized OptimismPortal')
const version = await optimismPortalProxy.callStatic.version()
console.log(`OptimismPortal version: ${version}`)
}
deployFn.tags = ['OptimismPortalInitialize', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { awaitCondition } from '@eth-optimism/core-utils'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin] = await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
])
const finalOwner = hre.deployConfig.finalSystemOwner
const proxyAdminOwner = await proxyAdmin.callStatic.owner()
if (proxyAdminOwner !== finalOwner) {
const tx = await proxyAdmin.transferOwnership(finalOwner)
await tx.wait()
await awaitCondition(
async () => {
return (await proxyAdmin.callStatic.owner()) === finalOwner
},
30000,
1000
)
}
}
deployFn.tags = ['ProxyAdmin', 'transferOwnership', 'l1']
export default deployFn
# `ResourceMetering` Invariants # `ResourceMetering` Invariants
## The base fee should increase if the last block used more than the target amount of gas
**Test:** [`ResourceMetering.t.sol#L180`](../contracts/test/invariants/ResourceMetering.t.sol#L180)
If the last block used more than the target amount of gas (and there were no empty blocks in between), ensure this block's baseFee increased, but not by more than the max amount per block.
## The base fee should decrease if the last block used less than the target amount of gas
**Test:** [`ResourceMetering.t.sol#L191`](../contracts/test/invariants/ResourceMetering.t.sol#L191)
If the previous block used less than the target amount of gas, the base fee should decrease, but not more than the max amount.
## A block's base fee should never be below `MINIMUM_BASE_FEE`
**Test:** [`ResourceMetering.t.sol#L201`](../contracts/test/invariants/ResourceMetering.t.sol#L201)
This test asserts that a block's base fee can never drop below the `MINIMUM_BASE_FEE` threshold.
## A block can never consume more than `MAX_RESOURCE_LIMIT` gas.
**Test:** [`ResourceMetering.t.sol#L211`](../contracts/test/invariants/ResourceMetering.t.sol#L211)
This test asserts that a block can never consume more than the `MAX_RESOURCE_LIMIT` gas threshold.
## The base fee can never be raised more than the max base fee change.
**Test:** [`ResourceMetering.t.sol#L222`](../contracts/test/invariants/ResourceMetering.t.sol#L222)
After a block consumes more gas than the target gas, the base fee cannot be raised more than the maximum amount allowed. The max base fee change (per-block) is derived as follows: `prevBaseFee / BASE_FEE_MAX_CHANGE_DENOMINATOR`
## The base fee can never be lowered more than the max base fee change.
**Test:** [`ResourceMetering.t.sol#L233`](../contracts/test/invariants/ResourceMetering.t.sol#L233)
After a block consumes less than the target gas, the base fee cannot be lowered more than the maximum amount allowed. The max base fee change (per-block) is derived as follows: `prevBaseFee / BASE_FEE_MAX_CHANGE_DENOMINATOR`
## The `maxBaseFeeChange` calculation over multiple blocks can never underflow.
**Test:** [`ResourceMetering.t.sol#L244`](../contracts/test/invariants/ResourceMetering.t.sol#L244)
When calculating the `maxBaseFeeChange` after multiple empty blocks, the calculation should never be allowed to underflow.
## The base fee should increase if the last block used more than the target amount of gas ## The base fee should increase if the last block used more than the target amount of gas
**Test:** [`FuzzResourceMetering.sol#L158`](../contracts/echidna/FuzzResourceMetering.sol#L158) **Test:** [`FuzzResourceMetering.sol#L158`](../contracts/echidna/FuzzResourceMetering.sol#L158)
......
#!/bin/sh
# RPC endpoint for the migration rehearsal network
export ETH_RPC_URL="https://mainnet-l1-rehearsal.optimism.io/"
# export ETH_RPC_URL="localhost:8545"
# Default HH key
HH_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
# Default HH addr
HH_ADDR="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
# SystemDictator contract (deployed by Maurelian on 2023-05-09)
MSD="0x49149a233de6E4cD6835971506F47EE5862289c1"
# ProxyAdmin contract
PROXY_ADMIN="0x43cA9bAe8dF108684E5EAaA720C25e1b32B0A075"
# AddressManager contract
ADDRESS_MANAGER="0xdE1FCfB0851916CA5101820A69b13a4E276bd81F"
# ResolvedDelegateProxy contract
RESOLVED_DELEGATE_PROXY="0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1"
# L1ChugSplashProxy contract
# use `setOwner(address)` for this one
L1_CHUG_SPLASH_PROXY="0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1"
# Proxy contract
# use `changeAdmin(address)` for this one
PROXY="0x5a7749f83b81B301cAb5f48EB8516B986DAef23D"
# OptimismPortal proxy
PORTAL_PROXY="0x59C4e2c6a6dC27c259D6d067a039c831e1ff4947"
# Check existing owners (should all be $HH_ADDR)
# cast call $PROXY_ADMIN "owner()(address)"
# cast call $ADDRESS_MANAGER "owner()(address)"
# cast admin $L1_CHUG_SPLASH_PROXY
# cast admin $PROXY
# ---
# Transfer ownership
# ---
# cast send --private-key $HH_KEY $PROXY_ADMIN "transferOwnership(address)" $MSD
# cast send --private-key $HH_KEY $ADDRESS_MANAGER "transferOwnership(address)" $MSD
# cast send --private-key $HH_KEY $L1_CHUG_SPLASH_PROXY "setOwner(address)" $MSD
# cast send --private-key $HH_KEY $PROXY "changeAdmin(address)" $MSD
# ---
# Execute Phase 1
# ---
# cast send --private-key $HH_KEY $MSD "phase1()"
# updateDynamicConfig signature
SIG="updateDynamicConfig((uint256,uint256),bool)"
# Encode calldata
CALLDATA=$(cast abi-encode $SIG "(17377105,1685641931)" true)
# Grab the selector
SELECTOR=$(cast sig $SIG)
# Prepare full payload
PAYLOAD=$(cast --concat-hex $SELECTOR $CALLDATA)
# Sanity check calldata
# cast pretty-calldata $PAYLOAD
# ---
# Update dynamic config
# ---
# cast send --private-key $HH_KEY $MSD $PAYLOAD
# ---
# !!!POINT OF NO RETURN!!!
# Execute phase 2
# ---
# cast send --private-key $HH_KEY $MSD "phase2()"
# ---
# Unpause the portal
# ---
# cast send --private-key $HH_KEY $PORTAL_PROXY "unpause()"
# ---
# Unpause Portal with CallForwarder
# ---
# Fetch portal guardian
# cast call $PORTAL_PROXY "GUARDIAN()(address)"
SIG="forward(address,bytes)"
FORWARD_SIG=$(cast sig $SIG)
CALLDATA=$(cast abi-encode $SIG $PORTAL_PROXY $(cast sig "unpause()"))
PAYLOAD=$(cast --concat-hex $FORWARD_SIG $CALLDATA)
# Sanity check calldata
# cast pretty-calldata $PAYLOAD
# Send unpause tx from multisig
# cast send "0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A" $PAYLOAD --private-key $HH_KEY
# Check if paused
# cast call $PORTAL_PROXY "paused()(bool)"
# Check bytecode of multisig
# cast code "0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A" --rpc-url https://mainnet-l1-rehearsal.optimism.io
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { console2 } from "forge-std/console2.sol";
import { Script } from "forge-std/Script.sol";
import { StdAssertions } from "forge-std/StdAssertions.sol";
/**
* @title BedrockMigrationChecker
* @notice A script to check safety of multisig operations for Bedrock.
* The usage is as follows:
* $ forge script scripts/CheckForBedrockMigration.s.sol \
* --rpc-url $ETH_RPC_URL
*/
contract BedrockMigrationChecker is Script, StdAssertions {
struct ContractSet {
// Please keep these sorted by name.
address AddressManager;
address L1CrossDomainMessengerImpl;
address L1CrossDomainMessengerProxy;
address L1ERC721BridgeImpl;
address L1ERC721BridgeProxy;
address L1ProxyAdmin;
address L1StandardBridgeImpl;
address L1StandardBridgeProxy;
address L1UpgradeKey;
address L2OutputOracleImpl;
address L2OutputOracleProxy;
address OptimismMintableERC20FactoryImpl;
address OptimismMintableERC20FactoryProxy;
address OptimismPortalImpl;
address OptimismPortalProxy;
address PortalSender;
address SystemConfigProxy;
address SystemDictatorImpl;
address SystemDictatorProxy;
}
/**
* @notice The entrypoint function.
*/
function run() external {
string memory bedrockJsonDir = vm.envString("BEDROCK_JSON_DIR");
console2.log("BEDROCK_JSON_DIR = %s", bedrockJsonDir);
ContractSet memory contracts = getContracts(bedrockJsonDir);
checkAddressManager(contracts);
checkL1CrossDomainMessengerImpl(contracts);
checkL1CrossDomainMessengerProxy(contracts);
checkL1ERC721BridgeImpl(contracts);
checkL1ERC721BridgeProxy(contracts);
checkL1ProxyAdmin(contracts);
checkL1StandardBridgeImpl(contracts);
checkL1StandardBridgeProxy(contracts);
checkL1UpgradeKey(contracts);
checkL2OutputOracleImpl(contracts);
checkL2OutputOracleProxy(contracts);
checkOptimismMintableERC20FactoryImpl(contracts);
checkOptimismMintableERC20FactoryProxy(contracts);
checkOptimismPortalImpl(contracts);
checkOptimismPortalProxy(contracts);
checkPortalSender(contracts);
checkSystemConfigProxy(contracts);
checkSystemDictatorImpl(contracts);
checkSystemDictatorProxy(contracts);
}
function checkAddressManager(ContractSet memory contracts) internal {
console2.log("Checking AddressManager %s", contracts.AddressManager);
checkAddressIsExpected(contracts.L1UpgradeKey, contracts.AddressManager, "owner()");
}
function checkL1CrossDomainMessengerImpl(ContractSet memory contracts) internal {
console2.log("Checking L1CrossDomainMessenger %s", contracts.L1CrossDomainMessengerImpl);
checkAddressIsExpected(contracts.OptimismPortalProxy, contracts.L1CrossDomainMessengerImpl, "PORTAL()");
}
function checkL1CrossDomainMessengerProxy(ContractSet memory contracts) internal {
console2.log("Checking L1CrossDomainMessengerProxy %s", contracts.L1CrossDomainMessengerProxy);
checkAddressIsExpected(contracts.L1UpgradeKey, contracts.L1CrossDomainMessengerProxy, "owner()");
checkAddressIsExpected(contracts.AddressManager, contracts.L1CrossDomainMessengerProxy, "libAddressManager()");
}
function checkL1ERC721BridgeImpl(ContractSet memory contracts) internal {
console2.log("Checking L1ERC721Bridge %s", contracts.L1ERC721BridgeImpl);
checkAddressIsExpected(contracts.L1CrossDomainMessengerProxy, contracts.L1ERC721BridgeImpl, "messenger()");
}
function checkL1ERC721BridgeProxy(ContractSet memory contracts) internal {
console2.log("Checking L1ERC721BridgeProxy %s", contracts.L1ERC721BridgeProxy);
checkAddressIsExpected(contracts.L1UpgradeKey, contracts.L1ERC721BridgeProxy, "admin()");
checkAddressIsExpected(contracts.L1CrossDomainMessengerProxy, contracts.L1ERC721BridgeProxy, "messenger()");
}
function checkL1ProxyAdmin(ContractSet memory contracts) internal {
console2.log("Checking L1ProxyAdmin %s", contracts.L1ProxyAdmin);
checkAddressIsExpected(contracts.L1UpgradeKey, contracts.L1ProxyAdmin, "owner()");
}
function checkL1StandardBridgeImpl(ContractSet memory contracts) internal {
console2.log("Checking L1StandardBridge %s", contracts.L1StandardBridgeImpl);
checkAddressIsExpected(contracts.L1CrossDomainMessengerProxy, contracts.L1StandardBridgeImpl, "messenger()");
}
function checkL1StandardBridgeProxy(ContractSet memory contracts) internal {
console2.log("Checking L1StandardBridgeProxy %s", contracts.L1StandardBridgeProxy);
checkAddressIsExpected(contracts.L1UpgradeKey, contracts.L1StandardBridgeProxy, "getOwner()");
checkAddressIsExpected(contracts.L1CrossDomainMessengerProxy, contracts.L1StandardBridgeProxy, "messenger()");
}
function checkL1UpgradeKey(ContractSet memory contracts) internal {
console2.log("Checking L1UpgradeKeyAddress %s", contracts.L1UpgradeKey);
// No need to check anything here, so just printing the address.
}
function checkL2OutputOracleImpl(ContractSet memory contracts) internal {
console2.log("Checking L2OutputOracle %s", contracts.L2OutputOracleImpl);
checkAddressIsExpected(contracts.L1UpgradeKey, contracts.L2OutputOracleImpl, "CHALLENGER()");
// 604800 seconds = 7 days, reusing the logic in
// checkAddressIsExpected for simplicity.
checkAddressIsExpected(address(604800), contracts.L2OutputOracleImpl, "FINALIZATION_PERIOD_SECONDS()");
}
function checkL2OutputOracleProxy(ContractSet memory contracts) internal {
console2.log("Checking L2OutputOracleProxy %s", contracts.L2OutputOracleProxy);
checkAddressIsExpected(contracts.L1ProxyAdmin, contracts.L2OutputOracleProxy, "admin()");
}
function checkOptimismMintableERC20FactoryImpl(ContractSet memory contracts) internal {
console2.log("Checking OptimismMintableERC20Factory %s", contracts.OptimismMintableERC20FactoryImpl);
checkAddressIsExpected(contracts.L1StandardBridgeProxy, contracts.OptimismMintableERC20FactoryImpl, "BRIDGE()");
}
function checkOptimismMintableERC20FactoryProxy(ContractSet memory contracts) internal {
console2.log("Checking OptimismMintableERC20FactoryProxy %s", contracts.OptimismMintableERC20FactoryProxy);
checkAddressIsExpected(contracts.L1ProxyAdmin, contracts.OptimismMintableERC20FactoryProxy, "admin()");
}
function checkOptimismPortalImpl(ContractSet memory contracts) internal {
console2.log("Checking OptimismPortal %s", contracts.OptimismPortalImpl);
checkAddressIsExpected(contracts.L2OutputOracleProxy, contracts.OptimismPortalImpl, "L2_ORACLE()");
}
function checkOptimismPortalProxy(ContractSet memory contracts) internal {
console2.log("Checking OptimismPortalProxy %s", contracts.OptimismPortalProxy);
checkAddressIsExpected(contracts.L1ProxyAdmin, contracts.OptimismPortalProxy, "admin()");
}
function checkPortalSender(ContractSet memory contracts) internal {
console2.log("Checking PortalSender %s", contracts.PortalSender);
checkAddressIsExpected(contracts.OptimismPortalProxy, contracts.PortalSender, "PORTAL()");
}
function checkSystemConfigProxy(ContractSet memory contracts) internal {
console2.log("Checking SystemConfigProxy %s", contracts.SystemConfigProxy);
checkAddressIsExpected(contracts.L1ProxyAdmin, contracts.SystemConfigProxy, "admin()");
}
function checkSystemDictatorImpl(ContractSet memory contracts) internal {
console2.log("Checking SystemDictator %s", contracts.SystemDictatorImpl);
checkAddressIsExpected(address(0), contracts.SystemDictatorImpl, "owner()");
}
function checkSystemDictatorProxy(ContractSet memory contracts) internal {
console2.log("Checking SystemDictatorProxy %s", contracts.SystemDictatorProxy);
checkAddressIsExpected(contracts.SystemDictatorImpl, contracts.SystemDictatorProxy, "implementation()");
checkAddressIsExpected(contracts.L1UpgradeKey, contracts.SystemDictatorProxy, "owner()");
checkAddressIsExpected(contracts.L1UpgradeKey, contracts.SystemDictatorProxy, "admin()");
}
function checkAddressIsExpected(address expectedAddr, address contractAddr, string memory signature) internal {
address actual = getAddressFromCall(contractAddr, signature);
if (expectedAddr != actual) {
console2.log(" !! Error: %s != %s.%s, ", expectedAddr, contractAddr, signature);
console2.log(" which is %s", actual);
} else {
console2.log(" -- Success: %s == %s.%s.", expectedAddr, contractAddr, signature);
}
}
function getAddressFromCall(address contractAddr, string memory signature) internal returns (address) {
vm.prank(address(0));
(bool success, bytes memory addrBytes) = contractAddr.staticcall(abi.encodeWithSignature(signature));
if (!success) {
console2.log(" !! Error calling %s.%s", contractAddr, signature);
return address(0);
}
return abi.decode(addrBytes, (address));
}
function getContracts(string memory bedrockJsonDir) internal returns (ContractSet memory) {
return ContractSet({
AddressManager: getAddressFromJson(string.concat(bedrockJsonDir, "/AddressManager.json")),
L1CrossDomainMessengerImpl: getAddressFromJson(string.concat(bedrockJsonDir, "/L1CrossDomainMessenger.json")),
L1CrossDomainMessengerProxy: getAddressFromJson(string.concat(bedrockJsonDir, "/L1CrossDomainMessengerProxy.json")),
L1ERC721BridgeImpl: getAddressFromJson(string.concat(bedrockJsonDir, "/L1ERC721Bridge.json")),
L1ERC721BridgeProxy: getAddressFromJson(string.concat(bedrockJsonDir, "/L1ERC721BridgeProxy.json")),
L1ProxyAdmin: getAddressFromJson(string.concat(bedrockJsonDir, "/ProxyAdmin.json")),
L1StandardBridgeImpl: getAddressFromJson(string.concat(bedrockJsonDir, "/L1StandardBridge.json")),
L1StandardBridgeProxy: getAddressFromJson(string.concat(bedrockJsonDir, "/L1StandardBridgeProxy.json")),
L1UpgradeKey: vm.envAddress("L1_UPGRADE_KEY"),
L2OutputOracleImpl: getAddressFromJson(string.concat(bedrockJsonDir, "/L2OutputOracle.json")),
L2OutputOracleProxy: getAddressFromJson(string.concat(bedrockJsonDir, "/L2OutputOracleProxy.json")),
OptimismMintableERC20FactoryImpl: getAddressFromJson(string.concat(bedrockJsonDir, "/OptimismMintableERC20Factory.json")),
OptimismMintableERC20FactoryProxy: getAddressFromJson(string.concat(bedrockJsonDir, "/OptimismMintableERC20FactoryProxy.json")),
OptimismPortalImpl: getAddressFromJson(string.concat(bedrockJsonDir, "/OptimismPortal.json")),
OptimismPortalProxy: getAddressFromJson(string.concat(bedrockJsonDir, "/OptimismPortalProxy.json")),
PortalSender: getAddressFromJson(string.concat(bedrockJsonDir, "/PortalSender.json")),
SystemConfigProxy: getAddressFromJson(string.concat(bedrockJsonDir, "/SystemConfigProxy.json")),
SystemDictatorImpl: getAddressFromJson(string.concat(bedrockJsonDir, "/SystemDictator.json")),
SystemDictatorProxy: getAddressFromJson(string.concat(bedrockJsonDir, "/SystemDictatorProxy.json"))
});
}
function getAddressFromJson(string memory jsonPath) internal returns (address) {
string memory json = vm.readFile(jsonPath);
return vm.parseJsonAddress(json, ".address");
}
}
## Multisig Operation Scripts
A collection of scripts used by multisig signers to verify the
integrity of the transactions to be signed.
### Contract Verification for Bedrock Migration
[CheckForBedrockMigration.s.sol](./CheckForBedrockMigration.s.sol) is
a script used by the Bedrock migration signers before the migration,
to verify the contracts affected by the migration are always under the
control of the multisig, and security critical configurations are
correctly initialized.
Example usage:
``` bash
git clone git@github.com:ethereum-optimism/optimism.git
cd optimism/
git pull
git checkout develop
nvm use
yarn install
yarn clean
yarn build
cd packages/contracts-bedrock
export L1_UPGRADE_KEY=0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A
export BEDROCK_JSON_DIR=deployments/mainnet
forge script scripts/multisig/CheckForBedrockMigration.s.sol --rpc-url <TRUSTWORTHY_L1_RPC_URL>
```
Expected output:
``` bash
Script ran successfully.
BEDROCK_JSON_DIR = deployments/mainnet
Checking AddressManager 0xdE1FCfB0851916CA5101820A69b13a4E276bd81F
-- Success: 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A == 0xdE1FCfB0851916CA5101820A69b13a4E276bd81F.owner().
Checking L1CrossDomainMessenger 0x2150Bc3c64cbfDDbaC9815EF615D6AB8671bfe43
-- Success: 0xbEb5Fc579115071764c7423A4f12eDde41f106Ed == 0x2150Bc3c64cbfDDbaC9815EF615D6AB8671bfe43.PORTAL().
Checking L1CrossDomainMessengerProxy 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1
-- Success: 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A == 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1.owner().
-- Success: 0xdE1FCfB0851916CA5101820A69b13a4E276bd81F == 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1.libAddressManager().
Checking L1ERC721Bridge 0x4afDD3A48E13B305e98D9EEad67B1b5867E370DF
-- Success: 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1 == 0x4afDD3A48E13B305e98D9EEad67B1b5867E370DF.messenger().
Checking L1ERC721BridgeProxy 0x5a7749f83b81B301cAb5f48EB8516B986DAef23D
-- Success: 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A == 0x5a7749f83b81B301cAb5f48EB8516B986DAef23D.admin().
-- Success: 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1 == 0x5a7749f83b81B301cAb5f48EB8516B986DAef23D.messenger().
Checking L1ProxyAdmin 0x543bA4AADBAb8f9025686Bd03993043599c6fB04
-- Success: 0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB == 0x543bA4AADBAb8f9025686Bd03993043599c6fB04.owner().
Checking L1StandardBridge 0xBFB731Cd36D26c2a7287716DE857E4380C73A64a
-- Success: 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1 == 0xBFB731Cd36D26c2a7287716DE857E4380C73A64a.messenger().
Checking L1StandardBridgeProxy 0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1
-- Success: 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A == 0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1.getOwner().
-- Success: 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1 == 0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1.messenger().
Checking L1UpgradeKeyAddress 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A
Checking L2OutputOracle 0xd2E67B6a032F0A9B1f569E63ad6C38f7342c2e00
-- Success: 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A == 0xd2E67B6a032F0A9B1f569E63ad6C38f7342c2e00.CHALLENGER().
-- Success: 0x0000000000000000000000000000000000093A80 == 0xd2E67B6a032F0A9B1f569E63ad6C38f7342c2e00.FINALIZATION_PERIOD_SECONDS().
Checking L2OutputOracleProxy 0xdfe97868233d1aa22e815a266982f2cf17685a27
-- Success: 0x543bA4AADBAb8f9025686Bd03993043599c6fB04 == 0xdfe97868233d1aa22e815a266982f2cf17685a27.admin().
Checking OptimismMintableERC20Factory 0xaE849EFA4BcFc419593420e14707996936E365E2
-- Success: 0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1 == 0xaE849EFA4BcFc419593420e14707996936E365E2.BRIDGE().
Checking OptimismMintableERC20FactoryProxy 0x75505a97BD334E7BD3C476893285569C4136Fa0F
-- Success: 0x543bA4AADBAb8f9025686Bd03993043599c6fB04 == 0x75505a97BD334E7BD3C476893285569C4136Fa0F.admin().
Checking OptimismPortal 0x28a55488fef40005309e2DA0040DbE9D300a64AB
-- Success: 0xdfe97868233d1aa22e815a266982f2cf17685a27 == 0x28a55488fef40005309e2DA0040DbE9D300a64AB.L2_ORACLE().
Checking OptimismPortalProxy 0xbEb5Fc579115071764c7423A4f12eDde41f106Ed
-- Success: 0x543bA4AADBAb8f9025686Bd03993043599c6fB04 == 0xbEb5Fc579115071764c7423A4f12eDde41f106Ed.admin().
Checking PortalSender 0x0A893d9576b9cFD9EF78595963dc973238E78210
-- Success: 0xbEb5Fc579115071764c7423A4f12eDde41f106Ed == 0x0A893d9576b9cFD9EF78595963dc973238E78210.PORTAL().
Checking SystemConfigProxy 0x229047fed2591dbec1eF1118d64F7aF3dB9EB290
-- Success: 0x543bA4AADBAb8f9025686Bd03993043599c6fB04 == 0x229047fed2591dbec1eF1118d64F7aF3dB9EB290.admin().
Checking SystemDictator 0x09E040a72FD3492355C5aEEdbC3154075f83488a
-- Success: 0x0000000000000000000000000000000000000000 == 0x09E040a72FD3492355C5aEEdbC3154075f83488a.owner().
Checking SystemDictatorProxy 0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB
-- Success: 0x09E040a72FD3492355C5aEEdbC3154075f83488a == 0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB.implementation().
-- Success: 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A == 0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB.owner().
-- Success: 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A == 0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB.admin().
```
{
"version": "1.0",
"chainId": "1",
"meta": {
"name": "batch1",
"description": "ProxyAdmin.transferOwnership, AddressManager.transferOwnership, L1StandardBridgeProxy.setOwner, L1ERC721BridgeProxy.changeAdmin, and SystemDictatorProxy.phase1.",
"txBuilderVersion": "1.13.3"
},
"transactions": [
{
"to": "0x543bA4AADBAb8f9025686Bd03993043599c6fB04",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"payable": false
},
"contractInputsValues": {
"newOwner": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB"
}
},
{
"to": "0xdE1FCfB0851916CA5101820A69b13a4E276bd81F",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"payable": false
},
"contractInputsValues": {
"newOwner": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB"
}
},
{
"to": "0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [
{
"internalType": "address",
"name": "_owner",
"type": "address"
}
],
"name": "setOwner",
"payable": false
},
"contractInputsValues": {
"_owner": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB"
}
},
{
"to": "0x5a7749f83b81B301cAb5f48EB8516B986DAef23D",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [
{
"internalType": "address",
"name": "_admin",
"type": "address"
}
],
"name": "changeAdmin",
"payable": false
},
"contractInputsValues": {
"_admin": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB"
}
},
{
"to": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [],
"name": "phase1",
"payable": false
},
"contractInputsValues": {}
}
]
}
{
"version": "1.0",
"chainId": "1",
"meta": {
"name": "batch2",
"description": "SystemDictatorProxy.updateDynamicConfig.",
"txBuilderVersion": "1.13.3"
},
"transactions": [
{
"to": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [
{
"components": [
{
"internalType": "uint256",
"name": "l2OutputOracleStartingBlockNumber",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "l2OutputOracleStartingTimestamp",
"type": "uint256"
}
],
"internalType": "struct SystemDictator.L2OutputOracleDynamicConfig",
"name": "_l2OutputOracleDynamicConfig",
"type": "tuple"
},
{
"internalType": "bool",
"name": "_optimismPortalDynamicConfig",
"type": "bool"
}
],
"name": "updateDynamicConfig",
"payable": false
},
"contractInputsValues": {
"_l2OutputOracleDynamicConfig": "[105235063, 1686068903]",
"_optimismPortalDynamicConfig": "true"
}
}
]
}
{
"version": "1.0",
"chainId": "1",
"meta": {
"name": "batch3",
"description": "SystemDictatorProxy.phase2 and OptimismPortalProxy.unpause.",
"txBuilderVersion": "1.13.3"
},
"transactions": [
{
"to": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [],
"name": "phase2",
"payable": false
},
"contractInputsValues": {}
},
{
"to": "0xbEb5Fc579115071764c7423A4f12eDde41f106Ed",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [],
"name": "unpause",
"payable": false
},
"contractInputsValues": {}
}
]
}
{
"version": "1.0",
"chainId": "1",
"meta": {
"name": "EscapeHatch",
"description": "SystemDictatorProxy.exit1 and finalize.",
"txBuilderVersion": "1.14.1"
},
"transactions": [
{
"to": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [],
"name": "exit1",
"payable": false
},
"contractInputsValues": null
},
{
"to": "0xB4453CEb33d2e67FA244A24acf2E50CEF31F53cB",
"value": "0",
"data": null,
"contractMethod": {
"inputs": [],
"name": "finalize",
"payable": false
},
"contractInputsValues": null
}
]
}
...@@ -32,7 +32,6 @@ contracts=( ...@@ -32,7 +32,6 @@ contracts=(
contracts/universal/OptimismMintableERC20.sol:OptimismMintableERC20 contracts/universal/OptimismMintableERC20.sol:OptimismMintableERC20
contracts/universal/OptimismMintableERC20Factory.sol:OptimismMintableERC20Factory contracts/universal/OptimismMintableERC20Factory.sol:OptimismMintableERC20Factory
contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory
contracts/dispute/BondManager.sol:BondManager
) )
dir=$(dirname "$0") dir=$(dirname "$0")
......
...@@ -110,6 +110,11 @@ interface RequiredDeployConfig { ...@@ -110,6 +110,11 @@ interface RequiredDeployConfig {
*/ */
l2OutputOracleChallenger: string l2OutputOracleChallenger: string
/**
* Whether to enable governance token predeploy.
*/
enableGovernance: boolean
/** /**
* ERC20 symbol used for the L2 GovernanceToken. * ERC20 symbol used for the L2 GovernanceToken.
*/ */
...@@ -414,13 +419,15 @@ export const deployConfigSpec: { ...@@ -414,13 +419,15 @@ export const deployConfigSpec: {
type: 'number', type: 'number',
default: 1_000_000, default: 1_000_000,
}, },
enableGovernance: {
type: 'boolean',
default: false,
},
governanceTokenSymbol: { governanceTokenSymbol: {
type: 'string', type: 'string',
default: 'OP',
}, },
governanceTokenName: { governanceTokenName: {
type: 'string', type: 'string',
default: 'Optimism',
}, },
governanceTokenOwner: { governanceTokenOwner: {
type: 'string', type: 'string',
......
...@@ -4,7 +4,7 @@ import { URLSearchParams } from 'url' ...@@ -4,7 +4,7 @@ import { URLSearchParams } from 'url'
import { ethers, Contract } from 'ethers' import { ethers, Contract } from 'ethers'
import { Provider } from '@ethersproject/abstract-provider' import { Provider } from '@ethersproject/abstract-provider'
import { Signer } from '@ethersproject/abstract-signer' import { Signer } from '@ethersproject/abstract-signer'
import { awaitCondition, sleep } from '@eth-optimism/core-utils' import { sleep } from '@eth-optimism/core-utils'
import { HardhatRuntimeEnvironment } from 'hardhat/types' import { HardhatRuntimeEnvironment } from 'hardhat/types'
import { Deployment, DeployResult } from 'hardhat-deploy/dist/types' import { Deployment, DeployResult } from 'hardhat-deploy/dist/types'
import 'hardhat-deploy' import 'hardhat-deploy'
...@@ -37,7 +37,7 @@ export const deploy = async ({ ...@@ -37,7 +37,7 @@ export const deploy = async ({
contract?: string contract?: string
iface?: string iface?: string
postDeployAction?: (contract: Contract) => Promise<void> postDeployAction?: (contract: Contract) => Promise<void>
}) => { }): Promise<Contract> => {
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
// Hardhat deploy will usually do this check for us, but currently doesn't also consider // Hardhat deploy will usually do this check for us, but currently doesn't also consider
...@@ -323,35 +323,35 @@ export const printJsonTransaction = (tx: ethers.PopulatedTransaction): void => { ...@@ -323,35 +323,35 @@ export const printJsonTransaction = (tx: ethers.PopulatedTransaction): void => {
} }
/** /**
* Mini helper for transferring a Proxy to the MSD * Helper for transferring a Proxy to a target contract.
* *
* @param opts Options for executing the step. * @param opts Options for executing the step.
* @param opts.isLiveDeployer True if the deployer is live. * @param opts.isLiveDeployer True if the deployer is live.
* @param opts.proxy proxy contract. * @param opts.proxy proxy contract.
* @param opts.dictator dictator contract. * @param opts.target target contract.
*/ */
export const doOwnershipTransfer = async (opts: { export const doOwnershipTransfer = async (opts: {
isLiveDeployer?: boolean isLiveDeployer?: boolean
proxy: ethers.Contract proxy: ethers.Contract
name: string name: string
transferFunc: string transferFunc: string
dictator: ethers.Contract target: ethers.Contract
}): Promise<void> => { }): Promise<void> => {
if (opts.isLiveDeployer) { if (opts.isLiveDeployer) {
console.log(`Setting ${opts.name} owner to MSD`) console.log(`Setting ${opts.name} owner to target ${opts.target.address}`)
await opts.proxy[opts.transferFunc](opts.dictator.address) await opts.proxy[opts.transferFunc](opts.target.address)
} else { } else {
const tx = await opts.proxy.populateTransaction[opts.transferFunc]( const tx = await opts.proxy.populateTransaction[opts.transferFunc](
opts.dictator.address opts.target.address
) )
console.log(` console.log(`
Please transfer ${opts.name} (proxy) owner to MSD Please transfer ${opts.name} (proxy) owner to MSD
- ${opts.name} address: ${opts.proxy.address} - ${opts.name} address: ${opts.proxy.address}
- MSD address: ${opts.dictator.address} - target address: ${opts.target.address}
`) `)
printJsonTransaction(tx) printJsonTransaction(tx)
printCastCommand(tx) printCastCommand(tx)
await printTenderlySimulationLink(opts.dictator.provider, tx) await printTenderlySimulationLink(opts.target.provider, tx)
} }
} }
...@@ -377,146 +377,6 @@ export const liveDeployer = async (opts: { ...@@ -377,146 +377,6 @@ export const liveDeployer = async (opts: {
return ret return ret
} }
/**
* Mini helper for checking if the current step is a target step.
*
* @param dictator SystemDictator contract.
* @param step Target step.
* @returns True if the current step is the target step.
*/
export const isStep = async (
dictator: ethers.Contract,
step: number
): Promise<boolean> => {
return (await dictator.currentStep()) === step
}
/**
* Mini helper for checking if the current step is the first step in target phase.
*
* @param dictator SystemDictator contract.
* @param phase Target phase.
* @returns True if the current step is the first step in target phase.
*/
export const isStartOfPhase = async (
dictator: ethers.Contract,
phase: number
): Promise<boolean> => {
const phaseToStep = {
1: 1,
2: 3,
3: 6,
}
return (await dictator.currentStep()) === phaseToStep[phase]
}
/**
* Mini helper for executing a given step.
*
* @param opts Options for executing the step.
* @param opts.isLiveDeployer True if the deployer is live.
* @param opts.SystemDictator SystemDictator contract.
* @param opts.step Step to execute.
* @param opts.message Message to print before executing the step.
* @param opts.checks Checks to perform after executing the step.
*/
export const doStep = async (opts: {
isLiveDeployer?: boolean
SystemDictator: ethers.Contract
step: number
message: string
checks: () => Promise<void>
}): Promise<void> => {
const isStepVal = await isStep(opts.SystemDictator, opts.step)
if (!isStepVal) {
console.log(`Step already completed: ${opts.step}`)
return
}
// Extra message to help the user understand what's going on.
console.log(opts.message)
// Either automatically or manually execute the step.
if (opts.isLiveDeployer) {
console.log(`Executing step ${opts.step}...`)
await opts.SystemDictator[`step${opts.step}`]()
} else {
const tx = await opts.SystemDictator.populateTransaction[
`step${opts.step}`
]()
console.log(`Please execute step ${opts.step}...`)
console.log(`MSD address: ${opts.SystemDictator.address}`)
printJsonTransaction(tx)
printCastCommand(tx)
await printTenderlySimulationLink(opts.SystemDictator.provider, tx)
}
// Wait for the step to complete.
await awaitCondition(
async () => {
return isStep(opts.SystemDictator, opts.step + 1)
},
30000,
1000
)
// Perform post-step checks.
await opts.checks()
}
/**
* Mini helper for executing a given phase.
*
* @param opts Options for executing the step.
* @param opts.isLiveDeployer True if the deployer is live.
* @param opts.SystemDictator SystemDictator contract.
* @param opts.step Step to execute.
* @param opts.message Message to print before executing the step.
* @param opts.checks Checks to perform after executing the step.
*/
export const doPhase = async (opts: {
isLiveDeployer?: boolean
SystemDictator: ethers.Contract
phase: number
message: string
checks: () => Promise<void>
}): Promise<void> => {
const isStart = await isStartOfPhase(opts.SystemDictator, opts.phase)
if (!isStart) {
console.log(`Start of phase ${opts.phase} already completed`)
return
}
// Extra message to help the user understand what's going on.
console.log(opts.message)
// Either automatically or manually execute the step.
if (opts.isLiveDeployer) {
console.log(`Executing phase ${opts.phase}...`)
await opts.SystemDictator[`phase${opts.phase}`]()
} else {
const tx = await opts.SystemDictator.populateTransaction[
`phase${opts.phase}`
]()
console.log(`Please execute phase ${opts.phase}...`)
console.log(`MSD address: ${opts.SystemDictator.address}`)
printJsonTransaction(tx)
await printTenderlySimulationLink(opts.SystemDictator.provider, tx)
}
// Wait for the step to complete.
await awaitCondition(
async () => {
return isStartOfPhase(opts.SystemDictator, opts.phase + 1)
},
30000,
1000
)
// Perform post-step checks.
await opts.checks()
}
/** /**
* Prints a direct link to a Tenderly simulation. * Prints a direct link to a Tenderly simulation.
* *
......
...@@ -465,46 +465,6 @@ const check = { ...@@ -465,46 +465,6 @@ const check = {
await checkProxy(hre, 'L1Block', signer.provider) await checkProxy(hre, 'L1Block', signer.provider)
await assertProxy(hre, 'L1Block', signer.provider) await assertProxy(hre, 'L1Block', signer.provider)
}, },
// LegacyERC20ETH
// - not behind a proxy
// - check name
// - check symbol
// - check decimals
// - check BRIDGE
// - check REMOTE_TOKEN
// - totalSupply should be set to 0
LegacyERC20ETH: async (hre: HardhatRuntimeEnvironment, signer: Signer) => {
const LegacyERC20ETH = await hre.ethers.getContractAt(
'LegacyERC20ETH',
predeploys.LegacyERC20ETH,
signer
)
const name = await LegacyERC20ETH.name()
assert(name === 'Ether')
console.log(` - name: ${name}`)
const symbol = await LegacyERC20ETH.symbol()
assert(symbol === 'ETH')
console.log(` - symbol: ${symbol}`)
const decimals = await LegacyERC20ETH.decimals()
assert(decimals === 18)
console.log(` - decimals: ${decimals}`)
const BRIDGE = await LegacyERC20ETH.BRIDGE()
assert(BRIDGE === predeploys.L2StandardBridge)
const REMOTE_TOKEN = await LegacyERC20ETH.REMOTE_TOKEN()
assert(REMOTE_TOKEN === hre.ethers.constants.AddressZero)
const totalSupply = await LegacyERC20ETH.totalSupply()
assert(totalSupply.eq(0))
console.log(` - totalSupply: ${totalSupply}`)
await checkProxy(hre, 'LegacyERC20ETH', signer.provider)
// No proxy at this address, don't call assertProxy
},
// WETH9 // WETH9
// - check name // - check name
// - check symbol // - check symbol
......
...@@ -48,3 +48,14 @@ export const reqenv = (name: string): string => { ...@@ -48,3 +48,14 @@ export const reqenv = (name: string): string => {
export const getenv = (name: string, fallback?: string): string | undefined => { export const getenv = (name: string, fallback?: string): string | undefined => {
return process.env[name] || fallback return process.env[name] || fallback
} }
/**
* Returns true if the given string is a valid address.
*
* @param a First address to check.
* @param b Second address to check.
* @returns True if the given addresses match.
*/
export const compareAddrs = (a: string, b: string): boolean => {
return a.toLowerCase() === b.toLowerCase()
}
...@@ -89,6 +89,52 @@ Cache use Redis and can be enabled for the following immutable methods: ...@@ -89,6 +89,52 @@ Cache use Redis and can be enabled for the following immutable methods:
* `eth_getUncleByBlockHashAndIndex` * `eth_getUncleByBlockHashAndIndex`
* `debug_getRawReceipts` (block hash only) * `debug_getRawReceipts` (block hash only)
## Meta method `consensus_getReceipts`
To support backends with different specifications in the same backend group,
proxyd exposes a convenient method to fetch receipts abstracting away
what specific backend will serve the request.
Each backend specifies their preferred method to fetch receipts with `consensus_receipts_target` config,
which will be translated from `consensus_getReceipts`.
This method takes a `blockNumberOrHash` (i.e. `tag|qty|hash`)
and returns the receipts for all transactions in the block.
Request example
```json
{
"jsonrpc":"2.0",
"id": 1,
"params": ["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"]
}
```
It currently supports translation to the following targets:
* `debug_getRawReceipts(blockOrHash)` (default)
* `alchemy_getTransactionReceipts(blockOrHash)`
* `parity_getBlockReceipts(blockOrHash)`
* `eth_getBlockReceipts(blockOrHash)`
The selected target is returned in the response, in a wrapped result.
Response example
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"method": "debug_getRawReceipts",
"result": {
// the actual raw result from backend
}
}
}
```
See [op-node receipt fetcher](https://github.com/ethereum-optimism/optimism/blob/186e46a47647a51a658e699e9ff047d39444c2de/op-node/sources/receipts.go#L186-L253).
## Metrics ## Metrics
See `metrics.go` for a list of all available metrics. See `metrics.go` for a list of all available metrics.
......
...@@ -18,6 +18,8 @@ import ( ...@@ -18,6 +18,8 @@ import (
"time" "time"
sw "github.com/ethereum-optimism/optimism/proxyd/pkg/avg-sliding-window" sw "github.com/ethereum-optimism/optimism/proxyd/pkg/avg-sliding-window"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
...@@ -97,6 +99,9 @@ var ( ...@@ -97,6 +99,9 @@ var (
} }
ErrBackendUnexpectedJSONRPC = errors.New("backend returned an unexpected JSON-RPC response") ErrBackendUnexpectedJSONRPC = errors.New("backend returned an unexpected JSON-RPC response")
ErrConsensusGetReceiptsCantBeBatched = errors.New("consensus_getReceipts cannot be batched")
ErrConsensusGetReceiptsInvalidTarget = errors.New("unsupported consensus_receipts_target")
) )
func ErrInvalidRequest(msg string) *RPCErr { func ErrInvalidRequest(msg string) *RPCErr {
...@@ -118,6 +123,7 @@ func ErrInvalidParams(msg string) *RPCErr { ...@@ -118,6 +123,7 @@ func ErrInvalidParams(msg string) *RPCErr {
type Backend struct { type Backend struct {
Name string Name string
rpcURL string rpcURL string
receiptsTarget string
wsURL string wsURL string
authUsername string authUsername string
authPassword string authPassword string
...@@ -208,7 +214,7 @@ func WithProxydIP(ip string) BackendOpt { ...@@ -208,7 +214,7 @@ func WithProxydIP(ip string) BackendOpt {
} }
} }
func WithSkipPeerCountCheck(skipPeerCountCheck bool) BackendOpt { func WithConsensusSkipPeerCountCheck(skipPeerCountCheck bool) BackendOpt {
return func(b *Backend) { return func(b *Backend) {
b.skipPeerCountCheck = skipPeerCountCheck b.skipPeerCountCheck = skipPeerCountCheck
} }
...@@ -232,12 +238,36 @@ func WithMaxErrorRateThreshold(maxErrorRateThreshold float64) BackendOpt { ...@@ -232,12 +238,36 @@ func WithMaxErrorRateThreshold(maxErrorRateThreshold float64) BackendOpt {
} }
} }
func WithConsensusReceiptTarget(receiptsTarget string) BackendOpt {
return func(b *Backend) {
b.receiptsTarget = receiptsTarget
}
}
type indexedReqRes struct { type indexedReqRes struct {
index int index int
req *RPCReq req *RPCReq
res *RPCRes res *RPCRes
} }
const ConsensusGetReceiptsMethod = "consensus_getReceipts"
const ReceiptsTargetDebugGetRawReceipts = "debug_getRawReceipts"
const ReceiptsTargetAlchemyGetTransactionReceipts = "alchemy_getTransactionReceipts"
const ReceiptsTargetParityGetTransactionReceipts = "parity_getBlockReceipts"
const ReceiptsTargetEthGetTransactionReceipts = "eth_getBlockReceipts"
type ConsensusGetReceiptsResult struct {
Method string `json:"method"`
Result interface{} `json:"result"`
}
// BlockHashOrNumberParameter is a non-conventional wrapper used by alchemy_getTransactionReceipts
type BlockHashOrNumberParameter struct {
BlockHash *common.Hash `json:"blockHash"`
BlockNumber *rpc.BlockNumber `json:"blockNumber"`
}
func NewBackend( func NewBackend(
name string, name string,
rpcURL string, rpcURL string,
...@@ -266,9 +296,7 @@ func NewBackend( ...@@ -266,9 +296,7 @@ func NewBackend(
networkErrorsSlidingWindow: sw.NewSlidingWindow(), networkErrorsSlidingWindow: sw.NewSlidingWindow(),
} }
for _, opt := range opts { backend.Override(opts...)
opt(backend)
}
if !backend.stripTrailingXFF && backend.proxydIP == "" { if !backend.stripTrailingXFF && backend.proxydIP == "" {
log.Warn("proxied requests' XFF header will not contain the proxyd ip address") log.Warn("proxied requests' XFF header will not contain the proxyd ip address")
...@@ -277,6 +305,12 @@ func NewBackend( ...@@ -277,6 +305,12 @@ func NewBackend(
return backend return backend
} }
func (b *Backend) Override(opts ...BackendOpt) {
for _, opt := range opts {
opt(b)
}
}
func (b *Backend) Forward(ctx context.Context, reqs []*RPCReq, isBatch bool) ([]*RPCRes, error) { func (b *Backend) Forward(ctx context.Context, reqs []*RPCReq, isBatch bool) ([]*RPCRes, error) {
var lastError error var lastError error
// <= to account for the first attempt not technically being // <= to account for the first attempt not technically being
...@@ -298,6 +332,20 @@ func (b *Backend) Forward(ctx context.Context, reqs []*RPCReq, isBatch bool) ([] ...@@ -298,6 +332,20 @@ func (b *Backend) Forward(ctx context.Context, reqs []*RPCReq, isBatch bool) ([]
res, err := b.doForward(ctx, reqs, isBatch) res, err := b.doForward(ctx, reqs, isBatch)
switch err { switch err {
case nil: // do nothing case nil: // do nothing
case ErrConsensusGetReceiptsCantBeBatched:
log.Warn(
"Received unsupported batch request for consensus_getReceipts",
"name", b.Name,
"req_id", GetReqID(ctx),
"err", err,
)
case ErrConsensusGetReceiptsInvalidTarget:
log.Error(
"Unsupported consensus_receipts_target for consensus_getReceipts",
"name", b.Name,
"req_id", GetReqID(ctx),
"err", err,
)
// ErrBackendUnexpectedJSONRPC occurs because infura responds with a single JSON-RPC object // ErrBackendUnexpectedJSONRPC occurs because infura responds with a single JSON-RPC object
// to a batch request whenever any Request Object in the batch would induce a partial error. // to a batch request whenever any Request Object in the batch would induce a partial error.
// We don't label the backend offline in this case. But the error is still returned to // We don't label the backend offline in this case. But the error is still returned to
...@@ -375,11 +423,63 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool ...@@ -375,11 +423,63 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool
// we are concerned about network error rates, so we record 1 request independently of how many are in the batch // we are concerned about network error rates, so we record 1 request independently of how many are in the batch
b.networkRequestsSlidingWindow.Incr() b.networkRequestsSlidingWindow.Incr()
translatedReqs := make(map[string]*RPCReq, len(rpcReqs))
// translate consensus_getReceipts to receipts target
// right now we only support non-batched
if isBatch {
for _, rpcReq := range rpcReqs {
if rpcReq.Method == ConsensusGetReceiptsMethod {
return nil, ErrConsensusGetReceiptsCantBeBatched
}
}
} else {
for _, rpcReq := range rpcReqs {
if rpcReq.Method == ConsensusGetReceiptsMethod {
translatedReqs[string(rpcReq.ID)] = rpcReq
rpcReq.Method = b.receiptsTarget
var reqParams []rpc.BlockNumberOrHash
err := json.Unmarshal(rpcReq.Params, &reqParams)
if err != nil {
return nil, ErrInvalidRequest("invalid request")
}
var translatedParams []byte
switch rpcReq.Method {
case ReceiptsTargetDebugGetRawReceipts,
ReceiptsTargetEthGetTransactionReceipts,
ReceiptsTargetParityGetTransactionReceipts:
// conventional methods use an array of strings having either block number or block hash
// i.e. ["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"]
params := make([]string, 1)
if reqParams[0].BlockNumber != nil {
params[0] = reqParams[0].BlockNumber.String()
} else {
params[0] = reqParams[0].BlockHash.Hex()
}
translatedParams = mustMarshalJSON(params)
case ReceiptsTargetAlchemyGetTransactionReceipts:
// alchemy uses an array of object with either block number or block hash
// i.e. [{ blockHash: "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b" }]
params := make([]BlockHashOrNumberParameter, 1)
if reqParams[0].BlockNumber != nil {
params[0].BlockNumber = reqParams[0].BlockNumber
} else {
params[0].BlockHash = reqParams[0].BlockHash
}
translatedParams = mustMarshalJSON(params)
default:
return nil, ErrConsensusGetReceiptsInvalidTarget
}
rpcReq.Params = translatedParams
}
}
}
isSingleElementBatch := len(rpcReqs) == 1 isSingleElementBatch := len(rpcReqs) == 1
// Single element batches are unwrapped before being sent // Single element batches are unwrapped before being sent
// since Alchemy handles single requests better than batches. // since Alchemy handles single requests better than batches.
var body []byte var body []byte
if isSingleElementBatch { if isSingleElementBatch {
body = mustMarshalJSON(rpcReqs[0]) body = mustMarshalJSON(rpcReqs[0])
...@@ -443,17 +543,17 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool ...@@ -443,17 +543,17 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool
return nil, wrapErr(err, "error reading response body") return nil, wrapErr(err, "error reading response body")
} }
var res []*RPCRes var rpcRes []*RPCRes
if isSingleElementBatch { if isSingleElementBatch {
var singleRes RPCRes var singleRes RPCRes
if err := json.Unmarshal(resB, &singleRes); err != nil { if err := json.Unmarshal(resB, &singleRes); err != nil {
return nil, ErrBackendBadResponse return nil, ErrBackendBadResponse
} }
res = []*RPCRes{ rpcRes = []*RPCRes{
&singleRes, &singleRes,
} }
} else { } else {
if err := json.Unmarshal(resB, &res); err != nil { if err := json.Unmarshal(resB, &rpcRes); err != nil {
// Infura may return a single JSON-RPC response if, for example, the batch contains a request for an unsupported method // Infura may return a single JSON-RPC response if, for example, the batch contains a request for an unsupported method
if responseIsNotBatched(resB) { if responseIsNotBatched(resB) {
b.networkErrorsSlidingWindow.Incr() b.networkErrorsSlidingWindow.Incr()
...@@ -466,7 +566,7 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool ...@@ -466,7 +566,7 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool
} }
} }
if len(rpcReqs) != len(res) { if len(rpcReqs) != len(rpcRes) {
b.networkErrorsSlidingWindow.Incr() b.networkErrorsSlidingWindow.Incr()
RecordBackendNetworkErrorRateSlidingWindow(b, b.ErrorRate()) RecordBackendNetworkErrorRateSlidingWindow(b, b.ErrorRate())
return nil, ErrBackendUnexpectedJSONRPC return nil, ErrBackendUnexpectedJSONRPC
...@@ -475,7 +575,7 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool ...@@ -475,7 +575,7 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool
// capture the HTTP status code in the response. this will only // capture the HTTP status code in the response. this will only
// ever be 400 given the status check on line 318 above. // ever be 400 given the status check on line 318 above.
if httpRes.StatusCode != 200 { if httpRes.StatusCode != 200 {
for _, res := range res { for _, res := range rpcRes {
res.Error.HTTPErrorCode = httpRes.StatusCode res.Error.HTTPErrorCode = httpRes.StatusCode
} }
} }
...@@ -484,8 +584,20 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool ...@@ -484,8 +584,20 @@ func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool
RecordBackendNetworkLatencyAverageSlidingWindow(b, time.Duration(b.latencySlidingWindow.Avg())) RecordBackendNetworkLatencyAverageSlidingWindow(b, time.Duration(b.latencySlidingWindow.Avg()))
RecordBackendNetworkErrorRateSlidingWindow(b, b.ErrorRate()) RecordBackendNetworkErrorRateSlidingWindow(b, b.ErrorRate())
sortBatchRPCResponse(rpcReqs, res) // enrich the response with the actual request method
return res, nil for _, res := range rpcRes {
translatedReq, exist := translatedReqs[string(res.ID)]
if exist {
res.Result = ConsensusGetReceiptsResult{
Method: translatedReq.Method,
Result: res.Result,
}
}
}
sortBatchRPCResponse(rpcReqs, rpcRes)
return rpcRes, nil
} }
// IsHealthy checks if the backend is able to serve traffic, based on dynamic parameters // IsHealthy checks if the backend is able to serve traffic, based on dynamic parameters
...@@ -604,7 +716,9 @@ func (bg *BackendGroup) Forward(ctx context.Context, rpcReqs []*RPCReq, isBatch ...@@ -604,7 +716,9 @@ func (bg *BackendGroup) Forward(ctx context.Context, rpcReqs []*RPCReq, isBatch
if len(rpcReqs) > 0 { if len(rpcReqs) > 0 {
res, err = back.Forward(ctx, rpcReqs, isBatch) res, err = back.Forward(ctx, rpcReqs, isBatch)
if errors.Is(err, ErrMethodNotWhitelisted) { if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) ||
errors.Is(err, ErrConsensusGetReceiptsInvalidTarget) ||
errors.Is(err, ErrMethodNotWhitelisted) {
return nil, err return nil, err
} }
if errors.Is(err, ErrBackendOffline) { if errors.Is(err, ErrBackendOffline) {
......
...@@ -79,18 +79,20 @@ type BackendOptions struct { ...@@ -79,18 +79,20 @@ type BackendOptions struct {
} }
type BackendConfig struct { type BackendConfig struct {
Username string `toml:"username"` Username string `toml:"username"`
Password string `toml:"password"` Password string `toml:"password"`
RPCURL string `toml:"rpc_url"` RPCURL string `toml:"rpc_url"`
WSURL string `toml:"ws_url"` WSURL string `toml:"ws_url"`
WSPort int `toml:"ws_port"` WSPort int `toml:"ws_port"`
MaxRPS int `toml:"max_rps"` MaxRPS int `toml:"max_rps"`
MaxWSConns int `toml:"max_ws_conns"` MaxWSConns int `toml:"max_ws_conns"`
CAFile string `toml:"ca_file"` CAFile string `toml:"ca_file"`
ClientCertFile string `toml:"client_cert_file"` ClientCertFile string `toml:"client_cert_file"`
ClientKeyFile string `toml:"client_key_file"` ClientKeyFile string `toml:"client_key_file"`
StripTrailingXFF bool `toml:"strip_trailing_xff"` StripTrailingXFF bool `toml:"strip_trailing_xff"`
SkipPeerCountCheck bool `toml:"consensus_skip_peer_count"`
ConsensusSkipPeerCountCheck bool `toml:"consensus_skip_peer_count"`
ConsensusReceiptsTarget string `toml:"consensus_receipts_target"`
} }
type BackendsConfig map[string]*BackendConfig type BackendsConfig map[string]*BackendConfig
......
...@@ -74,7 +74,9 @@ client_cert_file = "" ...@@ -74,7 +74,9 @@ client_cert_file = ""
client_key_file = "" client_key_file = ""
# Allows backends to skip peer count checking, default false # Allows backends to skip peer count checking, default false
# consensus_skip_peer_count = true # consensus_skip_peer_count = true
# Specified the target method to get receipts, default "debug_getRawReceipts"
# See https://github.com/ethereum-optimism/optimism/blob/186e46a47647a51a658e699e9ff047d39444c2de/op-node/sources/receipts.go#L186-L253
consensus_receipts_target = "eth_getBlockReceipts"
[backends.alchemy] [backends.alchemy]
rpc_url = "" rpc_url = ""
...@@ -83,6 +85,7 @@ username = "" ...@@ -83,6 +85,7 @@ username = ""
password = "" password = ""
max_rps = 3 max_rps = 3
max_ws_conns = 1 max_ws_conns = 1
consensus_receipts_target = "alchemy_getTransactionReceipts"
[backend_groups] [backend_groups]
[backend_groups.main] [backend_groups.main]
......
...@@ -9,6 +9,7 @@ require ( ...@@ -9,6 +9,7 @@ require (
github.com/ethereum/go-ethereum v1.12.0 github.com/ethereum/go-ethereum v1.12.0
github.com/go-redis/redis/v8 v8.11.4 github.com/go-redis/redis/v8 v8.11.4
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.0 github.com/gorilla/websocket v1.5.0
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
......
...@@ -157,6 +157,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= ...@@ -157,6 +157,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
......
...@@ -784,6 +784,211 @@ func TestConsensus(t *testing.T) { ...@@ -784,6 +784,211 @@ func TestConsensus(t *testing.T) {
// dont rewrite for 0xe1 // dont rewrite for 0xe1
require.Equal(t, "0xe1", jsonMap[2]["result"].(map[string]interface{})["number"]) require.Equal(t, "0xe1", jsonMap[2]["result"].(map[string]interface{})["number"])
}) })
t.Run("translate consensus_getReceipts to debug_getRawReceipts", func(t *testing.T) {
reset()
useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts",
[]interface{}{"0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"})
require.NoError(t, err)
require.Equal(t, 200, statusCode)
var jsonMap map[string]interface{}
err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &jsonMap)
require.NoError(t, err)
require.Equal(t, "debug_getRawReceipts", jsonMap["method"])
require.Equal(t, "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", jsonMap["params"].([]interface{})[0])
var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap)
require.NoError(t, err)
require.Equal(t, "debug_getRawReceipts", resJsonMap["result"].(map[string]interface{})["method"].(string))
require.Equal(t, "debug_getRawReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
})
t.Run("translate consensus_getReceipts to debug_getRawReceipts with latest block tag", func(t *testing.T) {
reset()
useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts",
[]interface{}{"latest"})
require.NoError(t, err)
require.Equal(t, 200, statusCode)
var jsonMap map[string]interface{}
err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &jsonMap)
require.NoError(t, err)
require.Equal(t, "debug_getRawReceipts", jsonMap["method"])
require.Equal(t, "0x101", jsonMap["params"].([]interface{})[0])
var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap)
require.NoError(t, err)
require.Equal(t, "debug_getRawReceipts", resJsonMap["result"].(map[string]interface{})["method"].(string))
require.Equal(t, "debug_getRawReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
})
t.Run("translate consensus_getReceipts to debug_getRawReceipts with block number", func(t *testing.T) {
reset()
useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts",
[]interface{}{"0x55"})
require.NoError(t, err)
require.Equal(t, 200, statusCode)
var jsonMap map[string]interface{}
err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &jsonMap)
require.NoError(t, err)
require.Equal(t, "debug_getRawReceipts", jsonMap["method"])
require.Equal(t, "0x55", jsonMap["params"].([]interface{})[0])
var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap)
require.NoError(t, err)
require.Equal(t, "debug_getRawReceipts", resJsonMap["result"].(map[string]interface{})["method"].(string))
require.Equal(t, "debug_getRawReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
})
t.Run("translate consensus_getReceipts to alchemy_getTransactionReceipts with block hash", func(t *testing.T) {
reset()
useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("alchemy_getTransactionReceipts"))
defer nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("debug_getRawReceipts"))
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts",
[]interface{}{"0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b"})
require.NoError(t, err)
require.Equal(t, 200, statusCode)
var reqJsonMap map[string]interface{}
err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &reqJsonMap)
require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", reqJsonMap["method"])
require.Equal(t, "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", reqJsonMap["params"].([]interface{})[0].(map[string]interface{})["blockHash"])
var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap)
require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["method"].(string))
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
})
t.Run("translate consensus_getReceipts to alchemy_getTransactionReceipts with block number", func(t *testing.T) {
reset()
useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("alchemy_getTransactionReceipts"))
defer nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("debug_getRawReceipts"))
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts",
[]interface{}{"0x55"})
require.NoError(t, err)
require.Equal(t, 200, statusCode)
var reqJsonMap map[string]interface{}
err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &reqJsonMap)
require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", reqJsonMap["method"])
require.Equal(t, "0x55", reqJsonMap["params"].([]interface{})[0].(map[string]interface{})["blockNumber"])
var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap)
require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["method"].(string))
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
})
t.Run("translate consensus_getReceipts to alchemy_getTransactionReceipts with latest block tag", func(t *testing.T) {
reset()
useOnlyNode1()
update()
// reset request counts
nodes["node1"].mockBackend.Reset()
nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("alchemy_getTransactionReceipts"))
defer nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("debug_getRawReceipts"))
resRaw, statusCode, err := client.SendRPC("consensus_getReceipts",
[]interface{}{"latest"})
require.NoError(t, err)
require.Equal(t, 200, statusCode)
var reqJsonMap map[string]interface{}
err = json.Unmarshal(nodes["node1"].mockBackend.Requests()[0].Body, &reqJsonMap)
require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", reqJsonMap["method"])
require.Equal(t, "0x101", reqJsonMap["params"].([]interface{})[0].(map[string]interface{})["blockNumber"])
var resJsonMap map[string]interface{}
err = json.Unmarshal(resRaw, &resJsonMap)
require.NoError(t, err)
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["method"].(string))
require.Equal(t, "alchemy_getTransactionReceipts", resJsonMap["result"].(map[string]interface{})["result"].(map[string]interface{})["_"])
})
t.Run("translate consensus_getReceipts to unsupported consensus_receipts_target", func(t *testing.T) {
reset()
useOnlyNode1()
nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("unsupported_consensus_receipts_target"))
defer nodes["node1"].backend.Override(proxyd.WithConsensusReceiptTarget("debug_getRawReceipts"))
_, statusCode, err := client.SendRPC("consensus_getReceipts",
[]interface{}{"latest"})
require.NoError(t, err)
require.Equal(t, 400, statusCode)
})
t.Run("consensus_getReceipts should not be used in a batch", func(t *testing.T) {
reset()
useOnlyNode1()
_, statusCode, err := client.SendBatchRPC(
NewRPCReq("1", "eth_getBlockByNumber", []interface{}{"latest"}),
NewRPCReq("2", "consensus_getReceipts", []interface{}{"0x55"}),
NewRPCReq("3", "eth_getBlockByNumber", []interface{}{"0xe1"}))
require.NoError(t, err)
require.Equal(t, 400, statusCode)
})
} }
func buildResponse(result interface{}) string { func buildResponse(result interface{}) string {
......
...@@ -27,3 +27,4 @@ eth_call = "node" ...@@ -27,3 +27,4 @@ eth_call = "node"
eth_chainId = "node" eth_chainId = "node"
eth_blockNumber = "node" eth_blockNumber = "node"
eth_getBlockByNumber = "node" eth_getBlockByNumber = "node"
consensus_getReceipts = "node"
...@@ -184,3 +184,30 @@ ...@@ -184,3 +184,30 @@
"number": "0xd1" "number": "0xd1"
} }
} }
- method: debug_getRawReceipts
response: >
{
"jsonrpc": "2.0",
"id": 67,
"result": {
"_": "debug_getRawReceipts"
}
}
- method: eth_getTransactionReceipt
response: >
{
"jsonrpc": "2.0",
"id": 67,
"result": {
"_": "eth_getTransactionReceipt"
}
}
- method: alchemy_getTransactionReceipts
response: >
{
"jsonrpc": "2.0",
"id": 67,
"result": {
"_": "alchemy_getTransactionReceipts"
}
}
...@@ -141,7 +141,17 @@ func Start(config *Config) (*Server, func(), error) { ...@@ -141,7 +141,17 @@ func Start(config *Config) (*Server, func(), error) {
opts = append(opts, WithStrippedTrailingXFF()) opts = append(opts, WithStrippedTrailingXFF())
} }
opts = append(opts, WithProxydIP(os.Getenv("PROXYD_IP"))) opts = append(opts, WithProxydIP(os.Getenv("PROXYD_IP")))
opts = append(opts, WithSkipPeerCountCheck(cfg.SkipPeerCountCheck)) opts = append(opts, WithConsensusSkipPeerCountCheck(cfg.ConsensusSkipPeerCountCheck))
receiptsTarget, err := ReadFromEnvOrConfig(cfg.ConsensusReceiptsTarget)
if err != nil {
return nil, nil, err
}
receiptsTarget, err = validateReceiptsTarget(receiptsTarget)
if err != nil {
return nil, nil, err
}
opts = append(opts, WithConsensusReceiptTarget(receiptsTarget))
back := NewBackend(name, rpcURL, wsURL, rpcRequestSemaphore, opts...) back := NewBackend(name, rpcURL, wsURL, rpcRequestSemaphore, opts...)
backendNames = append(backendNames, name) backendNames = append(backendNames, name)
...@@ -316,6 +326,21 @@ func Start(config *Config) (*Server, func(), error) { ...@@ -316,6 +326,21 @@ func Start(config *Config) (*Server, func(), error) {
return srv, shutdownFunc, nil return srv, shutdownFunc, nil
} }
func validateReceiptsTarget(val string) (string, error) {
if val == "" {
val = ReceiptsTargetDebugGetRawReceipts
}
switch val {
case ReceiptsTargetDebugGetRawReceipts,
ReceiptsTargetAlchemyGetTransactionReceipts,
ReceiptsTargetEthGetTransactionReceipts,
ReceiptsTargetParityGetTransactionReceipts:
return val, nil
default:
return "", fmt.Errorf("invalid receipts target: %s", val)
}
}
func secondsToDuration(seconds int) time.Duration { func secondsToDuration(seconds int) time.Duration {
return time.Duration(seconds) * time.Second return time.Duration(seconds) * time.Second
} }
......
...@@ -63,7 +63,7 @@ func RewriteRequest(rctx RewriteContext, req *RPCReq, res *RPCRes) (RewriteResul ...@@ -63,7 +63,7 @@ func RewriteRequest(rctx RewriteContext, req *RPCReq, res *RPCRes) (RewriteResul
case "eth_getLogs", case "eth_getLogs",
"eth_newFilter": "eth_newFilter":
return rewriteRange(rctx, req, res, 0) return rewriteRange(rctx, req, res, 0)
case "debug_getRawReceipts": case "debug_getRawReceipts", "consensus_getReceipts":
return rewriteParam(rctx, req, res, 0, true) return rewriteParam(rctx, req, res, 0, true)
case "eth_getBalance", case "eth_getBalance",
"eth_getCode", "eth_getCode",
......
...@@ -347,6 +347,11 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) { ...@@ -347,6 +347,11 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
writeRPCError(ctx, w, nil, ErrGatewayTimeout) writeRPCError(ctx, w, nil, ErrGatewayTimeout)
return return
} }
if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) ||
errors.Is(err, ErrConsensusGetReceiptsInvalidTarget) {
writeRPCError(ctx, w, nil, ErrInvalidRequest(err.Error()))
return
}
if err != nil { if err != nil {
writeRPCError(ctx, w, nil, ErrInternal) writeRPCError(ctx, w, nil, ErrInternal)
return return
...@@ -360,6 +365,11 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) { ...@@ -360,6 +365,11 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
rawBody := json.RawMessage(body) rawBody := json.RawMessage(body)
backendRes, cached, err := s.handleBatchRPC(ctx, []json.RawMessage{rawBody}, isLimited, false) backendRes, cached, err := s.handleBatchRPC(ctx, []json.RawMessage{rawBody}, isLimited, false)
if err != nil { if err != nil {
if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) ||
errors.Is(err, ErrConsensusGetReceiptsInvalidTarget) {
writeRPCError(ctx, w, nil, ErrInvalidRequest(err.Error()))
return
}
writeRPCError(ctx, w, nil, ErrInternal) writeRPCError(ctx, w, nil, ErrInternal)
return return
} }
...@@ -485,6 +495,10 @@ func (s *Server) handleBatchRPC(ctx context.Context, reqs []json.RawMessage, isL ...@@ -485,6 +495,10 @@ func (s *Server) handleBatchRPC(ctx context.Context, reqs []json.RawMessage, isL
elems := cacheMisses[start:end] elems := cacheMisses[start:end]
res, err := s.BackendGroups[group.backendGroup].Forward(ctx, createBatchRequest(elems), isBatch) res, err := s.BackendGroups[group.backendGroup].Forward(ctx, createBatchRequest(elems), isBatch)
if err != nil { if err != nil {
if errors.Is(err, ErrConsensusGetReceiptsCantBeBatched) ||
errors.Is(err, ErrConsensusGetReceiptsInvalidTarget) {
return nil, false, err
}
log.Error( log.Error(
"error forwarding RPC batch", "error forwarding RPC batch",
"batch_size", len(elems), "batch_size", len(elems),
......
...@@ -88,7 +88,15 @@ func (mh *MockedHandler) Handler(w http.ResponseWriter, req *http.Request) { ...@@ -88,7 +88,15 @@ func (mh *MockedHandler) Handler(w http.ResponseWriter, req *http.Request) {
} }
} }
if selectedResponse != "" { if selectedResponse != "" {
responses = append(responses, selectedResponse) var rpcRes proxyd.RPCRes
err = json.Unmarshal([]byte(selectedResponse), &rpcRes)
if err != nil {
panic(err)
}
idJson, _ := json.Marshal(r["id"])
rpcRes.ID = idJson
res, _ := json.Marshal(rpcRes)
responses = append(responses, string(res))
} }
} }
......
...@@ -62,7 +62,7 @@ or `Bedrock`. Deprecated contracts should not be used. ...@@ -62,7 +62,7 @@ or `Bedrock`. Deprecated contracts should not be used.
| L2ToL1MessagePasser | 0x4200000000000000000000000000000000000016 | Bedrock | No | Yes | | L2ToL1MessagePasser | 0x4200000000000000000000000000000000000016 | Bedrock | No | Yes |
| L2ERC721Bridge | 0x4200000000000000000000000000000000000014 | Legacy | No | Yes | | L2ERC721Bridge | 0x4200000000000000000000000000000000000014 | Legacy | No | Yes |
| OptimismMintableERC721Factory | 0x4200000000000000000000000000000000000017 | Bedrock | No | Yes | | OptimismMintableERC721Factory | 0x4200000000000000000000000000000000000017 | Bedrock | No | Yes |
| ProxyAdmin | 0x4200000000000000000000000000000000000018 | Bedrock | No | No | | ProxyAdmin | 0x4200000000000000000000000000000000000018 | Bedrock | No | Yes |
| BaseFeeVault | 0x4200000000000000000000000000000000000019 | Bedrock | No | Yes | | BaseFeeVault | 0x4200000000000000000000000000000000000019 | Bedrock | No | Yes |
| L1FeeVault | 0x420000000000000000000000000000000000001a | Bedrock | No | Yes | | L1FeeVault | 0x420000000000000000000000000000000000001a | Bedrock | No | Yes |
......
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