Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
afba7c22
Unverified
Commit
afba7c22
authored
Oct 20, 2022
by
mergify[bot]
Committed by
GitHub
Oct 20, 2022
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into jg/limit_blocks_per_channel
parents
03bb2700
ef32b59d
Changes
57
Hide whitespace changes
Inline
Side-by-side
Showing
57 changed files
with
769 additions
and
252 deletions
+769
-252
config.yml
.circleci/config.yml
+47
-4
mergify.yml
.github/mergify.yml
+36
-0
.snyk
.snyk
+3
-0
package.json
integration-tests/package.json
+1
-1
CHANGELOG.md
l2geth/CHANGELOG.md
+6
-0
statedb.go
l2geth/core/state/statedb.go
+8
-2
evm.go
l2geth/core/vm/evm.go
+5
-0
package.json
l2geth/package.json
+1
-1
constants.go
l2geth/rollup/dump/constants.go
+1
-0
dumper.go
l2geth/statedumper/dumper.go
+74
-0
Makefile
op-chain-ops/Makefile
+1
-1
main.go
op-chain-ops/cmd/main.go
+0
-120
legacy_withdrawal.go
op-chain-ops/crossdomain/legacy_withdrawal.go
+4
-4
migrate.go
op-chain-ops/crossdomain/migrate.go
+116
-0
migrate_test.go
op-chain-ops/crossdomain/migrate_test.go
+41
-0
withdrawal.go
op-chain-ops/crossdomain/withdrawal.go
+6
-6
withdrawals.go
op-chain-ops/crossdomain/withdrawals.go
+8
-11
withdrawals_test.go
op-chain-ops/crossdomain/withdrawals_test.go
+2
-3
addresses.go
op-chain-ops/ether/addresses.go
+1
-1
cli.go
op-chain-ops/ether/cli.go
+1
-1
db.go
op-chain-ops/ether/db.go
+1
-1
genesis.go
op-chain-ops/ether/genesis.go
+1
-1
params.go
op-chain-ops/ether/params.go
+1
-1
storage.go
op-chain-ops/ether/storage.go
+1
-1
util.go
op-chain-ops/ether/util.go
+1
-1
l2_engine_api.go
op-e2e/actions/l2_engine_api.go
+1
-1
user.go
op-e2e/actions/user.go
+23
-0
metrics.go
op-node/metrics/metrics.go
+174
-0
node.go
op-node/node/node.go
+1
-1
config.go
op-node/p2p/config.go
+1
-3
gossip.go
op-node/p2p/gossip.go
+16
-1
host.go
op-node/p2p/host.go
+3
-2
host_test.go
op-node/p2p/host_test.go
+8
-9
node.go
op-node/p2p/node.go
+14
-6
notifications.go
op-node/p2p/notifications.go
+17
-3
prepared.go
op-node/p2p/prepared.go
+2
-1
rpc_server.go
op-node/p2p/rpc_server.go
+6
-2
channel.go
op-node/rollup/derive/channel.go
+1
-1
channel_out.go
op-node/rollup/derive/channel_out.go
+46
-25
params.go
op-node/rollup/derive/params.go
+4
-0
conf_depth.go
op-node/rollup/driver/conf_depth.go
+4
-1
conf_depth_test.go
op-node/rollup/driver/conf_depth_test.go
+18
-13
CHANGELOG.md
packages/actor-tests/CHANGELOG.md
+8
-0
package.json
packages/actor-tests/package.json
+3
-3
CHANGELOG.md
packages/contracts-bedrock/CHANGELOG.md
+6
-0
OptimismPortal.sol
packages/contracts-bedrock/contracts/L1/OptimismPortal.sol
+8
-8
001-InitImplementations.deploy.ts
...ontracts-bedrock/deploy/001-InitImplementations.deploy.ts
+1
-1
package.json
packages/contracts-bedrock/package.json
+1
-1
CHANGELOG.md
packages/drippie-mon/CHANGELOG.md
+6
-0
package.json
packages/drippie-mon/package.json
+2
-2
package.json
packages/integration-tests-bedrock/package.json
+1
-1
CHANGELOG.md
packages/message-relayer/CHANGELOG.md
+6
-0
package.json
packages/message-relayer/package.json
+2
-2
CHANGELOG.md
packages/sdk/CHANGELOG.md
+7
-0
package.json
packages/sdk/package.json
+2
-2
derivation.md
specs/derivation.md
+5
-3
exec-engine.md
specs/exec-engine.md
+5
-0
No files found.
.circleci/config.yml
View file @
afba7c22
...
@@ -2,6 +2,48 @@ version: 2.1
...
@@ -2,6 +2,48 @@ version: 2.1
orbs
:
orbs
:
go
:
circleci/go@1.5.0
go
:
circleci/go@1.5.0
gcp-cli
:
circleci/gcp-cli@2.4.1
commands
:
gcp-oidc-authenticate
:
description
:
"
Authenticate
with
GCP
using
a
CircleCI
OIDC
token."
parameters
:
project_id
:
type
:
env_var_name
default
:
GCP_PROJECT_ID
workload_identity_pool_id
:
type
:
env_var_name
default
:
GCP_WIP_ID
workload_identity_pool_provider_id
:
type
:
env_var_name
default
:
GCP_WIP_PROVIDER_ID
service_account_email
:
type
:
env_var_name
default
:
GCP_SERVICE_ACCOUNT_EMAIL
gcp_cred_config_file_path
:
type
:
string
default
:
/home/circleci/gcp_cred_config.json
oidc_token_file_path
:
type
:
string
default
:
/home/circleci/oidc_token.json
steps
:
-
run
:
name
:
"
Create
OIDC
credential
configuration"
command
:
|
# Store OIDC token in temp file
echo $CIRCLE_OIDC_TOKEN > << parameters.oidc_token_file_path >>
# Create a credential configuration for the generated OIDC ID Token
gcloud iam workload-identity-pools create-cred-config \
"projects/${<< parameters.project_id >>}/locations/global/workloadIdentityPools/${<< parameters.workload_identity_pool_id >>}/providers/${<< parameters.workload_identity_pool_provider_id >>}"\
--output-file="<< parameters.gcp_cred_config_file_path >>" \
--service-account="${<< parameters.service_account_email >>}" \
--credential-source-file=<< parameters.oidc_token_file_path >>
-
run
:
name
:
"
Authenticate
with
GCP
using
OIDC"
command
:
|
# Configure gcloud to leverage the generated credential configuration
gcloud auth login --brief --cred-file "<< parameters.gcp_cred_config_file_path >>"
# Configure ADC
echo "export GOOGLE_APPLICATION_CREDENTIALS='<< parameters.gcp_cred_config_file_path >>'" | tee -a "$BASH_ENV"
jobs
:
jobs
:
yarn-monorepo
:
yarn-monorepo
:
docker
:
docker
:
...
@@ -67,6 +109,7 @@ jobs:
...
@@ -67,6 +109,7 @@ jobs:
image
:
ubuntu-2204:2022.07.1
image
:
ubuntu-2204:2022.07.1
resource_class
:
xlarge
resource_class
:
xlarge
steps
:
steps
:
-
gcp-oidc-authenticate
# Below is CircleCI recommended way of specifying nameservers on an Ubuntu box:
# Below is CircleCI recommended way of specifying nameservers on an Ubuntu box:
# https://support.circleci.com/hc/en-us/articles/7323511028251-How-to-set-custom-DNS-on-Ubuntu-based-images-using-netplan
# https://support.circleci.com/hc/en-us/articles/7323511028251-How-to-set-custom-DNS-on-Ubuntu-based-images-using-netplan
-
run
:
sudo sed -i '13 i \ \ \ \ \ \ \ \ \ \ \ \ nameservers:' /etc/netplan/50-cloud-init.yaml
-
run
:
sudo sed -i '13 i \ \ \ \ \ \ \ \ \ \ \ \ nameservers:' /etc/netplan/50-cloud-init.yaml
...
@@ -101,7 +144,7 @@ jobs:
...
@@ -101,7 +144,7 @@ jobs:
-
run
:
-
run
:
name
:
Publish
name
:
Publish
command
:
|
command
:
|
echo "$DOCKER_PASS" | docker login -u "$DOCKER_USERNAME" --password-stdin "<<parameters.repo>>"
gcloud auth configure-docker us-central1-docker.pkg.dev
docker push <<parameters.docker_tags>>
docker push <<parameters.docker_tags>>
contracts-bedrock-tests
:
contracts-bedrock-tests
:
...
@@ -145,7 +188,7 @@ jobs:
...
@@ -145,7 +188,7 @@ jobs:
working_directory
:
packages/contracts-bedrock
working_directory
:
packages/contracts-bedrock
-
run
:
-
run
:
name
:
upload coverage
name
:
upload coverage
command
:
codecov --verbose --clean --flag contracts-bedrock-tests
command
:
codecov --verbose --clean --flag
s
contracts-bedrock-tests
environment
:
environment
:
FOUNDRY_PROFILE
:
ci
FOUNDRY_PROFILE
:
ci
-
run
:
-
run
:
...
@@ -217,7 +260,7 @@ jobs:
...
@@ -217,7 +260,7 @@ jobs:
working_directory
:
packages/<<parameters.package_name>>
working_directory
:
packages/<<parameters.package_name>>
-
run
:
-
run
:
name
:
Upload coverage
name
:
Upload coverage
command
:
codecov --verbose --clean --flag <<parameters.coverage_flag>>
command
:
codecov --verbose --clean --flag
s
<<parameters.coverage_flag>>
bedrock-go-tests
:
bedrock-go-tests
:
docker
:
docker
:
...
@@ -308,7 +351,7 @@ jobs:
...
@@ -308,7 +351,7 @@ jobs:
working_directory
:
op-chain-ops
working_directory
:
op-chain-ops
-
run
:
-
run
:
name
:
upload coverage
name
:
upload coverage
command
:
codecov --verbose --clean --flag bedrock-go-tests
command
:
codecov --verbose --clean --flag
s
bedrock-go-tests
-
store_test_results
:
-
store_test_results
:
path
:
/test-results
path
:
/test-results
-
run
:
-
run
:
...
...
.github/mergify.yml
View file @
afba7c22
...
@@ -105,3 +105,39 @@ pull_request_rules:
...
@@ -105,3 +105,39 @@ pull_request_rules:
comment
:
comment
:
message
:
|
message
:
|
This PR changes implementation code, but doesn't include a changeset. Did you forget to add one?
This PR changes implementation code, but doesn't include a changeset. Did you forget to add one?
-
name
:
Add indexer tag and ecopod reviewers
conditions
:
-
'
files~=^indexer/'
actions
:
label
:
add
:
-
indexer
request_reviews
:
users
:
-
roninjin10
-
nickbalestra
-
name
:
Add sdk tag and ecopod reviewers
conditions
:
-
'
files~=^packages/sdk/'
actions
:
label
:
add
:
-
sdk
request_reviews
:
users
:
-
roninjin10
-
nickbalestra
-
name
:
Add common-ts tag and ecopod reviewers
conditions
:
-
'
files~=^packages/common-ts/'
actions
:
label
:
add
:
-
common-ts
request_reviews
:
users
:
-
imranjami
-
roninjin10
.snyk
0 → 100644
View file @
afba7c22
exclude:
global:
- infra/op-replica/** # snyk does not respect kustomizations, so not useful here
integration-tests/package.json
View file @
afba7c22
...
@@ -32,7 +32,7 @@
...
@@ -32,7 +32,7 @@
"@eth-optimism/contracts"
:
"^0.5.37"
,
"@eth-optimism/contracts"
:
"^0.5.37"
,
"@eth-optimism/contracts-periphery"
:
"^1.0.2"
,
"@eth-optimism/contracts-periphery"
:
"^1.0.2"
,
"@eth-optimism/core-utils"
:
"0.10.1"
,
"@eth-optimism/core-utils"
:
"0.10.1"
,
"@eth-optimism/sdk"
:
"1.6.
7
"
,
"@eth-optimism/sdk"
:
"1.6.
8
"
,
"@ethersproject/abstract-provider"
:
"^5.7.0"
,
"@ethersproject/abstract-provider"
:
"^5.7.0"
,
"@ethersproject/providers"
:
"^5.7.0"
,
"@ethersproject/providers"
:
"^5.7.0"
,
"@ethersproject/transactions"
:
"^5.7.0"
,
"@ethersproject/transactions"
:
"^5.7.0"
,
...
...
l2geth/CHANGELOG.md
View file @
afba7c22
# Changelog
# Changelog
## 0.5.26
### Patch Changes
-
397b27ee: Add data exporter
## 0.5.25
## 0.5.25
### Patch Changes
### Patch Changes
...
...
l2geth/core/state/statedb.go
View file @
afba7c22
...
@@ -24,6 +24,8 @@ import (
...
@@ -24,6 +24,8 @@ import (
"sort"
"sort"
"time"
"time"
"github.com/ethereum-optimism/optimism/l2geth/statedumper"
"github.com/ethereum-optimism/optimism/l2geth/common"
"github.com/ethereum-optimism/optimism/l2geth/common"
"github.com/ethereum-optimism/optimism/l2geth/core/types"
"github.com/ethereum-optimism/optimism/l2geth/core/types"
"github.com/ethereum-optimism/optimism/l2geth/crypto"
"github.com/ethereum-optimism/optimism/l2geth/crypto"
...
@@ -244,6 +246,7 @@ func (s *StateDB) GetBalance(addr common.Address) *big.Int {
...
@@ -244,6 +246,7 @@ func (s *StateDB) GetBalance(addr common.Address) *big.Int {
if
rcfg
.
UsingOVM
{
if
rcfg
.
UsingOVM
{
// Get balance from the OVM_ETH contract.
// Get balance from the OVM_ETH contract.
// NOTE: We may remove this feature in a future release.
// NOTE: We may remove this feature in a future release.
statedumper
.
WriteETH
(
addr
)
key
:=
GetOVMBalanceKey
(
addr
)
key
:=
GetOVMBalanceKey
(
addr
)
bal
:=
s
.
GetState
(
dump
.
OvmEthAddress
,
key
)
bal
:=
s
.
GetState
(
dump
.
OvmEthAddress
,
key
)
return
bal
.
Big
()
return
bal
.
Big
()
...
@@ -377,6 +380,7 @@ func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) {
...
@@ -377,6 +380,7 @@ func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) {
// Note that we don't need to check for overflows or underflows here because the code that
// Note that we don't need to check for overflows or underflows here because the code that
// uses this codepath already checks for them. You can follow the original codepath below
// uses this codepath already checks for them. You can follow the original codepath below
// (stateObject.AddBalance) to confirm that there are no checks being performed here.
// (stateObject.AddBalance) to confirm that there are no checks being performed here.
statedumper
.
WriteETH
(
addr
)
key
:=
GetOVMBalanceKey
(
addr
)
key
:=
GetOVMBalanceKey
(
addr
)
value
:=
s
.
GetState
(
dump
.
OvmEthAddress
,
key
)
value
:=
s
.
GetState
(
dump
.
OvmEthAddress
,
key
)
bal
:=
value
.
Big
()
bal
:=
value
.
Big
()
...
@@ -397,6 +401,7 @@ func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) {
...
@@ -397,6 +401,7 @@ func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) {
// Note that we don't need to check for overflows or underflows here because the code that
// Note that we don't need to check for overflows or underflows here because the code that
// uses this codepath already checks for them. You can follow the original codepath below
// uses this codepath already checks for them. You can follow the original codepath below
// (stateObject.SubBalance) to confirm that there are no checks being performed here.
// (stateObject.SubBalance) to confirm that there are no checks being performed here.
statedumper
.
WriteETH
(
addr
)
key
:=
GetOVMBalanceKey
(
addr
)
key
:=
GetOVMBalanceKey
(
addr
)
value
:=
s
.
GetState
(
dump
.
OvmEthAddress
,
key
)
value
:=
s
.
GetState
(
dump
.
OvmEthAddress
,
key
)
bal
:=
value
.
Big
()
bal
:=
value
.
Big
()
...
@@ -413,6 +418,7 @@ func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) {
...
@@ -413,6 +418,7 @@ func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) {
func
(
s
*
StateDB
)
SetBalance
(
addr
common
.
Address
,
amount
*
big
.
Int
)
{
func
(
s
*
StateDB
)
SetBalance
(
addr
common
.
Address
,
amount
*
big
.
Int
)
{
if
rcfg
.
UsingOVM
{
if
rcfg
.
UsingOVM
{
// Mutate the storage slot inside of OVM_ETH to change balances.
// Mutate the storage slot inside of OVM_ETH to change balances.
statedumper
.
WriteETH
(
addr
)
key
:=
GetOVMBalanceKey
(
addr
)
key
:=
GetOVMBalanceKey
(
addr
)
s
.
SetState
(
dump
.
OvmEthAddress
,
key
,
common
.
BigToHash
(
amount
))
s
.
SetState
(
dump
.
OvmEthAddress
,
key
,
common
.
BigToHash
(
amount
))
}
else
{
}
else
{
...
@@ -580,8 +586,8 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
...
@@ -580,8 +586,8 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
// CreateAccount is called during the EVM CREATE operation. The situation might arise that
// CreateAccount is called during the EVM CREATE operation. The situation might arise that
// a contract does the following:
// a contract does the following:
//
//
//
1. sends funds to sha(account ++ (nonce + 1))
// 1. sends funds to sha(account ++ (nonce + 1))
//
2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
//
//
// Carrying over the balance ensures that Ether doesn't disappear.
// Carrying over the balance ensures that Ether doesn't disappear.
func
(
s
*
StateDB
)
CreateAccount
(
addr
common
.
Address
)
{
func
(
s
*
StateDB
)
CreateAccount
(
addr
common
.
Address
)
{
...
...
l2geth/core/vm/evm.go
View file @
afba7c22
...
@@ -18,6 +18,7 @@ package vm
...
@@ -18,6 +18,7 @@ package vm
import
(
import
(
"fmt"
"fmt"
"github.com/ethereum-optimism/optimism/l2geth/statedumper"
"math/big"
"math/big"
"sync/atomic"
"sync/atomic"
"time"
"time"
...
@@ -198,6 +199,10 @@ func (evm *EVM) Interpreter() Interpreter {
...
@@ -198,6 +199,10 @@ func (evm *EVM) Interpreter() Interpreter {
// the necessary steps to create accounts and reverses the state in case of an
// the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer.
// execution error or failed value transfer.
func
(
evm
*
EVM
)
Call
(
caller
ContractRef
,
addr
common
.
Address
,
input
[]
byte
,
gas
uint64
,
value
*
big
.
Int
)
(
ret
[]
byte
,
leftOverGas
uint64
,
err
error
)
{
func
(
evm
*
EVM
)
Call
(
caller
ContractRef
,
addr
common
.
Address
,
input
[]
byte
,
gas
uint64
,
value
*
big
.
Int
)
(
ret
[]
byte
,
leftOverGas
uint64
,
err
error
)
{
if
addr
==
dump
.
MessagePasserAddress
{
statedumper
.
WriteMessage
(
caller
.
Address
(),
input
)
}
if
evm
.
vmConfig
.
NoRecursion
&&
evm
.
depth
>
0
{
if
evm
.
vmConfig
.
NoRecursion
&&
evm
.
depth
>
0
{
return
nil
,
gas
,
nil
return
nil
,
gas
,
nil
}
}
...
...
l2geth/package.json
View file @
afba7c22
{
{
"name"
:
"@eth-optimism/l2geth"
,
"name"
:
"@eth-optimism/l2geth"
,
"version"
:
"0.5.2
5
"
,
"version"
:
"0.5.2
6
"
,
"private"
:
true
,
"private"
:
true
,
"devDependencies"
:
{}
"devDependencies"
:
{}
}
}
l2geth/rollup/dump/constants.go
View file @
afba7c22
...
@@ -7,3 +7,4 @@ import (
...
@@ -7,3 +7,4 @@ import (
var
OvmEthAddress
=
common
.
HexToAddress
(
"0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
)
var
OvmEthAddress
=
common
.
HexToAddress
(
"0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
)
var
OvmFeeWallet
=
common
.
HexToAddress
(
"0x4200000000000000000000000000000000000011"
)
var
OvmFeeWallet
=
common
.
HexToAddress
(
"0x4200000000000000000000000000000000000011"
)
var
OvmWhitelistAddress
=
common
.
HexToAddress
(
"0x4200000000000000000000000000000000000002"
)
var
OvmWhitelistAddress
=
common
.
HexToAddress
(
"0x4200000000000000000000000000000000000002"
)
var
MessagePasserAddress
=
common
.
HexToAddress
(
"0x4200000000000000000000000000000000000000"
)
l2geth/statedumper/dumper.go
0 → 100644
View file @
afba7c22
package
statedumper
import
(
"fmt"
"github.com/ethereum-optimism/optimism/l2geth/common"
"io"
"os"
"sync"
)
type
StateDumper
interface
{
WriteETH
(
address
common
.
Address
)
WriteMessage
(
sender
common
.
Address
,
msg
[]
byte
)
}
var
DefaultStateDumper
StateDumper
func
NewStateDumper
()
StateDumper
{
path
:=
os
.
Getenv
(
"L2GETH_STATE_DUMP_PATH"
)
if
path
==
""
{
return
&
noopStateDumper
{}
}
f
,
err
:=
os
.
OpenFile
(
path
,
os
.
O_APPEND
|
os
.
O_CREATE
,
0
o755
)
if
err
!=
nil
{
panic
(
err
)
}
return
&
FileStateDumper
{
f
:
f
,
}
}
type
FileStateDumper
struct
{
f
io
.
Writer
mtx
sync
.
Mutex
}
func
(
s
*
FileStateDumper
)
WriteETH
(
address
common
.
Address
)
{
s
.
mtx
.
Lock
()
defer
s
.
mtx
.
Unlock
()
if
_
,
err
:=
s
.
f
.
Write
([]
byte
(
fmt
.
Sprintf
(
"ETH|%s
\n
"
,
address
.
Hex
())));
err
!=
nil
{
panic
(
err
)
}
}
func
(
s
*
FileStateDumper
)
WriteMessage
(
sender
common
.
Address
,
msg
[]
byte
)
{
s
.
mtx
.
Lock
()
defer
s
.
mtx
.
Unlock
()
if
_
,
err
:=
s
.
f
.
Write
([]
byte
(
fmt
.
Sprintf
(
"MSG|%s|%x
\n
"
,
sender
.
Hex
(),
msg
)));
err
!=
nil
{
panic
(
err
)
}
}
type
noopStateDumper
struct
{
}
func
(
n
*
noopStateDumper
)
WriteETH
(
address
common
.
Address
)
{
}
func
(
n
*
noopStateDumper
)
WriteMessage
(
sender
common
.
Address
,
msg
[]
byte
)
{
}
func
init
()
{
DefaultStateDumper
=
NewStateDumper
()
}
func
WriteETH
(
address
common
.
Address
)
{
DefaultStateDumper
.
WriteETH
(
address
)
}
func
WriteMessage
(
sender
common
.
Address
,
msg
[]
byte
)
{
DefaultStateDumper
.
WriteMessage
(
sender
,
msg
)
}
op-chain-ops/Makefile
View file @
afba7c22
surgery
:
surgery
:
go build
-o
./surgery ./cmd/main.go
go build
-o
./surgery ./cmd/
surgery/
main.go
test
:
test
:
go
test
./...
go
test
./...
...
...
op-chain-ops/cmd/main.go
deleted
100644 → 0
View file @
03bb2700
package
main
import
(
"os"
"strings"
ops
"github.com/ethereum-optimism/optimism/op-chain-ops"
"github.com/ethereum/go-ethereum/log"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2"
)
func
main
()
{
log
.
Root
()
.
SetHandler
(
log
.
StreamHandler
(
os
.
Stderr
,
log
.
TerminalFormat
(
isatty
.
IsTerminal
(
os
.
Stderr
.
Fd
()))))
app
:=
&
cli
.
App
{
Name
:
"surgery"
,
Usage
:
"migrates data from v0 to Bedrock"
,
Commands
:
[]
*
cli
.
Command
{
{
Name
:
"dump-addresses"
,
Usage
:
"dumps addresses from OVM ETH"
,
Flags
:
[]
cli
.
Flag
{
&
cli
.
StringFlag
{
Name
:
"out-file"
,
Aliases
:
[]
string
{
"o"
},
Usage
:
"file to write addresses to"
,
Required
:
true
,
},
},
Action
:
dumpAddressesAction
,
},
{
Name
:
"migrate"
,
Usage
:
"migrates state in OVM ETH"
,
Flags
:
[]
cli
.
Flag
{
&
cli
.
StringFlag
{
Name
:
"genesis-file"
,
Aliases
:
[]
string
{
"g"
},
Usage
:
"path to a genesis file"
,
Required
:
true
,
},
&
cli
.
StringFlag
{
Name
:
"out-dir"
,
Aliases
:
[]
string
{
"o"
},
Usage
:
"path to output directory"
,
Required
:
true
,
},
&
cli
.
StringFlag
{
Name
:
"address-lists"
,
Aliases
:
[]
string
{
"a"
},
Usage
:
"comma-separated list of address files to read"
,
Required
:
true
,
},
&
cli
.
StringFlag
{
Name
:
"allowance-lists"
,
Aliases
:
[]
string
{
"l"
},
Usage
:
"comma-separated list of allowance lists to read"
,
Required
:
true
,
},
&
cli
.
IntFlag
{
Name
:
"chain-id"
,
Usage
:
"chain ID"
,
Value
:
1
,
Required
:
false
,
},
&
cli
.
IntFlag
{
Name
:
"leveldb-cache-size-mb"
,
Usage
:
"leveldb cache size in MB"
,
Value
:
16
,
Required
:
false
,
},
&
cli
.
IntFlag
{
Name
:
"leveldb-file-handles"
,
Usage
:
"leveldb file handles"
,
Value
:
16
,
Required
:
false
,
},
},
Action
:
migrateAction
,
},
},
Flags
:
[]
cli
.
Flag
{
&
cli
.
StringFlag
{
Name
:
"data-dir"
,
Aliases
:
[]
string
{
"d"
},
Usage
:
"data directory to read"
,
Required
:
true
,
},
},
}
if
err
:=
app
.
Run
(
os
.
Args
);
err
!=
nil
{
log
.
Crit
(
"error in migration"
,
"err"
,
err
)
}
}
func
dumpAddressesAction
(
cliCtx
*
cli
.
Context
)
error
{
dataDir
:=
cliCtx
.
String
(
"data-dir"
)
outFile
:=
cliCtx
.
String
(
"out-file"
)
return
ops
.
DumpAddresses
(
dataDir
,
outFile
)
}
func
migrateAction
(
cliCtx
*
cli
.
Context
)
error
{
dataDir
:=
cliCtx
.
String
(
"data-dir"
)
outDir
:=
cliCtx
.
String
(
"out-dir"
)
genesisPath
:=
cliCtx
.
String
(
"genesis-file"
)
addressLists
:=
strings
.
Split
(
cliCtx
.
String
(
"address-lists"
),
","
)
allowanceLists
:=
strings
.
Split
(
cliCtx
.
String
(
"allowance-lists"
),
","
)
chainID
:=
cliCtx
.
Int
(
"chain-id"
)
levelDBCacheSize
:=
cliCtx
.
Int
(
"leveldb-cache-size-mb"
)
levelDBHandles
:=
cliCtx
.
Int
(
"leveldb-file-handles"
)
genesis
,
err
:=
ops
.
ReadGenesisFromFile
(
genesisPath
)
if
err
!=
nil
{
return
err
}
return
ops
.
Migrate
(
dataDir
,
outDir
,
genesis
,
addressLists
,
allowanceLists
,
chainID
,
levelDBCacheSize
,
levelDBHandles
)
}
op-chain-ops/crossdomain/legacy_withdrawal.go
View file @
afba7c22
...
@@ -14,10 +14,10 @@ import (
...
@@ -14,10 +14,10 @@ import (
// LegacyWithdrawal represents a pre bedrock upgrade withdrawal.
// LegacyWithdrawal represents a pre bedrock upgrade withdrawal.
type
LegacyWithdrawal
struct
{
type
LegacyWithdrawal
struct
{
Target
*
common
.
Address
Target
*
common
.
Address
`json:"target"`
Sender
*
common
.
Address
Sender
*
common
.
Address
`json:"sender"`
Data
[]
byte
Data
[]
byte
`json:"data"`
Nonce
*
big
.
Int
Nonce
*
big
.
Int
`json:"nonce"`
}
}
var
_
WithdrawalMessage
=
(
*
LegacyWithdrawal
)(
nil
)
var
_
WithdrawalMessage
=
(
*
LegacyWithdrawal
)(
nil
)
...
...
op-chain-ops/crossdomain/migrate.go
0 → 100644
View file @
afba7c22
package
crossdomain
import
(
"errors"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
)
var
(
abiTrue
=
common
.
Hash
{
31
:
0x01
}
errLegacyStorageSlotNotFound
=
errors
.
New
(
"cannot find storage slot"
)
)
// MigrateWithdrawals will migrate a list of pending withdrawals given a StateDB.
func
MigrateWithdrawals
(
withdrawals
[]
*
PendingWithdrawal
,
db
vm
.
StateDB
,
l1CrossDomainMessenger
,
l1StandardBridge
*
common
.
Address
)
error
{
for
_
,
legacy
:=
range
withdrawals
{
legacySlot
,
err
:=
legacy
.
StorageSlot
()
if
err
!=
nil
{
return
err
}
legacyValue
:=
db
.
GetState
(
predeploys
.
LegacyMessagePasserAddr
,
legacySlot
)
if
legacyValue
!=
abiTrue
{
return
fmt
.
Errorf
(
"%w: %s"
,
errLegacyStorageSlotNotFound
,
legacyValue
)
}
withdrawal
,
err
:=
MigrateWithdrawal
(
&
legacy
.
LegacyWithdrawal
,
l1CrossDomainMessenger
,
l1StandardBridge
)
if
err
!=
nil
{
return
err
}
slot
,
err
:=
withdrawal
.
StorageSlot
()
if
err
!=
nil
{
return
err
}
db
.
SetState
(
predeploys
.
L2ToL1MessagePasserAddr
,
slot
,
abiTrue
)
}
return
nil
}
// MigrateWithdrawal will turn a LegacyWithdrawal into a bedrock
// style Withdrawal.
func
MigrateWithdrawal
(
withdrawal
*
LegacyWithdrawal
,
l1CrossDomainMessenger
,
l1StandardBridge
*
common
.
Address
)
(
*
Withdrawal
,
error
)
{
value
:=
new
(
big
.
Int
)
isFromL2StandardBridge
:=
*
withdrawal
.
Sender
==
predeploys
.
L2StandardBridgeAddr
if
withdrawal
.
Target
==
nil
{
return
nil
,
errors
.
New
(
"withdrawal target cannot be nil"
)
}
isToL1StandardBridge
:=
*
withdrawal
.
Target
==
*
l1StandardBridge
if
isFromL2StandardBridge
&&
isToL1StandardBridge
{
abi
,
err
:=
bindings
.
L1StandardBridgeMetaData
.
GetAbi
()
if
err
!=
nil
{
return
nil
,
err
}
method
,
err
:=
abi
.
MethodById
(
withdrawal
.
Data
)
if
err
!=
nil
{
return
nil
,
err
}
if
method
.
Name
==
"finalizeETHWithdrawal"
{
data
,
err
:=
method
.
Inputs
.
Unpack
(
withdrawal
.
Data
[
4
:
])
if
err
!=
nil
{
return
nil
,
err
}
// bounds check
if
len
(
data
)
<
3
{
return
nil
,
errors
.
New
(
"not enough data"
)
}
var
ok
bool
value
,
ok
=
data
[
2
]
.
(
*
big
.
Int
)
if
!
ok
{
return
nil
,
errors
.
New
(
"not big.Int"
)
}
}
}
abi
,
err
:=
bindings
.
L1CrossDomainMessengerMetaData
.
GetAbi
()
if
err
!=
nil
{
return
nil
,
err
}
versionedNonce
:=
EncodeVersionedNonce
(
withdrawal
.
Nonce
,
common
.
Big1
)
data
,
err
:=
abi
.
Pack
(
"relayMessage"
,
versionedNonce
,
withdrawal
.
Sender
,
withdrawal
.
Target
,
value
,
new
(
big
.
Int
),
withdrawal
.
Data
,
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"cannot abi encode relayMessage: %w"
,
err
)
}
w
:=
NewWithdrawal
(
withdrawal
.
Nonce
,
&
predeploys
.
L2CrossDomainMessengerAddr
,
l1CrossDomainMessenger
,
value
,
new
(
big
.
Int
),
data
,
)
return
w
,
nil
}
op-chain-ops/crossdomain/migrate_test.go
0 → 100644
View file @
afba7c22
package
crossdomain_test
import
(
"fmt"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
func
TestMigrateWithdrawal
(
t
*
testing
.
T
)
{
withdrawals
:=
make
([]
*
crossdomain
.
LegacyWithdrawal
,
0
)
for
_
,
receipt
:=
range
receipts
{
msg
,
err
:=
findCrossDomainMessage
(
receipt
)
require
.
Nil
(
t
,
err
)
withdrawal
,
err
:=
msg
.
ToWithdrawal
()
require
.
Nil
(
t
,
err
)
legacyWithdrawal
,
ok
:=
withdrawal
.
(
*
crossdomain
.
LegacyWithdrawal
)
require
.
True
(
t
,
ok
)
withdrawals
=
append
(
withdrawals
,
legacyWithdrawal
)
}
l1CrossDomainMessenger
:=
common
.
HexToAddress
(
"0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1"
)
l1StandardBridge
:=
common
.
HexToAddress
(
"0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1"
)
for
i
,
legacy
:=
range
withdrawals
{
t
.
Run
(
fmt
.
Sprintf
(
"test%d"
,
i
),
func
(
t
*
testing
.
T
)
{
withdrawal
,
err
:=
crossdomain
.
MigrateWithdrawal
(
legacy
,
&
l1CrossDomainMessenger
,
&
l1StandardBridge
)
require
.
Nil
(
t
,
err
)
require
.
NotNil
(
t
,
withdrawal
)
require
.
Equal
(
t
,
legacy
.
Nonce
.
Uint64
(),
withdrawal
.
Nonce
.
Uint64
())
require
.
Equal
(
t
,
*
withdrawal
.
Sender
,
predeploys
.
L2CrossDomainMessengerAddr
)
require
.
Equal
(
t
,
*
withdrawal
.
Target
,
l1CrossDomainMessenger
)
})
}
}
op-chain-ops/crossdomain/withdrawal.go
View file @
afba7c22
...
@@ -13,12 +13,12 @@ var _ WithdrawalMessage = (*Withdrawal)(nil)
...
@@ -13,12 +13,12 @@ var _ WithdrawalMessage = (*Withdrawal)(nil)
// Withdrawal represents a withdrawal transaction on L2
// Withdrawal represents a withdrawal transaction on L2
type
Withdrawal
struct
{
type
Withdrawal
struct
{
Nonce
*
big
.
Int
Nonce
*
big
.
Int
`json:"nonce"`
Sender
*
common
.
Address
Sender
*
common
.
Address
`json:"sender"`
Target
*
common
.
Address
Target
*
common
.
Address
`json:"target"`
Value
*
big
.
Int
Value
*
big
.
Int
`json:"value"`
GasLimit
*
big
.
Int
GasLimit
*
big
.
Int
`json:"gasLimit"`
Data
[]
byte
Data
[]
byte
`json:"data"`
}
}
// NewWithdrawal will create a Withdrawal
// NewWithdrawal will create a Withdrawal
...
...
op-chain-ops/crossdomain/withdrawals.go
View file @
afba7c22
...
@@ -15,12 +15,8 @@ import (
...
@@ -15,12 +15,8 @@ import (
// A PendingWithdrawal represents a withdrawal that has
// A PendingWithdrawal represents a withdrawal that has
// not been finalized on L1
// not been finalized on L1
type
PendingWithdrawal
struct
{
type
PendingWithdrawal
struct
{
Target
common
.
Address
`json:"target"`
LegacyWithdrawal
`json:"withdrawal"`
Sender
common
.
Address
`json:"sender"`
TransactionHash
common
.
Hash
`json:"transactionHash"`
Message
[]
byte
`json:"message"`
MessageNonce
*
big
.
Int
`json:"nonce"`
GasLimit
*
big
.
Int
`json:"gasLimit"`
TransactionHash
common
.
Hash
`json:"transactionHash"`
}
}
// Backends represents a set of backends for L1 and L2.
// Backends represents a set of backends for L1 and L2.
...
@@ -119,11 +115,12 @@ func GetPendingWithdrawals(messengers *Messengers, version *big.Int, start, end
...
@@ -119,11 +115,12 @@ func GetPendingWithdrawals(messengers *Messengers, version *big.Int, start, end
log
.
Info
(
"%s not yet relayed"
,
event
.
Raw
.
TxHash
)
log
.
Info
(
"%s not yet relayed"
,
event
.
Raw
.
TxHash
)
withdrawal
:=
PendingWithdrawal
{
withdrawal
:=
PendingWithdrawal
{
Target
:
event
.
Target
,
LegacyWithdrawal
:
LegacyWithdrawal
{
Sender
:
event
.
Sender
,
Target
:
&
event
.
Target
,
Message
:
event
.
Message
,
Sender
:
&
event
.
Sender
,
MessageNonce
:
event
.
MessageNonce
,
Data
:
event
.
Message
,
GasLimit
:
event
.
GasLimit
,
Nonce
:
event
.
MessageNonce
,
},
TransactionHash
:
event
.
Raw
.
TxHash
,
TransactionHash
:
event
.
Raw
.
TxHash
,
}
}
...
...
op-chain-ops/crossdomain/withdrawals_test.go
View file @
afba7c22
...
@@ -272,8 +272,7 @@ func TestGetPendingWithdrawals(t *testing.T) {
...
@@ -272,8 +272,7 @@ func TestGetPendingWithdrawals(t *testing.T) {
for
i
,
msg
:=
range
msgs
[
3
:
]
{
for
i
,
msg
:=
range
msgs
[
3
:
]
{
withdrawal
:=
withdrawals
[
i
]
withdrawal
:=
withdrawals
[
i
]
require
.
Equal
(
t
,
msg
.
Target
,
withdrawal
.
Target
)
require
.
Equal
(
t
,
msg
.
Target
,
*
withdrawal
.
Target
)
require
.
Equal
(
t
,
msg
.
Message
,
withdrawal
.
Message
)
require
.
Equal
(
t
,
msg
.
Message
,
withdrawal
.
Data
)
require
.
Equal
(
t
,
uint64
(
msg
.
MinGasLimit
),
withdrawal
.
GasLimit
.
Uint64
())
}
}
}
}
op-chain-ops/addresses.go
→
op-chain-ops/
ether/
addresses.go
View file @
afba7c22
package
state_surgery
package
ether
import
(
import
(
"bufio"
"bufio"
...
...
op-chain-ops/cli.go
→
op-chain-ops/
ether/
cli.go
View file @
afba7c22
package
state_surgery
package
ether
import
(
import
(
"bytes"
"bytes"
...
...
op-chain-ops/db.go
→
op-chain-ops/
ether/
db.go
View file @
afba7c22
package
state_surgery
package
ether
import
(
import
(
"path/filepath"
"path/filepath"
...
...
op-chain-ops/genesis.go
→
op-chain-ops/
ether/
genesis.go
View file @
afba7c22
package
state_surgery
package
ether
import
(
import
(
"encoding/json"
"encoding/json"
...
...
op-chain-ops/params.go
→
op-chain-ops/
ether/
params.go
View file @
afba7c22
package
state_surgery
package
ether
import
(
import
(
"math/big"
"math/big"
...
...
op-chain-ops/storage.go
→
op-chain-ops/
ether/
storage.go
View file @
afba7c22
package
state_surgery
package
ether
import
(
import
(
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
...
...
op-chain-ops/util.go
→
op-chain-ops/
ether/
util.go
View file @
afba7c22
package
state_surgery
package
ether
import
(
import
(
"fmt"
"fmt"
...
...
op-e2e/actions/l2_engine_api.go
View file @
afba7c22
...
@@ -96,7 +96,7 @@ func (ea *L2EngineAPI) startBlock(parent common.Hash, params *eth.PayloadAttribu
...
@@ -96,7 +96,7 @@ func (ea *L2EngineAPI) startBlock(parent common.Hash, params *eth.PayloadAttribu
if
err
:=
tx
.
UnmarshalBinary
(
otx
);
err
!=
nil
{
if
err
:=
tx
.
UnmarshalBinary
(
otx
);
err
!=
nil
{
return
fmt
.
Errorf
(
"transaction %d is not valid: %v"
,
i
,
err
)
return
fmt
.
Errorf
(
"transaction %d is not valid: %v"
,
i
,
err
)
}
}
ea
.
l2BuildingState
.
Prepare
(
tx
.
Hash
(),
i
)
receipt
,
err
:=
core
.
ApplyTransaction
(
ea
.
l2Cfg
.
Config
,
ea
.
l2Chain
,
&
ea
.
l2BuildingHeader
.
Coinbase
,
receipt
,
err
:=
core
.
ApplyTransaction
(
ea
.
l2Cfg
.
Config
,
ea
.
l2Chain
,
&
ea
.
l2BuildingHeader
.
Coinbase
,
ea
.
l2GasPool
,
ea
.
l2BuildingState
,
ea
.
l2BuildingHeader
,
&
tx
,
&
ea
.
l2BuildingHeader
.
GasUsed
,
*
ea
.
l2Chain
.
GetVMConfig
())
ea
.
l2GasPool
,
ea
.
l2BuildingState
,
ea
.
l2BuildingHeader
,
&
tx
,
&
ea
.
l2BuildingHeader
.
GasUsed
,
*
ea
.
l2Chain
.
GetVMConfig
())
if
err
!=
nil
{
if
err
!=
nil
{
...
...
op-e2e/actions/user.go
View file @
afba7c22
...
@@ -149,6 +149,13 @@ func (s *BasicUser[B]) ActRandomTxToAddr(t Testing) {
...
@@ -149,6 +149,13 @@ func (s *BasicUser[B]) ActRandomTxToAddr(t Testing) {
s
.
txToAddr
=
to
s
.
txToAddr
=
to
}
}
func
(
s
*
BasicUser
[
B
])
ActSetTxCalldata
(
calldata
[]
byte
)
Action
{
return
func
(
t
Testing
)
{
require
.
NotNil
(
t
,
calldata
)
s
.
txCallData
=
calldata
}
}
func
(
s
*
BasicUser
[
B
])
ActSetTxToAddr
(
to
*
common
.
Address
)
Action
{
func
(
s
*
BasicUser
[
B
])
ActSetTxToAddr
(
to
*
common
.
Address
)
Action
{
return
func
(
t
Testing
)
{
return
func
(
t
Testing
)
{
s
.
txToAddr
=
to
s
.
txToAddr
=
to
...
@@ -165,6 +172,12 @@ func (s *BasicUser[B]) ActRandomTxValue(t Testing) {
...
@@ -165,6 +172,12 @@ func (s *BasicUser[B]) ActRandomTxValue(t Testing) {
s
.
txOpts
.
Value
=
big
.
NewInt
(
s
.
rng
.
Int63
())
s
.
txOpts
.
Value
=
big
.
NewInt
(
s
.
rng
.
Int63
())
}
}
func
(
s
*
BasicUser
[
B
])
ActSetTxValue
(
value
*
big
.
Int
)
Action
{
return
func
(
t
Testing
)
{
s
.
txOpts
.
Value
=
value
}
}
func
(
s
*
BasicUser
[
B
])
ActRandomTxData
(
t
Testing
)
{
func
(
s
*
BasicUser
[
B
])
ActRandomTxData
(
t
Testing
)
{
dataLen
:=
s
.
rng
.
Intn
(
128
_000
)
dataLen
:=
s
.
rng
.
Intn
(
128
_000
)
out
:=
make
([]
byte
,
dataLen
)
out
:=
make
([]
byte
,
dataLen
)
...
@@ -190,6 +203,13 @@ func (s *BasicUser[B]) TxValue() *big.Int {
...
@@ -190,6 +203,13 @@ func (s *BasicUser[B]) TxValue() *big.Int {
return
big
.
NewInt
(
0
)
return
big
.
NewInt
(
0
)
}
}
func
(
s
*
BasicUser
[
B
])
LastTxReceipt
(
t
Testing
)
*
types
.
Receipt
{
require
.
NotEqual
(
t
,
s
.
lastTxHash
,
common
.
Hash
{},
"must send tx before getting last receipt"
)
receipt
,
err
:=
s
.
env
.
EthCl
.
TransactionReceipt
(
t
.
Ctx
(),
s
.
lastTxHash
)
require
.
NoError
(
t
,
err
)
return
receipt
}
// ActMakeTx makes a tx with the predetermined contents (see randomization and other actions)
// ActMakeTx makes a tx with the predetermined contents (see randomization and other actions)
// and sends it to the tx pool
// and sends it to the tx pool
func
(
s
*
BasicUser
[
B
])
ActMakeTx
(
t
Testing
)
{
func
(
s
*
BasicUser
[
B
])
ActMakeTx
(
t
Testing
)
{
...
@@ -210,10 +230,13 @@ func (s *BasicUser[B]) ActMakeTx(t Testing) {
...
@@ -210,10 +230,13 @@ func (s *BasicUser[B]) ActMakeTx(t Testing) {
ChainID
:
s
.
env
.
Signer
.
ChainID
(),
ChainID
:
s
.
env
.
Signer
.
ChainID
(),
Nonce
:
s
.
PendingNonce
(
t
),
Nonce
:
s
.
PendingNonce
(
t
),
Gas
:
gas
,
Gas
:
gas
,
Data
:
s
.
txCallData
,
})
})
err
=
s
.
env
.
EthCl
.
SendTransaction
(
t
.
Ctx
(),
tx
)
err
=
s
.
env
.
EthCl
.
SendTransaction
(
t
.
Ctx
(),
tx
)
require
.
NoError
(
t
,
err
,
"must send tx"
)
require
.
NoError
(
t
,
err
,
"must send tx"
)
s
.
lastTxHash
=
tx
.
Hash
()
s
.
lastTxHash
=
tx
.
Hash
()
// reset the calldata
s
.
txCallData
=
[]
byte
{}
}
}
func
(
s
*
BasicUser
[
B
])
ActCheckReceiptStatusOfLastTx
(
success
bool
)
func
(
t
Testing
)
{
func
(
s
*
BasicUser
[
B
])
ActCheckReceiptStatusOfLastTx
(
success
bool
)
func
(
t
Testing
)
{
...
...
op-node/metrics/metrics.go
View file @
afba7c22
...
@@ -10,6 +10,8 @@ import (
...
@@ -10,6 +10,8 @@ import (
"strconv"
"strconv"
"time"
"time"
libp2pmetrics
"github.com/libp2p/go-libp2p-core/metrics"
pb
"github.com/libp2p/go-libp2p-pubsub/pb"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promauto"
...
@@ -30,6 +32,32 @@ const (
...
@@ -30,6 +32,32 @@ const (
BatchMethod
=
"<batch>"
BatchMethod
=
"<batch>"
)
)
type
Metricer
interface
{
RecordInfo
(
version
string
)
RecordUp
()
RecordRPCServerRequest
(
method
string
)
func
()
RecordRPCClientRequest
(
method
string
)
func
(
err
error
)
RecordRPCClientResponse
(
method
string
,
err
error
)
SetDerivationIdle
(
status
bool
)
RecordPipelineReset
()
RecordSequencingError
()
RecordPublishingError
()
RecordDerivationError
()
RecordReceivedUnsafePayload
(
payload
*
eth
.
ExecutionPayload
)
recordRef
(
layer
string
,
name
string
,
num
uint64
,
timestamp
uint64
,
h
common
.
Hash
)
RecordL1Ref
(
name
string
,
ref
eth
.
L1BlockRef
)
RecordL2Ref
(
name
string
,
ref
eth
.
L2BlockRef
)
RecordUnsafePayloadsBuffer
(
length
uint64
,
memSize
uint64
,
next
eth
.
BlockID
)
CountSequencedTxs
(
count
int
)
RecordL1ReorgDepth
(
d
uint64
)
RecordGossipEvent
(
evType
int32
)
IncPeerCount
()
DecPeerCount
()
IncStreamCount
()
DecStreamCount
()
RecordBandwidth
(
ctx
context
.
Context
,
bwc
*
libp2pmetrics
.
BandwidthCounter
)
}
type
Metrics
struct
{
type
Metrics
struct
{
Info
*
prometheus
.
GaugeVec
Info
*
prometheus
.
GaugeVec
Up
prometheus
.
Gauge
Up
prometheus
.
Gauge
...
@@ -67,6 +95,12 @@ type Metrics struct {
...
@@ -67,6 +95,12 @@ type Metrics struct {
TransactionsSequencedTotal
prometheus
.
Counter
TransactionsSequencedTotal
prometheus
.
Counter
// P2P Metrics
PeerCount
prometheus
.
Gauge
StreamCount
prometheus
.
Gauge
GossipEventsTotal
*
prometheus
.
CounterVec
BandwidthTotal
*
prometheus
.
GaugeVec
registry
*
prometheus
.
Registry
registry
*
prometheus
.
Registry
}
}
...
@@ -217,6 +251,35 @@ func NewMetrics(procName string) *Metrics {
...
@@ -217,6 +251,35 @@ func NewMetrics(procName string) *Metrics {
Help
:
"Count of total transactions sequenced"
,
Help
:
"Count of total transactions sequenced"
,
}),
}),
PeerCount
:
promauto
.
With
(
registry
)
.
NewGauge
(
prometheus
.
GaugeOpts
{
Namespace
:
ns
,
Subsystem
:
"p2p"
,
Name
:
"peer_count"
,
Help
:
"Count of currently connected p2p peers"
,
}),
StreamCount
:
promauto
.
With
(
registry
)
.
NewGauge
(
prometheus
.
GaugeOpts
{
Namespace
:
ns
,
Subsystem
:
"p2p"
,
Name
:
"stream_count"
,
Help
:
"Count of currently connected p2p streams"
,
}),
GossipEventsTotal
:
promauto
.
With
(
registry
)
.
NewCounterVec
(
prometheus
.
CounterOpts
{
Namespace
:
ns
,
Subsystem
:
"p2p"
,
Name
:
"gossip_events_total"
,
Help
:
"Count of gossip events by type"
,
},
[]
string
{
"type"
,
}),
BandwidthTotal
:
promauto
.
With
(
registry
)
.
NewGaugeVec
(
prometheus
.
GaugeOpts
{
Namespace
:
ns
,
Subsystem
:
"p2p"
,
Name
:
"bandwidth_bytes_total"
,
Help
:
"P2P bandwidth by direction"
,
},
[]
string
{
"direction"
,
}),
registry
:
registry
,
registry
:
registry
,
}
}
}
}
...
@@ -348,6 +411,42 @@ func (m *Metrics) RecordL1ReorgDepth(d uint64) {
...
@@ -348,6 +411,42 @@ func (m *Metrics) RecordL1ReorgDepth(d uint64) {
m
.
L1ReorgDepth
.
Observe
(
float64
(
d
))
m
.
L1ReorgDepth
.
Observe
(
float64
(
d
))
}
}
func
(
m
*
Metrics
)
RecordGossipEvent
(
evType
int32
)
{
m
.
GossipEventsTotal
.
WithLabelValues
(
pb
.
TraceEvent_Type_name
[
evType
])
.
Inc
()
}
func
(
m
*
Metrics
)
IncPeerCount
()
{
m
.
PeerCount
.
Inc
()
}
func
(
m
*
Metrics
)
DecPeerCount
()
{
m
.
PeerCount
.
Dec
()
}
func
(
m
*
Metrics
)
IncStreamCount
()
{
m
.
StreamCount
.
Inc
()
}
func
(
m
*
Metrics
)
DecStreamCount
()
{
m
.
StreamCount
.
Dec
()
}
func
(
m
*
Metrics
)
RecordBandwidth
(
ctx
context
.
Context
,
bwc
*
libp2pmetrics
.
BandwidthCounter
)
{
tick
:=
time
.
NewTicker
(
10
*
time
.
Second
)
defer
tick
.
Stop
()
for
{
select
{
case
<-
tick
.
C
:
bwTotals
:=
bwc
.
GetBandwidthTotals
()
m
.
BandwidthTotal
.
WithLabelValues
(
"in"
)
.
Set
(
float64
(
bwTotals
.
TotalIn
))
m
.
BandwidthTotal
.
WithLabelValues
(
"out"
)
.
Set
(
float64
(
bwTotals
.
TotalOut
))
case
<-
ctx
.
Done
()
:
return
}
}
}
// Serve starts the metrics server on the given hostname and port.
// Serve starts the metrics server on the given hostname and port.
// The server will be closed when the passed-in context is cancelled.
// The server will be closed when the passed-in context is cancelled.
func
(
m
*
Metrics
)
Serve
(
ctx
context
.
Context
,
hostname
string
,
port
int
)
error
{
func
(
m
*
Metrics
)
Serve
(
ctx
context
.
Context
,
hostname
string
,
port
int
)
error
{
...
@@ -364,3 +463,78 @@ func (m *Metrics) Serve(ctx context.Context, hostname string, port int) error {
...
@@ -364,3 +463,78 @@ func (m *Metrics) Serve(ctx context.Context, hostname string, port int) error {
}()
}()
return
server
.
ListenAndServe
()
return
server
.
ListenAndServe
()
}
}
type
noopMetricer
struct
{}
var
NoopMetrics
=
new
(
noopMetricer
)
func
(
n
*
noopMetricer
)
RecordInfo
(
version
string
)
{
}
func
(
n
*
noopMetricer
)
RecordUp
()
{
}
func
(
n
*
noopMetricer
)
RecordRPCServerRequest
(
method
string
)
func
()
{
return
func
()
{}
}
func
(
n
*
noopMetricer
)
RecordRPCClientRequest
(
method
string
)
func
(
err
error
)
{
return
func
(
err
error
)
{}
}
func
(
n
*
noopMetricer
)
RecordRPCClientResponse
(
method
string
,
err
error
)
{
}
func
(
n
*
noopMetricer
)
SetDerivationIdle
(
status
bool
)
{
}
func
(
n
*
noopMetricer
)
RecordPipelineReset
()
{
}
func
(
n
*
noopMetricer
)
RecordSequencingError
()
{
}
func
(
n
*
noopMetricer
)
RecordPublishingError
()
{
}
func
(
n
*
noopMetricer
)
RecordDerivationError
()
{
}
func
(
n
*
noopMetricer
)
RecordReceivedUnsafePayload
(
payload
*
eth
.
ExecutionPayload
)
{
}
func
(
n
*
noopMetricer
)
recordRef
(
layer
string
,
name
string
,
num
uint64
,
timestamp
uint64
,
h
common
.
Hash
)
{
}
func
(
n
*
noopMetricer
)
RecordL1Ref
(
name
string
,
ref
eth
.
L1BlockRef
)
{
}
func
(
n
*
noopMetricer
)
RecordL2Ref
(
name
string
,
ref
eth
.
L2BlockRef
)
{
}
func
(
n
*
noopMetricer
)
RecordUnsafePayloadsBuffer
(
length
uint64
,
memSize
uint64
,
next
eth
.
BlockID
)
{
}
func
(
n
*
noopMetricer
)
CountSequencedTxs
(
count
int
)
{
}
func
(
n
*
noopMetricer
)
RecordL1ReorgDepth
(
d
uint64
)
{
}
func
(
n
*
noopMetricer
)
RecordGossipEvent
(
evType
int32
)
{
}
func
(
n
*
noopMetricer
)
IncPeerCount
()
{
}
func
(
n
*
noopMetricer
)
DecPeerCount
()
{
}
func
(
n
*
noopMetricer
)
IncStreamCount
()
{
}
func
(
n
*
noopMetricer
)
DecStreamCount
()
{
}
func
(
n
*
noopMetricer
)
RecordBandwidth
(
ctx
context
.
Context
,
bwc
*
libp2pmetrics
.
BandwidthCounter
)
{
}
op-node/node/node.go
View file @
afba7c22
...
@@ -196,7 +196,7 @@ func (n *OpNode) initMetricsServer(ctx context.Context, cfg *Config) error {
...
@@ -196,7 +196,7 @@ func (n *OpNode) initMetricsServer(ctx context.Context, cfg *Config) error {
func
(
n
*
OpNode
)
initP2P
(
ctx
context
.
Context
,
cfg
*
Config
)
error
{
func
(
n
*
OpNode
)
initP2P
(
ctx
context
.
Context
,
cfg
*
Config
)
error
{
if
cfg
.
P2P
!=
nil
{
if
cfg
.
P2P
!=
nil
{
p2pNode
,
err
:=
p2p
.
NewNodeP2P
(
n
.
resourcesCtx
,
&
cfg
.
Rollup
,
n
.
log
,
cfg
.
P2P
,
n
)
p2pNode
,
err
:=
p2p
.
NewNodeP2P
(
n
.
resourcesCtx
,
&
cfg
.
Rollup
,
n
.
log
,
cfg
.
P2P
,
n
,
n
.
metrics
)
if
err
!=
nil
||
p2pNode
==
nil
{
if
err
!=
nil
||
p2pNode
==
nil
{
return
err
return
err
}
}
...
...
op-node/p2p/config.go
View file @
afba7c22
...
@@ -41,7 +41,7 @@ import (
...
@@ -41,7 +41,7 @@ import (
type
SetupP2P
interface
{
type
SetupP2P
interface
{
Check
()
error
Check
()
error
// Host creates a libp2p host service. Returns nil, nil if p2p is disabled.
// Host creates a libp2p host service. Returns nil, nil if p2p is disabled.
Host
(
log
log
.
Logger
)
(
host
.
Host
,
error
)
Host
(
log
log
.
Logger
,
reporter
metrics
.
Reporter
)
(
host
.
Host
,
error
)
// Discovery creates a disc-v5 service. Returns nil, nil, nil if discovery is disabled.
// Discovery creates a disc-v5 service. Returns nil, nil, nil if discovery is disabled.
Discovery
(
log
log
.
Logger
,
rollupCfg
*
rollup
.
Config
,
tcpPort
uint16
)
(
*
enode
.
LocalNode
,
*
discover
.
UDPv5
,
error
)
Discovery
(
log
log
.
Logger
,
rollupCfg
*
rollup
.
Config
,
tcpPort
uint16
)
(
*
enode
.
LocalNode
,
*
discover
.
UDPv5
,
error
)
TargetPeers
()
uint
TargetPeers
()
uint
...
@@ -91,8 +91,6 @@ type Config struct {
...
@@ -91,8 +91,6 @@ type Config struct {
ConnGater
func
(
conf
*
Config
)
(
connmgr
.
ConnectionGater
,
error
)
ConnGater
func
(
conf
*
Config
)
(
connmgr
.
ConnectionGater
,
error
)
ConnMngr
func
(
conf
*
Config
)
(
connmgr
.
ConnManager
,
error
)
ConnMngr
func
(
conf
*
Config
)
(
connmgr
.
ConnManager
,
error
)
// nil to disable bandwidth metrics
BandwidthMetrics
metrics
.
Reporter
}
}
type
ConnectionGater
interface
{
type
ConnectionGater
interface
{
...
...
op-node/p2p/gossip.go
View file @
afba7c22
...
@@ -46,6 +46,10 @@ var MessageDomainValidSnappy = [4]byte{1, 0, 0, 0}
...
@@ -46,6 +46,10 @@ var MessageDomainValidSnappy = [4]byte{1, 0, 0, 0}
const
MaxGossipSize
=
1
<<
20
const
MaxGossipSize
=
1
<<
20
type
GossipMetricer
interface
{
RecordGossipEvent
(
evType
int32
)
}
func
blocksTopicV1
(
cfg
*
rollup
.
Config
)
string
{
func
blocksTopicV1
(
cfg
*
rollup
.
Config
)
string
{
return
fmt
.
Sprintf
(
"/optimism/%s/0/blocks"
,
cfg
.
L2ChainID
.
String
())
return
fmt
.
Sprintf
(
"/optimism/%s/0/blocks"
,
cfg
.
L2ChainID
.
String
())
}
}
...
@@ -115,7 +119,7 @@ func BuildGlobalGossipParams(cfg *rollup.Config) pubsub.GossipSubParams {
...
@@ -115,7 +119,7 @@ func BuildGlobalGossipParams(cfg *rollup.Config) pubsub.GossipSubParams {
return
params
return
params
}
}
func
NewGossipSub
(
p2pCtx
context
.
Context
,
h
host
.
Host
,
cfg
*
rollup
.
Config
)
(
*
pubsub
.
PubSub
,
error
)
{
func
NewGossipSub
(
p2pCtx
context
.
Context
,
h
host
.
Host
,
cfg
*
rollup
.
Config
,
m
GossipMetricer
)
(
*
pubsub
.
PubSub
,
error
)
{
denyList
,
err
:=
pubsub
.
NewTimeCachedBlacklist
(
30
*
time
.
Second
)
denyList
,
err
:=
pubsub
.
NewTimeCachedBlacklist
(
30
*
time
.
Second
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
...
@@ -132,6 +136,7 @@ func NewGossipSub(p2pCtx context.Context, h host.Host, cfg *rollup.Config) (*pub
...
@@ -132,6 +136,7 @@ func NewGossipSub(p2pCtx context.Context, h host.Host, cfg *rollup.Config) (*pub
pubsub
.
WithPeerExchange
(
false
),
pubsub
.
WithPeerExchange
(
false
),
pubsub
.
WithBlacklist
(
denyList
),
pubsub
.
WithBlacklist
(
denyList
),
pubsub
.
WithGossipSubParams
(
BuildGlobalGossipParams
(
cfg
)),
pubsub
.
WithGossipSubParams
(
BuildGlobalGossipParams
(
cfg
)),
pubsub
.
WithEventTracer
(
&
gossipTracer
{
m
:
m
}),
)
)
// TODO: pubsub.WithPeerScoreInspect(inspect, InspectInterval) to update peerstore scores with gossip scores
// TODO: pubsub.WithPeerScoreInspect(inspect, InspectInterval) to update peerstore scores with gossip scores
}
}
...
@@ -441,3 +446,13 @@ func LogTopicEvents(ctx context.Context, log log.Logger, evHandler *pubsub.Topic
...
@@ -441,3 +446,13 @@ func LogTopicEvents(ctx context.Context, log log.Logger, evHandler *pubsub.Topic
}
}
}
}
}
}
type
gossipTracer
struct
{
m
GossipMetricer
}
func
(
g
*
gossipTracer
)
Trace
(
evt
*
pb
.
TraceEvent
)
{
if
g
.
m
!=
nil
{
g
.
m
.
RecordGossipEvent
(
int32
(
*
evt
.
Type
))
}
}
op-node/p2p/host.go
View file @
afba7c22
...
@@ -8,6 +8,7 @@ import (
...
@@ -8,6 +8,7 @@ import (
"github.com/libp2p/go-libp2p-core/connmgr"
"github.com/libp2p/go-libp2p-core/connmgr"
"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/metrics"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p-peerstore/pstoreds"
"github.com/libp2p/go-libp2p-peerstore/pstoreds"
lconf
"github.com/libp2p/go-libp2p/config"
lconf
"github.com/libp2p/go-libp2p/config"
...
@@ -41,7 +42,7 @@ func (e *extraHost) ConnectionManager() connmgr.ConnManager {
...
@@ -41,7 +42,7 @@ func (e *extraHost) ConnectionManager() connmgr.ConnManager {
var
_
ExtraHostFeatures
=
(
*
extraHost
)(
nil
)
var
_
ExtraHostFeatures
=
(
*
extraHost
)(
nil
)
func
(
conf
*
Config
)
Host
(
log
log
.
Logger
)
(
host
.
Host
,
error
)
{
func
(
conf
*
Config
)
Host
(
log
log
.
Logger
,
reporter
metrics
.
Reporter
)
(
host
.
Host
,
error
)
{
if
conf
.
DisableP2P
{
if
conf
.
DisableP2P
{
return
nil
,
nil
return
nil
,
nil
}
}
...
@@ -115,7 +116,7 @@ func (conf *Config) Host(log log.Logger) (host.Host, error) {
...
@@ -115,7 +116,7 @@ func (conf *Config) Host(log log.Logger) (host.Host, error) {
ResourceManager
:
nil
,
// TODO use resource manager interface to manage resources per peer better.
ResourceManager
:
nil
,
// TODO use resource manager interface to manage resources per peer better.
NATManager
:
nat
,
NATManager
:
nat
,
Peerstore
:
ps
,
Peerstore
:
ps
,
Reporter
:
conf
.
BandwidthMetrics
,
// may be nil if disabled
Reporter
:
reporter
,
// may be nil if disabled
MultiaddrResolver
:
madns
.
DefaultResolver
,
MultiaddrResolver
:
madns
.
DefaultResolver
,
// Ping is a small built-in libp2p protocol that helps us check/debug latency between peers.
// Ping is a small built-in libp2p protocol that helps us check/debug latency between peers.
DisablePing
:
false
,
DisablePing
:
false
,
...
...
op-node/p2p/host_test.go
View file @
afba7c22
...
@@ -21,7 +21,6 @@ import (
...
@@ -21,7 +21,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/metrics"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/log"
...
@@ -65,10 +64,10 @@ func TestingConfig(t *testing.T) *Config {
...
@@ -65,10 +64,10 @@ func TestingConfig(t *testing.T) *Config {
func
TestP2PSimple
(
t
*
testing
.
T
)
{
func
TestP2PSimple
(
t
*
testing
.
T
)
{
confA
:=
TestingConfig
(
t
)
confA
:=
TestingConfig
(
t
)
confB
:=
TestingConfig
(
t
)
confB
:=
TestingConfig
(
t
)
hostA
,
err
:=
confA
.
Host
(
testlog
.
Logger
(
t
,
log
.
LvlError
)
.
New
(
"host"
,
"A"
))
hostA
,
err
:=
confA
.
Host
(
testlog
.
Logger
(
t
,
log
.
LvlError
)
.
New
(
"host"
,
"A"
)
,
nil
)
require
.
NoError
(
t
,
err
,
"failed to launch host A"
)
require
.
NoError
(
t
,
err
,
"failed to launch host A"
)
defer
hostA
.
Close
()
defer
hostA
.
Close
()
hostB
,
err
:=
confB
.
Host
(
testlog
.
Logger
(
t
,
log
.
LvlError
)
.
New
(
"host"
,
"B"
))
hostB
,
err
:=
confB
.
Host
(
testlog
.
Logger
(
t
,
log
.
LvlError
)
.
New
(
"host"
,
"B"
)
,
nil
)
require
.
NoError
(
t
,
err
,
"failed to launch host B"
)
require
.
NoError
(
t
,
err
,
"failed to launch host B"
)
defer
hostB
.
Close
()
defer
hostB
.
Close
()
err
=
hostA
.
Connect
(
context
.
Background
(),
peer
.
AddrInfo
{
ID
:
hostB
.
ID
(),
Addrs
:
hostB
.
Addrs
()})
err
=
hostA
.
Connect
(
context
.
Background
(),
peer
.
AddrInfo
{
ID
:
hostB
.
ID
(),
Addrs
:
hostB
.
Addrs
()})
...
@@ -132,7 +131,7 @@ func TestP2PFull(t *testing.T) {
...
@@ -132,7 +131,7 @@ func TestP2PFull(t *testing.T) {
// TODO: maybe swap the order of sec/mux preferences, to test that negotiation works
// TODO: maybe swap the order of sec/mux preferences, to test that negotiation works
logA
:=
testlog
.
Logger
(
t
,
log
.
LvlError
)
.
New
(
"host"
,
"A"
)
logA
:=
testlog
.
Logger
(
t
,
log
.
LvlError
)
.
New
(
"host"
,
"A"
)
nodeA
,
err
:=
NewNodeP2P
(
context
.
Background
(),
&
rollup
.
Config
{},
logA
,
&
confA
,
&
mockGossipIn
{})
nodeA
,
err
:=
NewNodeP2P
(
context
.
Background
(),
&
rollup
.
Config
{},
logA
,
&
confA
,
&
mockGossipIn
{}
,
nil
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
defer
nodeA
.
Close
()
defer
nodeA
.
Close
()
...
@@ -143,7 +142,7 @@ func TestP2PFull(t *testing.T) {
...
@@ -143,7 +142,7 @@ func TestP2PFull(t *testing.T) {
conns
<-
conn
conns
<-
conn
}})
}})
backend
:=
NewP2PAPIBackend
(
nodeA
,
logA
,
metrics
.
NewMetrics
(
""
)
)
backend
:=
NewP2PAPIBackend
(
nodeA
,
logA
,
nil
)
srv
:=
rpc
.
NewServer
()
srv
:=
rpc
.
NewServer
()
require
.
NoError
(
t
,
srv
.
RegisterName
(
"opp2p"
,
backend
))
require
.
NoError
(
t
,
srv
.
RegisterName
(
"opp2p"
,
backend
))
client
:=
rpc
.
DialInProc
(
srv
)
client
:=
rpc
.
DialInProc
(
srv
)
...
@@ -155,7 +154,7 @@ func TestP2PFull(t *testing.T) {
...
@@ -155,7 +154,7 @@ func TestP2PFull(t *testing.T) {
logB
:=
testlog
.
Logger
(
t
,
log
.
LvlError
)
.
New
(
"host"
,
"B"
)
logB
:=
testlog
.
Logger
(
t
,
log
.
LvlError
)
.
New
(
"host"
,
"B"
)
nodeB
,
err
:=
NewNodeP2P
(
context
.
Background
(),
&
rollup
.
Config
{},
logB
,
&
confB
,
&
mockGossipIn
{})
nodeB
,
err
:=
NewNodeP2P
(
context
.
Background
(),
&
rollup
.
Config
{},
logB
,
&
confB
,
&
mockGossipIn
{}
,
nil
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
defer
nodeB
.
Close
()
defer
nodeB
.
Close
()
hostB
:=
nodeB
.
Host
()
hostB
:=
nodeB
.
Host
()
...
@@ -289,7 +288,7 @@ func TestDiscovery(t *testing.T) {
...
@@ -289,7 +288,7 @@ func TestDiscovery(t *testing.T) {
resourcesCtx
,
resourcesCancel
:=
context
.
WithCancel
(
context
.
Background
())
resourcesCtx
,
resourcesCancel
:=
context
.
WithCancel
(
context
.
Background
())
defer
resourcesCancel
()
defer
resourcesCancel
()
nodeA
,
err
:=
NewNodeP2P
(
context
.
Background
(),
rollupCfg
,
logA
,
&
confA
,
&
mockGossipIn
{})
nodeA
,
err
:=
NewNodeP2P
(
context
.
Background
(),
rollupCfg
,
logA
,
&
confA
,
&
mockGossipIn
{}
,
nil
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
defer
nodeA
.
Close
()
defer
nodeA
.
Close
()
hostA
:=
nodeA
.
Host
()
hostA
:=
nodeA
.
Host
()
...
@@ -304,7 +303,7 @@ func TestDiscovery(t *testing.T) {
...
@@ -304,7 +303,7 @@ func TestDiscovery(t *testing.T) {
confB
.
DiscoveryDB
=
discDBC
confB
.
DiscoveryDB
=
discDBC
// Start B
// Start B
nodeB
,
err
:=
NewNodeP2P
(
context
.
Background
(),
rollupCfg
,
logB
,
&
confB
,
&
mockGossipIn
{})
nodeB
,
err
:=
NewNodeP2P
(
context
.
Background
(),
rollupCfg
,
logB
,
&
confB
,
&
mockGossipIn
{}
,
nil
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
defer
nodeB
.
Close
()
defer
nodeB
.
Close
()
hostB
:=
nodeB
.
Host
()
hostB
:=
nodeB
.
Host
()
...
@@ -319,7 +318,7 @@ func TestDiscovery(t *testing.T) {
...
@@ -319,7 +318,7 @@ func TestDiscovery(t *testing.T) {
}})
}})
// Start C
// Start C
nodeC
,
err
:=
NewNodeP2P
(
context
.
Background
(),
rollupCfg
,
logC
,
&
confC
,
&
mockGossipIn
{})
nodeC
,
err
:=
NewNodeP2P
(
context
.
Background
(),
rollupCfg
,
logC
,
&
confC
,
&
mockGossipIn
{}
,
nil
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
defer
nodeC
.
Close
()
defer
nodeC
.
Close
()
hostC
:=
nodeC
.
Host
()
hostC
:=
nodeC
.
Host
()
...
...
op-node/p2p/node.go
View file @
afba7c22
...
@@ -6,9 +6,11 @@ import (
...
@@ -6,9 +6,11 @@ import (
"fmt"
"fmt"
"strconv"
"strconv"
"github.com/ethereum-optimism/optimism/op-node/metrics"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-multierror"
"github.com/libp2p/go-libp2p-core/connmgr"
"github.com/libp2p/go-libp2p-core/connmgr"
"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/host"
p2pmetrics
"github.com/libp2p/go-libp2p-core/metrics"
pubsub
"github.com/libp2p/go-libp2p-pubsub"
pubsub
"github.com/libp2p/go-libp2p-pubsub"
"github.com/libp2p/go-libp2p/p2p/protocol/identify"
"github.com/libp2p/go-libp2p/p2p/protocol/identify"
ma
"github.com/multiformats/go-multiaddr"
ma
"github.com/multiformats/go-multiaddr"
...
@@ -30,12 +32,12 @@ type NodeP2P struct {
...
@@ -30,12 +32,12 @@ type NodeP2P struct {
gsOut
GossipOut
// p2p gossip application interface for publishing
gsOut
GossipOut
// p2p gossip application interface for publishing
}
}
func
NewNodeP2P
(
resourcesCtx
context
.
Context
,
rollupCfg
*
rollup
.
Config
,
log
log
.
Logger
,
setup
SetupP2P
,
gossipIn
GossipIn
)
(
*
NodeP2P
,
error
)
{
func
NewNodeP2P
(
resourcesCtx
context
.
Context
,
rollupCfg
*
rollup
.
Config
,
log
log
.
Logger
,
setup
SetupP2P
,
gossipIn
GossipIn
,
metrics
metrics
.
Metricer
)
(
*
NodeP2P
,
error
)
{
if
setup
==
nil
{
if
setup
==
nil
{
return
nil
,
errors
.
New
(
"p2p node cannot be created without setup"
)
return
nil
,
errors
.
New
(
"p2p node cannot be created without setup"
)
}
}
var
n
NodeP2P
var
n
NodeP2P
if
err
:=
n
.
init
(
resourcesCtx
,
rollupCfg
,
log
,
setup
,
gossipIn
);
err
!=
nil
{
if
err
:=
n
.
init
(
resourcesCtx
,
rollupCfg
,
log
,
setup
,
gossipIn
,
metrics
);
err
!=
nil
{
closeErr
:=
n
.
Close
()
closeErr
:=
n
.
Close
()
if
closeErr
!=
nil
{
if
closeErr
!=
nil
{
log
.
Error
(
"failed to close p2p after starting with err"
,
"closeErr"
,
closeErr
,
"err"
,
err
)
log
.
Error
(
"failed to close p2p after starting with err"
,
"closeErr"
,
closeErr
,
"err"
,
err
)
...
@@ -48,10 +50,12 @@ func NewNodeP2P(resourcesCtx context.Context, rollupCfg *rollup.Config, log log.
...
@@ -48,10 +50,12 @@ func NewNodeP2P(resourcesCtx context.Context, rollupCfg *rollup.Config, log log.
return
&
n
,
nil
return
&
n
,
nil
}
}
func
(
n
*
NodeP2P
)
init
(
resourcesCtx
context
.
Context
,
rollupCfg
*
rollup
.
Config
,
log
log
.
Logger
,
setup
SetupP2P
,
gossipIn
GossipIn
)
error
{
func
(
n
*
NodeP2P
)
init
(
resourcesCtx
context
.
Context
,
rollupCfg
*
rollup
.
Config
,
log
log
.
Logger
,
setup
SetupP2P
,
gossipIn
GossipIn
,
metrics
metrics
.
Metricer
)
error
{
bwc
:=
p2pmetrics
.
NewBandwidthCounter
()
var
err
error
var
err
error
// nil if disabled.
// nil if disabled.
n
.
host
,
err
=
setup
.
Host
(
log
)
n
.
host
,
err
=
setup
.
Host
(
log
,
bwc
)
if
err
!=
nil
{
if
err
!=
nil
{
if
n
.
dv5Udp
!=
nil
{
if
n
.
dv5Udp
!=
nil
{
n
.
dv5Udp
.
Close
()
n
.
dv5Udp
.
Close
()
...
@@ -66,10 +70,10 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l
...
@@ -66,10 +70,10 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l
n
.
connMgr
=
extra
.
ConnectionManager
()
n
.
connMgr
=
extra
.
ConnectionManager
()
}
}
// notify of any new connections/streams/etc.
// notify of any new connections/streams/etc.
n
.
host
.
Network
()
.
Notify
(
NewNetworkNotifier
(
log
))
n
.
host
.
Network
()
.
Notify
(
NewNetworkNotifier
(
log
,
metrics
))
// unregister identify-push handler. Only identifying on dial is fine, and more robust against spam
// unregister identify-push handler. Only identifying on dial is fine, and more robust against spam
n
.
host
.
RemoveStreamHandler
(
identify
.
IDDelta
)
n
.
host
.
RemoveStreamHandler
(
identify
.
IDDelta
)
n
.
gs
,
err
=
NewGossipSub
(
resourcesCtx
,
n
.
host
,
rollupCfg
)
n
.
gs
,
err
=
NewGossipSub
(
resourcesCtx
,
n
.
host
,
rollupCfg
,
metrics
)
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
)
}
}
...
@@ -90,6 +94,10 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l
...
@@ -90,6 +94,10 @@ func (n *NodeP2P) init(resourcesCtx context.Context, rollupCfg *rollup.Config, l
if
err
!=
nil
{
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to start discv5: %w"
,
err
)
return
fmt
.
Errorf
(
"failed to start discv5: %w"
,
err
)
}
}
if
metrics
!=
nil
{
go
metrics
.
RecordBandwidth
(
resourcesCtx
,
bwc
)
}
}
}
return
nil
return
nil
}
}
...
...
op-node/p2p/notifications.go
View file @
afba7c22
package
p2p
package
p2p
import
(
import
(
"github.com/ethereum-optimism/optimism/op-node/metrics"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/network"
ma
"github.com/multiformats/go-multiaddr"
ma
"github.com/multiformats/go-multiaddr"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/log"
)
)
// TODO: add metrics here as well
type
NotificationsMetricer
interface
{
IncPeerCount
()
DecPeerCount
()
IncStreamCount
()
DecStreamCount
()
}
type
notifications
struct
{
type
notifications
struct
{
log
log
.
Logger
log
log
.
Logger
m
NotificationsMetricer
}
}
func
(
notif
*
notifications
)
Listen
(
n
network
.
Network
,
a
ma
.
Multiaddr
)
{
func
(
notif
*
notifications
)
Listen
(
n
network
.
Network
,
a
ma
.
Multiaddr
)
{
...
@@ -20,20 +27,27 @@ func (notif *notifications) ListenClose(n network.Network, a ma.Multiaddr) {
...
@@ -20,20 +27,27 @@ func (notif *notifications) ListenClose(n network.Network, a ma.Multiaddr) {
notif
.
log
.
Info
(
"stopped listening network address"
,
"addr"
,
a
)
notif
.
log
.
Info
(
"stopped listening network address"
,
"addr"
,
a
)
}
}
func
(
notif
*
notifications
)
Connected
(
n
network
.
Network
,
v
network
.
Conn
)
{
func
(
notif
*
notifications
)
Connected
(
n
network
.
Network
,
v
network
.
Conn
)
{
notif
.
m
.
IncPeerCount
()
notif
.
log
.
Info
(
"connected to peer"
,
"peer"
,
v
.
RemotePeer
(),
"addr"
,
v
.
RemoteMultiaddr
())
notif
.
log
.
Info
(
"connected to peer"
,
"peer"
,
v
.
RemotePeer
(),
"addr"
,
v
.
RemoteMultiaddr
())
}
}
func
(
notif
*
notifications
)
Disconnected
(
n
network
.
Network
,
v
network
.
Conn
)
{
func
(
notif
*
notifications
)
Disconnected
(
n
network
.
Network
,
v
network
.
Conn
)
{
notif
.
m
.
DecPeerCount
()
notif
.
log
.
Info
(
"disconnected from peer"
,
"peer"
,
v
.
RemotePeer
(),
"addr"
,
v
.
RemoteMultiaddr
())
notif
.
log
.
Info
(
"disconnected from peer"
,
"peer"
,
v
.
RemotePeer
(),
"addr"
,
v
.
RemoteMultiaddr
())
}
}
func
(
notif
*
notifications
)
OpenedStream
(
n
network
.
Network
,
v
network
.
Stream
)
{
func
(
notif
*
notifications
)
OpenedStream
(
n
network
.
Network
,
v
network
.
Stream
)
{
notif
.
m
.
IncStreamCount
()
c
:=
v
.
Conn
()
c
:=
v
.
Conn
()
notif
.
log
.
Trace
(
"opened stream"
,
"protocol"
,
v
.
Protocol
(),
"peer"
,
c
.
RemotePeer
(),
"addr"
,
c
.
RemoteMultiaddr
())
notif
.
log
.
Trace
(
"opened stream"
,
"protocol"
,
v
.
Protocol
(),
"peer"
,
c
.
RemotePeer
(),
"addr"
,
c
.
RemoteMultiaddr
())
}
}
func
(
notif
*
notifications
)
ClosedStream
(
n
network
.
Network
,
v
network
.
Stream
)
{
func
(
notif
*
notifications
)
ClosedStream
(
n
network
.
Network
,
v
network
.
Stream
)
{
notif
.
m
.
DecStreamCount
()
c
:=
v
.
Conn
()
c
:=
v
.
Conn
()
notif
.
log
.
Trace
(
"opened stream"
,
"protocol"
,
v
.
Protocol
(),
"peer"
,
c
.
RemotePeer
(),
"addr"
,
c
.
RemoteMultiaddr
())
notif
.
log
.
Trace
(
"opened stream"
,
"protocol"
,
v
.
Protocol
(),
"peer"
,
c
.
RemotePeer
(),
"addr"
,
c
.
RemoteMultiaddr
())
}
}
func
NewNetworkNotifier
(
log
log
.
Logger
)
network
.
Notifiee
{
func
NewNetworkNotifier
(
log
log
.
Logger
,
m
metrics
.
Metricer
)
network
.
Notifiee
{
return
&
notifications
{
log
:
log
}
if
m
==
nil
{
m
=
metrics
.
NoopMetrics
}
return
&
notifications
{
log
:
log
,
m
:
m
}
}
}
op-node/p2p/prepared.go
View file @
afba7c22
...
@@ -5,6 +5,7 @@ import (
...
@@ -5,6 +5,7 @@ import (
"fmt"
"fmt"
"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/metrics"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/log"
...
@@ -38,7 +39,7 @@ func (p *Prepared) Check() error {
...
@@ -38,7 +39,7 @@ func (p *Prepared) Check() error {
}
}
// Host creates a libp2p host service. Returns nil, nil if p2p is disabled.
// Host creates a libp2p host service. Returns nil, nil if p2p is disabled.
func
(
p
*
Prepared
)
Host
(
log
log
.
Logger
)
(
host
.
Host
,
error
)
{
func
(
p
*
Prepared
)
Host
(
log
log
.
Logger
,
reporter
metrics
.
Reporter
)
(
host
.
Host
,
error
)
{
return
p
.
HostP2P
,
nil
return
p
.
HostP2P
,
nil
}
}
...
...
op-node/p2p/rpc_server.go
View file @
afba7c22
...
@@ -54,12 +54,16 @@ type Node interface {
...
@@ -54,12 +54,16 @@ type Node interface {
type
APIBackend
struct
{
type
APIBackend
struct
{
node
Node
node
Node
log
log
.
Logger
log
log
.
Logger
m
*
metrics
.
Metrics
m
metrics
.
Metricer
}
}
var
_
API
=
(
*
APIBackend
)(
nil
)
var
_
API
=
(
*
APIBackend
)(
nil
)
func
NewP2PAPIBackend
(
node
Node
,
log
log
.
Logger
,
m
*
metrics
.
Metrics
)
*
APIBackend
{
func
NewP2PAPIBackend
(
node
Node
,
log
log
.
Logger
,
m
metrics
.
Metricer
)
*
APIBackend
{
if
m
==
nil
{
m
=
metrics
.
NoopMetrics
}
return
&
APIBackend
{
return
&
APIBackend
{
node
:
node
,
node
:
node
,
log
:
log
,
log
:
log
,
...
...
op-node/rollup/derive/channel.go
View file @
afba7c22
...
@@ -138,7 +138,7 @@ func BatchReader(r io.Reader, l1InclusionBlock eth.L1BlockRef) (func() (BatchWit
...
@@ -138,7 +138,7 @@ func BatchReader(r io.Reader, l1InclusionBlock eth.L1BlockRef) (func() (BatchWit
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
rlpReader
:=
rlp
.
NewStream
(
zr
,
10
_000_000
)
rlpReader
:=
rlp
.
NewStream
(
zr
,
MaxRLPBytesPerChannel
)
// Read each batch iteratively
// Read each batch iteratively
return
func
()
(
BatchWithL1InclusionBlock
,
error
)
{
return
func
()
(
BatchWithL1InclusionBlock
,
error
)
{
ret
:=
BatchWithL1InclusionBlock
{
ret
:=
BatchWithL1InclusionBlock
{
...
...
op-node/rollup/derive/channel_out.go
View file @
afba7c22
...
@@ -5,6 +5,7 @@ import (
...
@@ -5,6 +5,7 @@ import (
"compress/zlib"
"compress/zlib"
"crypto/rand"
"crypto/rand"
"errors"
"errors"
"fmt"
"io"
"io"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup"
...
@@ -14,15 +15,14 @@ import (
...
@@ -14,15 +15,14 @@ import (
)
)
var
ErrNotDepositTx
=
errors
.
New
(
"first transaction in block is not a deposit tx"
)
var
ErrNotDepositTx
=
errors
.
New
(
"first transaction in block is not a deposit tx"
)
var
ErrTooManyRLPBytes
=
errors
.
New
(
"batch would cause RLP bytes to go over limit"
)
type
ChannelOut
struct
{
type
ChannelOut
struct
{
id
ChannelID
id
ChannelID
// Frame ID of the next frame to emit. Increment after emitting
// Frame ID of the next frame to emit. Increment after emitting
frame
uint64
frame
uint64
// How much we've pulled from the reader so far
// rlpLength is the uncompressed size of the channel. Must be less than MAX_RLP_BYTES_PER_CHANNEL
offset
uint64
rlpLength
int
// scratch for temporary buffering
scratch
bytes
.
Buffer
// Compressor stage. Write input data to it
// Compressor stage. Write input data to it
compress
*
zlib
.
Writer
compress
*
zlib
.
Writer
...
@@ -38,9 +38,9 @@ func (co *ChannelOut) ID() ChannelID {
...
@@ -38,9 +38,9 @@ func (co *ChannelOut) ID() ChannelID {
func
NewChannelOut
()
(
*
ChannelOut
,
error
)
{
func
NewChannelOut
()
(
*
ChannelOut
,
error
)
{
c
:=
&
ChannelOut
{
c
:=
&
ChannelOut
{
id
:
ChannelID
{},
// TODO: use GUID here instead of fully random data
id
:
ChannelID
{},
// TODO: use GUID here instead of fully random data
frame
:
0
,
frame
:
0
,
offset
:
0
,
rlpLength
:
0
,
}
}
_
,
err
:=
rand
.
Read
(
c
.
id
[
:
])
_
,
err
:=
rand
.
Read
(
c
.
id
[
:
])
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -59,9 +59,8 @@ func NewChannelOut() (*ChannelOut, error) {
...
@@ -59,9 +59,8 @@ func NewChannelOut() (*ChannelOut, error) {
// TODO: reuse ChannelOut for performance
// TODO: reuse ChannelOut for performance
func
(
co
*
ChannelOut
)
Reset
()
error
{
func
(
co
*
ChannelOut
)
Reset
()
error
{
co
.
frame
=
0
co
.
frame
=
0
co
.
offset
=
0
co
.
rlpLength
=
0
co
.
buf
.
Reset
()
co
.
buf
.
Reset
()
co
.
scratch
.
Reset
()
co
.
compress
.
Reset
(
&
co
.
buf
)
co
.
compress
.
Reset
(
&
co
.
buf
)
co
.
closed
=
false
co
.
closed
=
false
_
,
err
:=
rand
.
Read
(
co
.
id
[
:
])
_
,
err
:=
rand
.
Read
(
co
.
id
[
:
])
...
@@ -71,11 +70,33 @@ func (co *ChannelOut) Reset() error {
...
@@ -71,11 +70,33 @@ func (co *ChannelOut) Reset() error {
return
nil
return
nil
}
}
// AddBlock adds a block to the channel. It returns an error
// if there is a problem adding the block. The only sentinel
// error that it returns is ErrTooManyRLPBytes. If this error
// is returned, the channel should be closed and a new one
// should be made.
func
(
co
*
ChannelOut
)
AddBlock
(
block
*
types
.
Block
)
error
{
func
(
co
*
ChannelOut
)
AddBlock
(
block
*
types
.
Block
)
error
{
if
co
.
closed
{
if
co
.
closed
{
return
errors
.
New
(
"already closed"
)
return
errors
.
New
(
"already closed"
)
}
}
return
blockToBatch
(
block
,
co
.
compress
)
batch
,
err
:=
blockToBatch
(
block
)
if
err
!=
nil
{
return
err
}
// We encode to a temporary buffer to determine the encoded length to
// ensure that the total size of all RLP elements is less than MAX_RLP_BYTES_PER_CHANNEL
var
buf
bytes
.
Buffer
if
err
:=
rlp
.
Encode
(
&
buf
,
batch
);
err
!=
nil
{
return
err
}
if
co
.
rlpLength
+
buf
.
Len
()
>
MaxRLPBytesPerChannel
{
return
fmt
.
Errorf
(
"could not add %d bytes to channel of %d bytes, max is %d. err: %w"
,
buf
.
Len
(),
co
.
rlpLength
,
MaxRLPBytesPerChannel
,
ErrTooManyRLPBytes
)
}
co
.
rlpLength
+=
buf
.
Len
()
_
,
err
=
io
.
Copy
(
co
.
compress
,
&
buf
)
return
err
}
}
// ReadyBytes returns the number of bytes that the channel out can immediately output into a frame.
// ReadyBytes returns the number of bytes that the channel out can immediately output into a frame.
...
@@ -141,35 +162,35 @@ func (co *ChannelOut) OutputFrame(w *bytes.Buffer, maxSize uint64) error {
...
@@ -141,35 +162,35 @@ func (co *ChannelOut) OutputFrame(w *bytes.Buffer, maxSize uint64) error {
}
}
}
}
// blockToBatch
writes the raw block bytes (after batch encoding) to the writer
// blockToBatch
transforms a block into a batch object that can easily be RLP encoded.
func
blockToBatch
(
block
*
types
.
Block
,
w
io
.
Writer
)
error
{
func
blockToBatch
(
block
*
types
.
Block
)
(
*
BatchData
,
error
)
{
var
opaqueTxs
[]
hexutil
.
Bytes
var
opaqueTxs
[]
hexutil
.
Bytes
for
_
,
tx
:=
range
block
.
Transactions
()
{
for
i
,
tx
:=
range
block
.
Transactions
()
{
if
tx
.
Type
()
==
types
.
DepositTxType
{
if
tx
.
Type
()
==
types
.
DepositTxType
{
continue
continue
}
}
otx
,
err
:=
tx
.
MarshalBinary
()
otx
,
err
:=
tx
.
MarshalBinary
()
if
err
!=
nil
{
if
err
!=
nil
{
return
err
// TODO: wrap err
return
nil
,
fmt
.
Errorf
(
"could not encode tx %v in block %v: %w"
,
i
,
tx
.
Hash
(),
err
)
}
}
opaqueTxs
=
append
(
opaqueTxs
,
otx
)
opaqueTxs
=
append
(
opaqueTxs
,
otx
)
}
}
l1InfoTx
:=
block
.
Transactions
()[
0
]
l1InfoTx
:=
block
.
Transactions
()[
0
]
if
l1InfoTx
.
Type
()
!=
types
.
DepositTxType
{
if
l1InfoTx
.
Type
()
!=
types
.
DepositTxType
{
return
ErrNotDepositTx
return
nil
,
ErrNotDepositTx
}
}
l1Info
,
err
:=
L1InfoDepositTxData
(
l1InfoTx
.
Data
())
l1Info
,
err
:=
L1InfoDepositTxData
(
l1InfoTx
.
Data
())
if
err
!=
nil
{
if
err
!=
nil
{
return
err
// TODO: wrap err
return
nil
,
fmt
.
Errorf
(
"could not parse the L1 Info deposit: %w"
,
err
)
}
}
batch
:=
&
BatchData
{
BatchV1
{
return
&
BatchData
{
ParentHash
:
block
.
ParentHash
(),
BatchV1
{
EpochNum
:
rollup
.
Epoch
(
l1Info
.
Number
),
ParentHash
:
block
.
ParentHash
(
),
EpochHash
:
l1Info
.
BlockHash
,
EpochNum
:
rollup
.
Epoch
(
l1Info
.
Number
)
,
Timestamp
:
block
.
Time
()
,
EpochHash
:
l1Info
.
BlockHash
,
Transactions
:
opaqueTxs
,
Timestamp
:
block
.
Time
()
,
}
,
Transactions
:
opaqueTxs
,
}
},
return
rlp
.
Encode
(
w
,
batch
)
},
nil
}
}
op-node/rollup/derive/params.go
View file @
afba7c22
...
@@ -15,6 +15,10 @@ const DerivationVersion0 = 0
...
@@ -15,6 +15,10 @@ const DerivationVersion0 = 0
// starting with the oldest channel.
// starting with the oldest channel.
const
MaxChannelBankSize
=
100
_000_000
const
MaxChannelBankSize
=
100
_000_000
// MaxRLPBytesPerChannel is the maximum amount of bytes that will be read from
// a channel. This limit is set when decoding the RLP.
const
MaxRLPBytesPerChannel
=
10
_000_000
// DuplicateErr is returned when a newly read frame is already known
// DuplicateErr is returned when a newly read frame is already known
var
DuplicateErr
=
errors
.
New
(
"duplicate frame"
)
var
DuplicateErr
=
errors
.
New
(
"duplicate frame"
)
...
...
op-node/rollup/driver/conf_depth.go
View file @
afba7c22
...
@@ -6,6 +6,7 @@ import (
...
@@ -6,6 +6,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
)
)
// confDepth is an util that wraps the L1 input fetcher used in the pipeline,
// confDepth is an util that wraps the L1 input fetcher used in the pipeline,
...
@@ -30,7 +31,9 @@ func (c *confDepth) L1BlockRefByNumber(ctx context.Context, num uint64) (eth.L1B
...
@@ -30,7 +31,9 @@ func (c *confDepth) L1BlockRefByNumber(ctx context.Context, num uint64) (eth.L1B
// TODO: performance optimization: buffer the l1Unsafe, invalidate any reorged previous buffer content,
// TODO: performance optimization: buffer the l1Unsafe, invalidate any reorged previous buffer content,
// and instantly return the origin by number from the buffer if we can.
// and instantly return the origin by number from the buffer if we can.
if
num
==
0
||
c
.
depth
==
0
||
num
+
c
.
depth
<=
c
.
l1Head
()
.
Number
{
// Don't apply the conf depth is l1Head is empty (as it is during the startup case before the l1State is initialized).
l1Head
:=
c
.
l1Head
()
if
num
==
0
||
c
.
depth
==
0
||
num
+
c
.
depth
<=
l1Head
.
Number
||
l1Head
.
Hash
==
(
common
.
Hash
{})
{
return
c
.
L1Fetcher
.
L1BlockRefByNumber
(
ctx
,
num
)
return
c
.
L1Fetcher
.
L1BlockRefByNumber
(
ctx
,
num
)
}
}
return
eth
.
L1BlockRef
{},
ethereum
.
NotFound
return
eth
.
L1BlockRef
{},
ethereum
.
NotFound
...
...
op-node/rollup/driver/conf_depth_test.go
View file @
afba7c22
...
@@ -9,11 +9,15 @@ import (
...
@@ -9,11 +9,15 @@ import (
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
)
)
var
exHash
=
common
.
Hash
{
0xff
}
type
confTest
struct
{
type
confTest
struct
{
name
string
name
string
head
uint64
head
uint64
hash
common
.
Hash
// hash of head block
req
uint64
req
uint64
depth
uint64
depth
uint64
pass
bool
pass
bool
...
@@ -21,7 +25,7 @@ type confTest struct {
...
@@ -21,7 +25,7 @@ type confTest struct {
func
(
ct
*
confTest
)
Run
(
t
*
testing
.
T
)
{
func
(
ct
*
confTest
)
Run
(
t
*
testing
.
T
)
{
l1Fetcher
:=
&
testutils
.
MockL1Source
{}
l1Fetcher
:=
&
testutils
.
MockL1Source
{}
l1Head
:=
eth
.
L1BlockRef
{
Number
:
ct
.
head
}
l1Head
:=
eth
.
L1BlockRef
{
Number
:
ct
.
head
,
Hash
:
ct
.
hash
}
l1HeadGetter
:=
func
()
eth
.
L1BlockRef
{
return
l1Head
}
l1HeadGetter
:=
func
()
eth
.
L1BlockRef
{
return
l1Head
}
cd
:=
NewConfDepth
(
ct
.
depth
,
l1HeadGetter
,
l1Fetcher
)
cd
:=
NewConfDepth
(
ct
.
depth
,
l1HeadGetter
,
l1Fetcher
)
...
@@ -43,18 +47,19 @@ func TestConfDepth(t *testing.T) {
...
@@ -43,18 +47,19 @@ func TestConfDepth(t *testing.T) {
// note: we're not testing overflows.
// note: we're not testing overflows.
// If a request is large enough to overflow the conf depth check, it's not returning anything anyway.
// If a request is large enough to overflow the conf depth check, it's not returning anything anyway.
testCases
:=
[]
confTest
{
testCases
:=
[]
confTest
{
{
name
:
"zero conf future"
,
head
:
4
,
req
:
5
,
depth
:
0
,
pass
:
true
},
{
name
:
"zero conf future"
,
head
:
4
,
hash
:
exHash
,
req
:
5
,
depth
:
0
,
pass
:
true
},
{
name
:
"zero conf present"
,
head
:
4
,
req
:
4
,
depth
:
0
,
pass
:
true
},
{
name
:
"zero conf present"
,
head
:
4
,
hash
:
exHash
,
req
:
4
,
depth
:
0
,
pass
:
true
},
{
name
:
"zero conf past"
,
head
:
4
,
req
:
4
,
depth
:
0
,
pass
:
true
},
{
name
:
"zero conf past"
,
head
:
4
,
hash
:
exHash
,
req
:
4
,
depth
:
0
,
pass
:
true
},
{
name
:
"one conf future"
,
head
:
4
,
req
:
5
,
depth
:
1
,
pass
:
false
},
{
name
:
"one conf future"
,
head
:
4
,
hash
:
exHash
,
req
:
5
,
depth
:
1
,
pass
:
false
},
{
name
:
"one conf present"
,
head
:
4
,
req
:
4
,
depth
:
1
,
pass
:
false
},
{
name
:
"one conf present"
,
head
:
4
,
hash
:
exHash
,
req
:
4
,
depth
:
1
,
pass
:
false
},
{
name
:
"one conf past"
,
head
:
4
,
req
:
3
,
depth
:
1
,
pass
:
true
},
{
name
:
"one conf past"
,
head
:
4
,
hash
:
exHash
,
req
:
3
,
depth
:
1
,
pass
:
true
},
{
name
:
"two conf future"
,
head
:
4
,
req
:
5
,
depth
:
2
,
pass
:
false
},
{
name
:
"two conf future"
,
head
:
4
,
hash
:
exHash
,
req
:
5
,
depth
:
2
,
pass
:
false
},
{
name
:
"two conf present"
,
head
:
4
,
req
:
4
,
depth
:
2
,
pass
:
false
},
{
name
:
"two conf present"
,
head
:
4
,
hash
:
exHash
,
req
:
4
,
depth
:
2
,
pass
:
false
},
{
name
:
"two conf not like 1"
,
head
:
4
,
req
:
3
,
depth
:
2
,
pass
:
false
},
{
name
:
"two conf not like 1"
,
head
:
4
,
hash
:
exHash
,
req
:
3
,
depth
:
2
,
pass
:
false
},
{
name
:
"two conf pass"
,
head
:
4
,
req
:
2
,
depth
:
2
,
pass
:
true
},
{
name
:
"two conf pass"
,
head
:
4
,
hash
:
exHash
,
req
:
2
,
depth
:
2
,
pass
:
true
},
{
name
:
"easy pass"
,
head
:
100
,
req
:
20
,
depth
:
5
,
pass
:
true
},
{
name
:
"easy pass"
,
head
:
100
,
hash
:
exHash
,
req
:
20
,
depth
:
5
,
pass
:
true
},
{
name
:
"genesis case"
,
head
:
0
,
req
:
0
,
depth
:
4
,
pass
:
true
},
{
name
:
"genesis case"
,
head
:
0
,
hash
:
exHash
,
req
:
0
,
depth
:
4
,
pass
:
true
},
{
name
:
"no L1 state"
,
req
:
10
,
depth
:
4
,
pass
:
true
},
}
}
for
_
,
tc
:=
range
testCases
{
for
_
,
tc
:=
range
testCases
{
t
.
Run
(
tc
.
name
,
tc
.
Run
)
t
.
Run
(
tc
.
name
,
tc
.
Run
)
...
...
packages/actor-tests/CHANGELOG.md
View file @
afba7c22
# @eth-optimism/actor-tests
# @eth-optimism/actor-tests
## 0.0.10
### Patch Changes
-
Updated dependencies
[
7d7d9ba8
]
-
@eth-optimism/contracts-bedrock@0.8.2
-
@eth-optimism/sdk@1.6.8
## 0.0.9
## 0.0.9
### Patch Changes
### Patch Changes
...
...
packages/actor-tests/package.json
View file @
afba7c22
{
{
"name"
:
"@eth-optimism/actor-tests"
,
"name"
:
"@eth-optimism/actor-tests"
,
"version"
:
"0.0.
9
"
,
"version"
:
"0.0.
10
"
,
"description"
:
"A library and suite of tests to stress test Optimism Bedrock."
,
"description"
:
"A library and suite of tests to stress test Optimism Bedrock."
,
"license"
:
"MIT"
,
"license"
:
"MIT"
,
"author"
:
""
,
"author"
:
""
,
...
@@ -18,9 +18,9 @@
...
@@ -18,9 +18,9 @@
"test:coverage"
:
"yarn test"
"test:coverage"
:
"yarn test"
},
},
"dependencies"
:
{
"dependencies"
:
{
"@eth-optimism/contracts-bedrock"
:
"0.8.
1
"
,
"@eth-optimism/contracts-bedrock"
:
"0.8.
2
"
,
"@eth-optimism/core-utils"
:
"^0.10.1"
,
"@eth-optimism/core-utils"
:
"^0.10.1"
,
"@eth-optimism/sdk"
:
"^1.6.
7
"
,
"@eth-optimism/sdk"
:
"^1.6.
8
"
,
"@types/chai"
:
"^4.2.18"
,
"@types/chai"
:
"^4.2.18"
,
"@types/chai-as-promised"
:
"^7.1.4"
,
"@types/chai-as-promised"
:
"^7.1.4"
,
"async-mutex"
:
"^0.3.2"
,
"async-mutex"
:
"^0.3.2"
,
...
...
packages/contracts-bedrock/CHANGELOG.md
View file @
afba7c22
# @eth-optimism/contracts-bedrock
# @eth-optimism/contracts-bedrock
## 0.8.2
### Patch Changes
-
7d7d9ba8: Moves initializers underneath constructors always
## 0.8.1
## 0.8.1
### Patch Changes
### Patch Changes
...
...
packages/contracts-bedrock/contracts/L1/OptimismPortal.sol
View file @
afba7c22
...
@@ -99,6 +99,14 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
...
@@ -99,6 +99,14 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
initialize();
initialize();
}
}
/**
* @notice Initializer;
*/
function initialize() public initializer {
l2Sender = DEFAULT_L2_SENDER;
__ResourceMetering_init();
}
/**
/**
* @notice Accepts value so that users can send ETH directly to this contract and have the
* @notice Accepts value so that users can send ETH directly to this contract and have the
* funds be deposited to their address on L2. This is intended as a convenience
* funds be deposited to their address on L2. This is intended as a convenience
...
@@ -214,14 +222,6 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
...
@@ -214,14 +222,6 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
return _isOutputFinalized(proposal);
return _isOutputFinalized(proposal);
}
}
/**
* @notice Initializer;
*/
function initialize() public initializer {
l2Sender = DEFAULT_L2_SENDER;
__ResourceMetering_init();
}
/**
/**
* @notice Accepts deposits of ETH and data, and emits a TransactionDeposited event for use in
* @notice Accepts deposits of ETH and data, and emits a TransactionDeposited event for use in
* deriving deposit transactions. Note that if a deposit is made by a contract, its
* deriving deposit transactions. Note that if a deposit is made by a contract, its
...
...
packages/contracts-bedrock/deploy/001-InitImplementations.deploy.ts
View file @
afba7c22
...
@@ -7,7 +7,7 @@ import '@eth-optimism/hardhat-deploy-config'
...
@@ -7,7 +7,7 @@ import '@eth-optimism/hardhat-deploy-config'
const
upgradeABIs
=
{
const
upgradeABIs
=
{
L2OutputOracleProxy
:
async
(
deployConfig
)
=>
[
L2OutputOracleProxy
:
async
(
deployConfig
)
=>
[
'
initialize(bytes32,
uint256
,address)
'
,
'
initialize(bytes32,
address
,address)
'
,
[
[
deployConfig
.
l2OutputOracleGenesisL2Output
,
deployConfig
.
l2OutputOracleGenesisL2Output
,
deployConfig
.
l2OutputOracleProposer
,
deployConfig
.
l2OutputOracleProposer
,
...
...
packages/contracts-bedrock/package.json
View file @
afba7c22
{
{
"name"
:
"@eth-optimism/contracts-bedrock"
,
"name"
:
"@eth-optimism/contracts-bedrock"
,
"version"
:
"0.8.
1
"
,
"version"
:
"0.8.
2
"
,
"description"
:
"Contracts for Optimism Specs"
,
"description"
:
"Contracts for Optimism Specs"
,
"main"
:
"dist/index"
,
"main"
:
"dist/index"
,
"types"
:
"dist/index"
,
"types"
:
"dist/index"
,
...
...
packages/drippie-mon/CHANGELOG.md
View file @
afba7c22
# @eth-optimism/drippie-mon
# @eth-optimism/drippie-mon
## 0.3.19
### Patch Changes
-
@eth-optimism/sdk@1.6.8
## 0.3.18
## 0.3.18
### Patch Changes
### Patch Changes
...
...
packages/drippie-mon/package.json
View file @
afba7c22
{
{
"private"
:
true
,
"private"
:
true
,
"name"
:
"@eth-optimism/drippie-mon"
,
"name"
:
"@eth-optimism/drippie-mon"
,
"version"
:
"0.3.1
8
"
,
"version"
:
"0.3.1
9
"
,
"description"
:
"[Optimism] Service for monitoring Drippie instances"
,
"description"
:
"[Optimism] Service for monitoring Drippie instances"
,
"main"
:
"dist/index"
,
"main"
:
"dist/index"
,
"types"
:
"dist/index"
,
"types"
:
"dist/index"
,
...
@@ -35,7 +35,7 @@
...
@@ -35,7 +35,7 @@
"@eth-optimism/common-ts"
:
"0.6.6"
,
"@eth-optimism/common-ts"
:
"0.6.6"
,
"@eth-optimism/contracts-periphery"
:
"1.0.2"
,
"@eth-optimism/contracts-periphery"
:
"1.0.2"
,
"@eth-optimism/core-utils"
:
"0.10.1"
,
"@eth-optimism/core-utils"
:
"0.10.1"
,
"@eth-optimism/sdk"
:
"1.6.
7
"
,
"@eth-optimism/sdk"
:
"1.6.
8
"
,
"ethers"
:
"^5.7.0"
"ethers"
:
"^5.7.0"
},
},
"devDependencies"
:
{
"devDependencies"
:
{
...
...
packages/integration-tests-bedrock/package.json
View file @
afba7c22
...
@@ -29,7 +29,7 @@
...
@@ -29,7 +29,7 @@
"devDependencies"
:
{
"devDependencies"
:
{
"@eth-optimism/contracts"
:
"0.5.37"
,
"@eth-optimism/contracts"
:
"0.5.37"
,
"@eth-optimism/core-utils"
:
"0.10.1"
,
"@eth-optimism/core-utils"
:
"0.10.1"
,
"@eth-optimism/sdk"
:
"1.6.
7
"
,
"@eth-optimism/sdk"
:
"1.6.
8
"
,
"@ethersproject/abstract-provider"
:
"^5.7.0"
,
"@ethersproject/abstract-provider"
:
"^5.7.0"
,
"chai-as-promised"
:
"^7.1.1"
,
"chai-as-promised"
:
"^7.1.1"
,
"chai"
:
"^4.3.4"
,
"chai"
:
"^4.3.4"
,
...
...
packages/message-relayer/CHANGELOG.md
View file @
afba7c22
# @eth-optimism/message-relayer
# @eth-optimism/message-relayer
## 0.5.18
### Patch Changes
-
@eth-optimism/sdk@1.6.8
## 0.5.17
## 0.5.17
### Patch Changes
### Patch Changes
...
...
packages/message-relayer/package.json
View file @
afba7c22
{
{
"private"
:
true
,
"private"
:
true
,
"name"
:
"@eth-optimism/message-relayer"
,
"name"
:
"@eth-optimism/message-relayer"
,
"version"
:
"0.5.1
7
"
,
"version"
:
"0.5.1
8
"
,
"description"
:
"[Optimism] Service for automatically relaying L2 to L1 transactions"
,
"description"
:
"[Optimism] Service for automatically relaying L2 to L1 transactions"
,
"main"
:
"dist/index"
,
"main"
:
"dist/index"
,
"types"
:
"dist/index"
,
"types"
:
"dist/index"
,
...
@@ -33,7 +33,7 @@
...
@@ -33,7 +33,7 @@
"dependencies"
:
{
"dependencies"
:
{
"@eth-optimism/common-ts"
:
"0.6.6"
,
"@eth-optimism/common-ts"
:
"0.6.6"
,
"@eth-optimism/core-utils"
:
"0.10.1"
,
"@eth-optimism/core-utils"
:
"0.10.1"
,
"@eth-optimism/sdk"
:
"1.6.
7
"
,
"@eth-optimism/sdk"
:
"1.6.
8
"
,
"ethers"
:
"^5.7.0"
"ethers"
:
"^5.7.0"
},
},
"devDependencies"
:
{
"devDependencies"
:
{
...
...
packages/sdk/CHANGELOG.md
View file @
afba7c22
# @eth-optimism/sdk
# @eth-optimism/sdk
## 1.6.8
### Patch Changes
-
Updated dependencies
[
7d7d9ba8
]
-
@eth-optimism/contracts-bedrock@0.8.2
## 1.6.7
## 1.6.7
### Patch Changes
### Patch Changes
...
...
packages/sdk/package.json
View file @
afba7c22
{
{
"name"
:
"@eth-optimism/sdk"
,
"name"
:
"@eth-optimism/sdk"
,
"version"
:
"1.6.
7
"
,
"version"
:
"1.6.
8
"
,
"description"
:
"[Optimism] Tools for working with Optimism"
,
"description"
:
"[Optimism] Tools for working with Optimism"
,
"main"
:
"dist/index"
,
"main"
:
"dist/index"
,
"types"
:
"dist/index"
,
"types"
:
"dist/index"
,
...
@@ -50,7 +50,7 @@
...
@@ -50,7 +50,7 @@
"dependencies"
:
{
"dependencies"
:
{
"@eth-optimism/contracts"
:
"0.5.37"
,
"@eth-optimism/contracts"
:
"0.5.37"
,
"@eth-optimism/core-utils"
:
"0.10.1"
,
"@eth-optimism/core-utils"
:
"0.10.1"
,
"@eth-optimism/contracts-bedrock"
:
"0.8.
1
"
,
"@eth-optimism/contracts-bedrock"
:
"0.8.
2
"
,
"lodash"
:
"^4.17.21"
,
"lodash"
:
"^4.17.21"
,
"merkletreejs"
:
"^0.2.27"
,
"merkletreejs"
:
"^0.2.27"
,
"rlp"
:
"^2.2.7"
"rlp"
:
"^2.2.7"
...
...
specs/derivation.md
View file @
afba7c22
...
@@ -299,9 +299,8 @@ Batcher transactions are encoded as `version_byte ++ rollup_payload` (where `++`
...
@@ -299,9 +299,8 @@ Batcher transactions are encoded as `version_byte ++ rollup_payload` (where `++`
| 0 |
`frame ...`
(one or more frames, concatenated) |
| 0 |
`frame ...`
(one or more frames, concatenated) |
Unknown versions make the batcher transaction invalid (it must be ignored by the rollup node).
Unknown versions make the batcher transaction invalid (it must be ignored by the rollup node).
All frames in a batcher transaction must be parseable. If any one frame fails to parse, the all frames in the
The
`rollup_payload`
may be right-padded with 0s, which will be ignored. It's allowed for them to be
transaction are rejected.
interpreted as frames for channel 0, which must always be ignored.
> **TODO** specify batcher authentication (i.e. where do we store / make available the public keys of authorize batcher
> **TODO** specify batcher authentication (i.e. where do we store / make available the public keys of authorize batcher
> signers)
> signers)
...
@@ -367,6 +366,9 @@ When decompressing a channel, we limit the amount of decompressed data to `MAX_R
...
@@ -367,6 +366,9 @@ When decompressing a channel, we limit the amount of decompressed data to `MAX_R
humongous amount of data). If the decompressed data exceeds the limit, things proceeds as thought the channel contained
humongous amount of data). If the decompressed data exceeds the limit, things proceeds as thought the channel contained
only the first
`MAX_RLP_BYTES_PER_CHANNEL`
decompressed bytes.
only the first
`MAX_RLP_BYTES_PER_CHANNEL`
decompressed bytes.
When decoding batches, all batches that can be completly decoded below
`MAX_RLP_BYTES_PER_CHANNEL`
will be accepted
even if the size of the channel is greater than
`MAX_RLP_BYTES_PER_CHANNEL`
.
While the above pseudocode implies that all batches are known in advance, it is possible to perform streaming
While the above pseudocode implies that all batches are known in advance, it is possible to perform streaming
compression and decompression of RLP-encoded batches. This means it is possible to start including channel frames in a
compression and decompression of RLP-encoded batches. This means it is possible to start including channel frames in a
[
batcher transaction
][
g-batcher-transaction
]
before we know how many batches (and how many frames) the channel will
[
batcher transaction
][
g-batcher-transaction
]
before we know how many batches (and how many frames) the channel will
...
...
specs/exec-engine.md
View file @
afba7c22
...
@@ -101,6 +101,11 @@ The `noTxPool` is optional as well, and extends the `transactions` meaning:
...
@@ -101,6 +101,11 @@ The `noTxPool` is optional as well, and extends the `transactions` meaning:
into the payload, after any of the
`transactions`
. This is the default behavior a L1 node implements.
into the payload, after any of the
`transactions`
. This is the default behavior a L1 node implements.
-
If
`true`
, the execution engine must not change anything about the given list of
`transactions`
.
-
If
`true`
, the execution engine must not change anything about the given list of
`transactions`
.
If the
`transactions`
field is present, the engine must execute the transactions in order and return
`STATUS_INVALID`
if there is an error processing the transactions. It must return
`STATUS_VALID`
if all of the transactions could
be executed without error.
**Note**
: The state transition rules have been modified such that deposits will never fail
so if
`engine_forkchoiceUpdatedV1`
returns
`STATUS_INVALID`
it is because a batched transaction is invalid.
[
rollup-driver
]:
rollup-node.md
[
rollup-driver
]:
rollup-node.md
### `engine_newPayloadV1`
### `engine_newPayloadV1`
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment