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

Merge branch 'develop' into jg/limit_blocks_per_channel

parents 03bb2700 ef32b59d
...@@ -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 --flags 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 --flags <<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 --flags bedrock-go-tests
- store_test_results: - store_test_results:
path: /test-results path: /test-results
- run: - run:
......
...@@ -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
exclude:
global:
- infra/op-replica/** # snyk does not respect kustomizations, so not useful here
...@@ -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",
......
# Changelog # Changelog
## 0.5.26
### Patch Changes
- 397b27ee: Add data exporter
## 0.5.25 ## 0.5.25
### Patch Changes ### Patch Changes
......
...@@ -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) {
......
...@@ -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
} }
......
{ {
"name": "@eth-optimism/l2geth", "name": "@eth-optimism/l2geth",
"version": "0.5.25", "version": "0.5.26",
"private": true, "private": true,
"devDependencies": {} "devDependencies": {}
} }
...@@ -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")
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, 0o755)
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)
}
surgery: surgery:
go build -o ./surgery ./cmd/main.go go build -o ./surgery ./cmd/surgery/main.go
test: test:
go test ./... go test ./...
......
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)
}
...@@ -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)
......
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
}
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)
})
}
}
...@@ -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
......
...@@ -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,
} }
......
...@@ -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())
} }
} }
package state_surgery package ether
import ( import (
"bufio" "bufio"
......
package state_surgery package ether
import ( import (
"bytes" "bytes"
......
package state_surgery package ether
import ( import (
"path/filepath" "path/filepath"
......
package state_surgery package ether
import ( import (
"encoding/json" "encoding/json"
......
package state_surgery package ether
import ( import (
"math/big" "math/big"
......
package state_surgery package ether
import ( import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
......
package state_surgery package ether
import ( import (
"fmt" "fmt"
......
...@@ -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 {
......
...@@ -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) {
......
...@@ -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) {
}
...@@ -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
} }
......
...@@ -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 {
......
...@@ -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))
}
}
...@@ -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,
......
...@@ -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()
......
...@@ -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
} }
......
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}
} }
...@@ -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
} }
......
...@@ -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,
......
...@@ -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{
......
...@@ -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
} }
...@@ -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")
......
...@@ -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
......
...@@ -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)
......
# @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
......
{ {
"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",
......
# @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
......
...@@ -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
......
...@@ -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,
......
{ {
"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",
......
# @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
......
{ {
"private": true, "private": true,
"name": "@eth-optimism/drippie-mon", "name": "@eth-optimism/drippie-mon",
"version": "0.3.18", "version": "0.3.19",
"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": {
......
...@@ -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",
......
# @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
......
{ {
"private": true, "private": true,
"name": "@eth-optimism/message-relayer", "name": "@eth-optimism/message-relayer",
"version": "0.5.17", "version": "0.5.18",
"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": {
......
# @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
......
{ {
"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"
......
...@@ -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
......
...@@ -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`
......
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