Commit ab26d1a6 authored by tre's avatar tre

Merge branch 'develop' of https://github.com/ethereum-optimism/optimism into develop

parents 42e79775 ed9d6a95

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

---
'@eth-optimism/contracts-periphery': patch
---
Add faucet contract
---
'@eth-optimism/fault-detector': minor
---
Remove pre-bedrock support from fault detector.
---
'@eth-optimism/contracts-bedrock': minor
---
Bump XDM semver after #5444
---
'@eth-optimism/sdk': patch
---
Fix firefox bug with getTokenPair
---
'@eth-optimism/chain-mon': patch
---
Fixes a bug in the wd-mon service where a node connection failure event was not handled correctly
---
'@eth-optimism/contracts-bedrock': minor
---
Increase precision in `SafeCall.hasMinGas`
---
'@eth-optimism/sdk': patch
---
Update the migrated withdrawal gas limit for non goerli networks
---
'@eth-optimism/contracts-periphery': patch
---
Change type for auth id on Faucet contracts from bytes to bytes32
---
'@eth-optimism/sdk': patch
---
Add warning if bedrock is not turned on
This diff is collapsed.
......@@ -12,8 +12,3 @@ build/_workspace
build/bin
build/_bin
tests/testdata
l2geth/signer/fourbyte
l2geth/cmd/puppeth
l2geth/cmd/clef
go/gas-oracle/gas-oracle
go/batch-submitter/batch-submitter
......@@ -4,7 +4,7 @@ module.exports = {
browser: true,
es6: true,
},
ignorePatterns: ['dist', 'coverage', 'packages/contracts/hardhat'],
ignorePatterns: ['dist', 'coverage'],
extends: ['plugin:prettier/recommended'],
parser: '@babel/eslint-parser',
parserOptions: {
......
3b1129b5bc43ba22a9bcf4e4323c5a9df0023140
# Legacy codebases
/batch-submitter @ethereum-optimism/legacy-reviewers
/bss-core @ethereum-optimism/legacy-reviewers
/gas-oracle @ethereum-optimism/legacy-reviewers
/integration-tests @ethereum-optimism/legacy-reviewers
/l2geth @ethereum-optimism/legacy-reviewers
/l2geth-exporter @ethereum-optimism/legacy-reviewers
/packages/actor-tests @ethereum-optimism/legacy-reviewers
/packages/common-ts @ethereum-optimism/typescript-reviewers
/packages/contracts @ethereum-optimism/contract-reviewers
/packages/contracts-bedrock @ethereum-optimism/contract-reviewers
/packages/contracts-periphery @ethereum-optimism/contract-reviewers
/packages/core-utils @ethereum-optimism/legacy-reviewers
/packages/data-transport-layer @ethereum-optimism/legacy-reviewers
/packages/chain-mon @smartcontracts
/packages/fault-detector @ethereum-optimism/devxpod
/packages/hardhat-deploy-config @ethereum-optimism/legacy-reviewers
/packages/message-relayer @ethereum-optimism/legacy-reviewers
/packages/migration-data @ethereum-optimism/legacy-reviewers
/packages/replica-healthcheck @ethereum-optimism/legacy-reviewers
/packages/sdk @ethereum-optimism/devxpod
/packages/atst @ethereum-optimism/devxpod
......@@ -29,6 +19,7 @@
/op-node @ethereum-optimism/go-reviewers
/op-node/rollup @protolambda @trianglesphere
/op-proposer @ethereum-optimism/go-reviewers
/op-bootnode @ethereum-optimism/go-reviewers
/op-program @ethereum-optimism/go-reviewers
/op-service @ethereum-optimism/go-reviewers
/ops-bedrock @ethereum-optimism/go-reviewers
......
---
C-Protocol-Critical:
- 'packages/data-transport-layer/**/*.ts'
- 'packages/contracts/**/*.sol'
- 'l2geth/**/*.go'
- 'packages/contracts-bedrock/**/*.sol'
This diff is collapsed.
This diff is collapsed.
......@@ -23,6 +23,8 @@ on:
- op-proposer
- proxyd
- indexer
- fault-detector
- ci-builder
prerelease:
description: Increment major/minor/patch as prerelease?
required: false
......
......@@ -17,22 +17,11 @@ dist
artifacts
cache
l2geth/build/bin
packages/contracts/deployments/custom
packages/contracts/coverage*
packages/contracts/@ens*
packages/contracts/@openzeppelin*
packages/contracts/hardhat*
packages/contracts-periphery/coverage*
packages/contracts-periphery/@openzeppelin*
packages/contracts-periphery/hardhat*
packages/contracts-periphery/forge-artifacts*
packages/data-transport-layer/db
packages/integration-tests-bedrock/cache
packages/integration-tests-bedrock/artifacts
packages/contracts-bedrock/deployments/devnetL1
packages/contracts-bedrock/deployments/anvil
......
[submodule "tests"]
path = l2geth/tests/testdata
url = https://github.com/ethereum/tests
[submodule "packages/contracts-periphery/lib/multicall"]
path = packages/contracts-periphery/lib/multicall
url = https://github.com/mds1/multicall
......
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn lerna run --concurrency 1 --stream pre-commit --since HEAD --exclude-dependents
yarn nx affected --target=pre-commit
......@@ -18,6 +18,7 @@ tests/
# Semgrep-action log folder
.semgrep_logs/
l2geth/
op-bindings/bindings
packages/*/node_modules
packages/*/test
......@@ -20,22 +20,10 @@
"directory": "packages/contracts-periphery",
"changeProcessCWD": true
},
{
"directory": "packages/data-transport-layer",
"changeProcessCWD": true
},
{
"directory": "packages/chain-mon",
"changeProcessCWD": true
},
{
"directory": "packages/batch-submitter",
"changeProcessCWD": true
},
{
"directory": "packages/message-relayer",
"changeProcessCWD": true
},
{
"directory": "packages/fault-detector",
"changeProcessCWD": true
......@@ -45,4 +33,4 @@
"eslint.format.enable": true,
"editorconfig.generateAuto": false,
"files.trimTrailingWhitespace": true
}
}
\ No newline at end of file
......@@ -120,27 +120,6 @@ export DOCKER_BUILDKIT=1
docker-compose build
```
This will build the following containers:
* [`l1_chain`](https://hub.docker.com/r/ethereumoptimism/hardhat): simulated L1 chain using hardhat-evm as a backend
* [`deployer`](https://hub.docker.com/r/ethereumoptimism/deployer): process that deploys L1 smart contracts to the L1 chain
* [`dtl`](https://hub.docker.com/r/ethereumoptimism/data-transport-layer): service that indexes transaction data from the L1 chain
* [`l2geth`](https://hub.docker.com/r/ethereumoptimism/l2geth): L2 geth node running in Sequencer mode
* [`verifier`](https://hub.docker.com/r/ethereumoptimism/go-ethereum): L2 geth node running in Verifier mode
* [`relayer`](https://hub.docker.com/r/ethereumoptimism/message-relayer): helper process that relays messages between L1 and L2
* [`batch_submitter`](https://hub.docker.com/r/ethereumoptimism/batch-submitter-service): service that submits batches of Sequencer transactions to the L1 chain
* [`integration_tests`](https://hub.docker.com/r/ethereumoptimism/integration-tests): integration tests in a box
If you want to make a change to a container, you'll need to take it down and rebuild it.
For example, if you make a change in l2geth:
```bash
cd ops
docker-compose stop -- l2geth
docker-compose build -- l2geth
docker-compose start l2geth
```
Source code changes can have an impact on more than one container.
**If you're unsure about which containers to rebuild, just rebuild them all**:
......@@ -193,16 +172,6 @@ cd packages/package-to-test
yarn test
```
#### Running integration tests
Follow above instructions for building the whole stack.
Build and run the integration tests:
```bash
cd integration-tests
yarn build
yarn test:integration
```
#### Running contract static analysis
We perform static analysis with [`slither`](https://github.com/crytic/slither).
......
......@@ -32,6 +32,14 @@ op-node:
make -C ./op-node op-node
.PHONY: op-node
generate-mocks-op-node:
make -C ./op-node generate-mocks
.PHONY: generate-mocks-op-node
generate-mocks-op-service:
make -C ./op-service generate-mocks
.PHONY: generate-mocks-op-service
op-batcher:
make -C ./op-batcher op-batcher
.PHONY: op-batcher
......@@ -66,11 +74,11 @@ nuke: clean devnet-clean
.PHONY: nuke
devnet-up:
@bash ./ops-bedrock/devnet-up.sh
PYTHONPATH=./bedrock-devnet python3 ./bedrock-devnet/main.py --monorepo-dir=.
.PHONY: devnet-up
devnet-up-deploy:
PYTHONPATH=./bedrock-devnet python3 ./bedrock-devnet/main.py --monorepo-dir=.
PYTHONPATH=./bedrock-devnet python3 ./bedrock-devnet/main.py --monorepo-dir=. --deploy
.PHONY: devnet-up-deploy
devnet-down:
......
......@@ -51,37 +51,44 @@ Refer to the Directory Structure section below to understand which packages are
~~ Production ~~
├── <a href="./packages">packages</a>
│ ├── <a href="./packages/common-ts">common-ts</a>: Common tools for building apps in TypeScript
│ ├── <a href="./packages/contracts">contracts</a>: L1 and L2 smart contracts for Optimism
│ ├── <a href="./packages/contracts-bedrock">contracts-bedrock</a>: Bedrock smart contracts.
│ ├── <a href="./packages/contracts-periphery">contracts-periphery</a>: Peripheral contracts for Optimism
│ ├── <a href="./packages/core-utils">core-utils</a>: Low-level utilities that make building Optimism easier
│ ├── <a href="./packages/data-transport-layer">data-transport-layer</a>: Service for indexing Optimism-related L1 data
│ ├── <a href="./packages/chain-mon">chain-mon</a>: Chain monitoring services
│ ├── <a href="./packages/fault-detector">fault-detector</a>: Service for detecting Sequencer faults
│ ├── <a href="./packages/message-relayer">message-relayer</a>: Tool for automatically relaying L1<>L2 messages in development
│ ├── <a href="./packages/replica-healthcheck">replica-healthcheck</a>: Service for monitoring the health of a replica node
│ └── <a href="./packages/sdk">sdk</a>: provides a set of tools for interacting with Optimism
├── <a href="./batch-submitter">batch-submitter</a>: Service for submitting batches of transactions and results to L1
├── <a href="./bss-core">bss-core</a>: Core batch-submitter logic and utilities
├── <a href="./gas-oracle">gas-oracle</a>: Service for updating L1 gas prices on L2
├── <a href="./indexer">indexer</a>: indexes and syncs transactions
├── <a href="./infra/op-replica">infra/op-replica</a>: Deployment examples and resources for running an Optimism replica
├── <a href="./integration-tests">integration-tests</a>: Various integration tests for the Optimism network
├── <a href="./l2geth">l2geth</a>: Optimism client software, a fork of <a href="https://github.com/ethereum/go-ethereum/tree/v1.9.10">geth v1.9.10</a>
├── <a href="./l2geth-exporter">l2geth-exporter</a>: A prometheus exporter to collect/serve metrics from an L2 geth node
├── <a href="./op-exporter">op-exporter</a>: A prometheus exporter to collect/serve metrics from an Optimism node
├── <a href="./proxyd">proxyd</a>: Configurable RPC request router and proxy
├── <a href="./technical-documents">technical-documents</a>: audits and post-mortem documents
~~ BEDROCK upgrade - Not production-ready yet, part of next major upgrade ~~
├── <a href="./packages">packages</a>
│ └── <a href="./packages/contracts-bedrock">contracts-bedrock</a>: Bedrock smart contracts. To be merged with ./packages/contracts.
├── <a href="./op-bindings">op-bindings</a>: Go bindings for Bedrock smart contracts.
├── <a href="./op-batcher">op-batcher</a>: L2-Batch Submitter, submits bundles of batches to L1
├── <a href="./op-bootnode">op-bootnode</a>: Standalone op-node discovery bootnode
├── <a href="./op-chain-ops">op-chain-ops</a>: State surgery utilities
├── <a href="./op-challenger">op-challenger</a>: Dispute game challenge agent
├── <a href="./op-e2e">op-e2e</a>: End-to-End testing of all bedrock components in Go
├── <a href="./op-node">op-node</a>: rollup consensus-layer client.
├── <a href="./op-exporter">op-exporter</a>: Prometheus exporter client
├── <a href="./op-heartbeat">op-heartbeat</a>: Heartbeat monitor service
├── <a href="./op-node">op-node</a>: rollup consensus-layer client
├── <a href="./op-program">op-program</a>: Fault proof program
├── <a href="./op-proposer">op-proposer</a>: L2-Output Submitter, submits proposals to L1
├── <a href="./op-service">op-service</a>: Common codebase utilities
├── <a href="./op-signer">op-signer</a>: Client signer
├── <a href="./op-wheel">op-wheel</a>: Database utilities
├── <a href="./ops-bedrock">ops-bedrock</a>: Bedrock devnet work
├── <a href="./proxyd">proxyd</a>: Configurable RPC request router and proxy
└── <a href="./specs">specs</a>: Specs of the rollup starting at the Bedrock upgrade
~~ Pre-BEDROCK ~~
├── <a href="./packages">packages</a>
│ ├── <a href="./packages/common-ts">common-ts</a>: Common tools for building apps in TypeScript
│ ├── <a href="./packages/contracts-periphery">contracts-periphery</a>: Peripheral contracts for Optimism
│ ├── <a href="./packages/core-utils">core-utils</a>: Low-level utilities that make building Optimism easier
│ ├── <a href="./packages/chain-mon">chain-mon</a>: Chain monitoring services
│ ├── <a href="./packages/fault-detector">fault-detector</a>: Service for detecting Sequencer faults
│ ├── <a href="./packages/replica-healthcheck">replica-healthcheck</a>: Service for monitoring the health of a replica node
│ └── <a href="./packages/sdk">sdk</a>: provides a set of tools for interacting with Optimism
├── <a href="./indexer">indexer</a>: indexes and syncs transactions
├── <a href="./op-exporter">op-exporter</a>: A prometheus exporter to collect/serve metrics from an Optimism node
├── <a href="./proxyd">proxyd</a>: Configurable RPC request router and proxy
└── <a href="./technical-documents">technical-documents</a>: audits and post-mortem documents
</pre>
## Branching Model
......@@ -111,7 +118,7 @@ The primary development branch is [`develop`](https://github.com/ethereum-optimi
`develop` contains the most up-to-date software that remains backwards compatible with the latest experimental [network deployments](https://community.optimism.io/docs/useful-tools/networks/).
If you're making a backwards compatible change, please direct your pull request towards `develop`.
**Changes to contracts within `packages/contracts/contracts` are usually NOT considered backwards compatible and SHOULD be made against a release candidate branch**.
**Changes to contracts within `packages/contracts-bedrock/contracts` are usually NOT considered backwards compatible and SHOULD be made against a release candidate branch**.
Some exceptions to this rule exist for cases in which we absolutely must deploy some new contract after a release candidate branch has already been fully deployed.
If you're changing or adding a contract and you're unsure about which branch to make a PR into, default to using the latest release candidate branch.
See below for info about release candidate branches.
......@@ -119,7 +126,7 @@ See below for info about release candidate branches.
### Release candidate branches
Branches marked `release/X.X.X` are **release candidate branches**.
Changes that are not backwards compatible and all changes to contracts within `packages/contracts/contracts` MUST be directed towards a release candidate branch.
Changes that are not backwards compatible and all changes to contracts within `packages/contracts-bedrock/contracts` MUST be directed towards a release candidate branch.
Release candidates are merged into `develop` and then into `master` once they've been fully deployed.
We may sometimes have more than one active `release/X.X.X` branch if we're in the middle of a deployment.
See table in the **Active Branches** section above to find the right branch to target.
......@@ -152,6 +159,4 @@ It's strongly recommended to avoid merging PRs into develop during an active rel
## License
Code forked from [`go-ethereum`](https://github.com/ethereum/go-ethereum) under the name [`l2geth`](https://github.com/ethereum-optimism/optimism/tree/master/l2geth) is licensed under the [GNU GPLv3](https://gist.github.com/kn9ts/cbe95340d29fc1aaeaa5dd5c059d2e60) in accordance with the [original license](https://github.com/ethereum/go-ethereum/blob/master/COPYING).
All other files within this repository are licensed under the [MIT License](https://github.com/ethereum-optimism/optimism/blob/master/LICENSE) unless stated otherwise.
# @eth-optimism/batch-submitter-service
## 0.1.16
### Patch Changes
- 32bd79ec9: Allow deposit only batches
- da79ef441: fix flag name for MaxStateRootElements in batch-submitter
fix log package for proposer
## 0.1.15
### Patch Changes
- 1d8d50c42: build(deps): bump golang.org/x/crypto from 0.0.0-20220307211146-efcb8507fb70 to 0.1.0 in /batch-submitter
## 0.1.14
### Patch Changes
- 72fa86bff: chore(deps): bump github.com/prometheus/client_golang from 1.11.0 to 1.11.1 in /batch-submitter
## 0.1.13
### Patch Changes
- 7a8812d14: Update go-ethereum to v1.10.26
## 0.1.12
### Patch Changes
- 6f458607: Bump go-ethereum to 1.10.17
## 0.1.11
### Patch Changes
- b2aa08e0: Add MAX_PLAINTEXT_BATCH_SIZE parameter to max out compression
## 0.1.10
### Patch Changes
- 526eac8d: feat: bss less strict min-tx-size
## 0.1.9
### Patch Changes
- 160f4c3d: Update docker image to use golang 1.18.0
- 0c4d4e08: l2geth: Revert transaction pubsub feature
## 0.1.8
### Patch Changes
- 88601cb7: Refactored Dockerfiles
- 6856b215: Count reverted transactions in failed_submissions
- 9678b357: Add Min/MaxStateRootElements configuration
- f8348862: l2geth: Sync from Backend Queue
- 727b0582: Enforce min/max tx size on plaintext batch encoding
## 0.1.7
### Patch Changes
- aca0684e: Add 20% buffer to gas estimation on tx-batch submission to prevent OOG reverts
- 75040ca5: Adds MIN_L1_TX_SIZE configuration
## 0.1.6
### Patch Changes
- 6af67df5: Move L2 dial logic out of bss-core to avoid l2geth dependency
- fe680568: Enable the usage of typed batches and type 0 zlib compressed batches
## 0.1.5
### Patch Changes
- 6f2ea193: Update to go-ethereum v1.10.16
- 87359fd2: Refactors the bss-core service to use a metrics interface to allow
driver-specific metric extensions
## 0.1.4
### Patch Changes
- bcbde5f3: Fixes a bug that causes the txmgr to not wait for the configured numConfirmations
## 0.1.3
### Patch Changes
- 69118ac3: Switch num_elements_per_batch from Histogram to Summary
- df98d134: Remove extra space in metric names
- 3ec06301: Default to JSON logs, add LOG_TERMINAL flag for debugging
- fe321618: Unify metric name format
- 93a26819: Fixes a bug where clearing txs are rejected on startup due to missing gas limit
## 0.1.2
### Patch Changes
- c775ffbe: fix BSS log-level flag parsing
- d093a6bb: Adds a fix for the BSS to account for the new timestamp logic in L2Geth
- d4c2e01b: Restructure to use bss-core package
## 0.1.1
### Patch Changes
- 5905f3dc: Update golang version to support HTTP/2
- c1eba2e6: use EIP-1559 txns for tx/state batches
## 0.1.0
### Minor Changes
- 356b7271: Add multi-tx support, clear pending txs on startup
### Patch Changes
- 85aa148d: Adds confirmation depth awareness to txmgr
## 0.0.2
### Patch Changes
- d6e0de5a: Fix metrics server
FROM golang:1.18.0-alpine3.15 as builder
RUN apk add --no-cache make gcc musl-dev linux-headers git jq bash
COPY ./batch-submitter /go/batch-submitter
COPY ./bss-core /go/bss-core
COPY ./l2geth /go/l2geth
COPY ./batch-submitter/docker.go.work /go/go.work
WORKDIR /go/batch-submitter
RUN make
FROM alpine:3.15
RUN apk add --no-cache ca-certificates jq curl
COPY --from=builder /go/batch-submitter/batch-submitter /usr/local/bin/
WORKDIR /usr/local/bin
COPY ./ops/scripts/batch-submitter.sh .
ENTRYPOINT ["batch-submitter"]
GITCOMMIT := $(shell git rev-parse HEAD)
GITDATE := $(shell git show -s --format='%ct')
GITVERSION := $(shell cat package.json | jq .version)
LDFLAGSSTRING +=-X main.GitCommit=$(GITCOMMIT)
LDFLAGSSTRING +=-X main.GitDate=$(GITDATE)
LDFLAGSSTRING +=-X main.GitVersion=$(GITVERSION)
LDFLAGS := -ldflags "$(LDFLAGSSTRING)"
CTC_ABI_ARTIFACT := ../../packages/contracts/artifacts/contracts/L1/rollup/CanonicalTransactionChain.sol/CanonicalTransactionChain.json
SCC_ABI_ARTIFACT := ../../packages/contracts/artifacts/contracts/L1/rollup/StateCommitmentChain.sol/StateCommitmentChain.json
batch-submitter:
env GO111MODULE=on go build -v $(LDFLAGS) ./cmd/batch-submitter
clean:
rm batch-submitter
test:
go test -v ./...
lint:
golangci-lint run ./...
bindings: bindings-ctc bindings-scc
bindings-ctc:
$(eval temp := $(shell mktemp))
cat $(CTC_ABI_ARTIFACT) \
| jq -r .bytecode > $(temp)
cat $(CTC_ABI_ARTIFACT) \
| jq .abi \
| abigen --pkg ctc \
--abi - \
--out bindings/ctc/canonical_transaction_chain.go \
--type CanonicalTransactionChain \
--bin $(temp)
rm $(temp)
bindings-scc:
$(eval temp := $(shell mktemp))
cat $(SCC_ABI_ARTIFACT) \
| jq -r .bytecode > $(temp)
cat $(SCC_ABI_ARTIFACT) \
| jq .abi \
| abigen --pkg scc \
--abi - \
--out bindings/scc/state_commitment_chain.go \
--type StateCommitmentChain \
--bin $(temp)
rm $(temp)
.PHONY: \
batch-submitter \
bindings \
bindings-ctc \
bindings-scc \
clean \
test \
lint
package batchsubmitter
import (
"context"
"os"
"time"
"github.com/ethereum-optimism/optimism/batch-submitter/drivers/proposer"
"github.com/ethereum-optimism/optimism/batch-submitter/drivers/sequencer"
bsscore "github.com/ethereum-optimism/optimism/bss-core"
"github.com/ethereum-optimism/optimism/bss-core/dial"
"github.com/ethereum-optimism/optimism/bss-core/metrics"
"github.com/ethereum-optimism/optimism/bss-core/txmgr"
"github.com/ethereum/go-ethereum/log"
"github.com/getsentry/sentry-go"
"github.com/urfave/cli"
)
// Main is the entrypoint into the batch submitter service. This method returns
// a closure that executes the service and blocks until the service exits. The
// use of a closure allows the parameters bound to the top-level main package,
// e.g. GitVersion, to be captured and used once the function is executed.
func Main(gitVersion string) func(ctx *cli.Context) error {
return func(cliCtx *cli.Context) error {
cfg, err := NewConfig(cliCtx)
if err != nil {
return err
}
log.Info("Config parsed",
"min_tx_size", cfg.MinL1TxSize,
"max_tx_size", cfg.MaxL1TxSize)
// The call to defer is done here so that any errors logged from
// this point on are posted to Sentry before exiting.
if cfg.SentryEnable {
defer sentry.Flush(2 * time.Second)
}
log.Info("Initializing batch submitter")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Set up our logging. If Sentry is enabled, we will use our custom log
// handler that logs to stdout and forwards any error messages to Sentry
// for collection. Otherwise, logs will only be posted to stdout.
var logHandler log.Handler
if cfg.SentryEnable {
err := sentry.Init(sentry.ClientOptions{
Dsn: cfg.SentryDsn,
Environment: cfg.EthNetworkName,
Release: "batch-submitter@" + gitVersion,
TracesSampleRate: bsscore.TraceRateToFloat64(cfg.SentryTraceRate),
Debug: false,
})
if err != nil {
return err
}
logHandler = bsscore.SentryStreamHandler(os.Stdout, log.JSONFormat())
} else if cfg.LogTerminal {
logHandler = log.StreamHandler(os.Stdout, log.TerminalFormat(true))
} else {
logHandler = log.StreamHandler(os.Stdout, log.JSONFormat())
}
logLevel, err := log.LvlFromString(cfg.LogLevel)
if err != nil {
return err
}
log.Root().SetHandler(log.LvlFilterHandler(logLevel, logHandler))
// Parse sequencer private key and CTC contract address.
sequencerPrivKey, ctcAddress, err := bsscore.ParseWalletPrivKeyAndContractAddr(
"Sequencer", cfg.Mnemonic, cfg.SequencerHDPath,
cfg.SequencerPrivateKey, cfg.CTCAddress,
)
if err != nil {
return err
}
// Parse proposer private key and SCC contract address.
proposerPrivKey, sccAddress, err := bsscore.ParseWalletPrivKeyAndContractAddr(
"Proposer", cfg.Mnemonic, cfg.ProposerHDPath,
cfg.ProposerPrivateKey, cfg.SCCAddress,
)
if err != nil {
return err
}
// Connect to L1 and L2 providers. Perform these last since they are the
// most expensive.
l1Client, err := dial.L1EthClientWithTimeout(ctx, cfg.L1EthRpc, cfg.DisableHTTP2)
if err != nil {
return err
}
l2Client, err := DialL2EthClientWithTimeout(ctx, cfg.L2EthRpc, cfg.DisableHTTP2)
if err != nil {
return err
}
if cfg.MetricsServerEnable {
go metrics.RunServer(cfg.MetricsHostname, cfg.MetricsPort)
}
chainID, err := l1Client.ChainID(ctx)
if err != nil {
return err
}
txManagerConfig := txmgr.Config{
ResubmissionTimeout: cfg.ResubmissionTimeout,
ReceiptQueryInterval: time.Second,
NumConfirmations: cfg.NumConfirmations,
SafeAbortNonceTooLowCount: cfg.SafeAbortNonceTooLowCount,
}
var services []*bsscore.Service
if cfg.RunTxBatchSubmitter {
batchTxDriver, err := sequencer.NewDriver(sequencer.Config{
Name: "Sequencer",
L1Client: l1Client,
L2Client: l2Client,
BlockOffset: cfg.BlockOffset,
MinTxSize: cfg.MinL1TxSize,
MaxTxSize: cfg.MaxL1TxSize,
MaxPlaintextBatchSize: cfg.MaxPlaintextBatchSize,
CTCAddr: ctcAddress,
ChainID: chainID,
PrivKey: sequencerPrivKey,
BatchType: sequencer.BatchTypeFromString(cfg.SequencerBatchType),
})
if err != nil {
return err
}
services = append(services, bsscore.NewService(bsscore.ServiceConfig{
Context: ctx,
Driver: batchTxDriver,
PollInterval: cfg.PollInterval,
ClearPendingTx: cfg.ClearPendingTxs,
L1Client: l1Client,
TxManagerConfig: txManagerConfig,
}))
}
if cfg.RunStateBatchSubmitter {
batchStateDriver, err := proposer.NewDriver(proposer.Config{
Name: "Proposer",
L1Client: l1Client,
L2Client: l2Client,
BlockOffset: cfg.BlockOffset,
MinStateRootElements: cfg.MinStateRootElements,
MaxStateRootElements: cfg.MaxStateRootElements,
SCCAddr: sccAddress,
CTCAddr: ctcAddress,
ChainID: chainID,
PrivKey: proposerPrivKey,
})
if err != nil {
return err
}
services = append(services, bsscore.NewService(bsscore.ServiceConfig{
Context: ctx,
Driver: batchStateDriver,
PollInterval: cfg.PollInterval,
ClearPendingTx: cfg.ClearPendingTxs,
L1Client: l1Client,
TxManagerConfig: txManagerConfig,
}))
}
batchSubmitter, err := bsscore.NewBatchSubmitter(ctx, cancel, services)
if err != nil {
log.Error("Unable to create batch submitter", "error", err)
return err
}
log.Info("Starting batch submitter")
if err := batchSubmitter.Start(); err != nil {
return err
}
defer batchSubmitter.Stop()
log.Info("Batch submitter started")
<-(chan struct{})(nil)
return nil
}
}
This diff is collapsed.
package main
import (
"fmt"
"os"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/urfave/cli"
batchsubmitter "github.com/ethereum-optimism/optimism/batch-submitter"
"github.com/ethereum-optimism/optimism/batch-submitter/flags"
)
var (
GitVersion = ""
GitCommit = ""
GitDate = ""
)
func main() {
// Set up logger with a default INFO level in case we fail to parse flags.
// Otherwise the final critical log won't show what the parsing error was.
log.Root().SetHandler(
log.LvlFilterHandler(
log.LvlInfo,
log.StreamHandler(os.Stdout, log.TerminalFormat(true)),
),
)
app := cli.NewApp()
app.Flags = flags.Flags
app.Version = fmt.Sprintf("%s-%s", GitVersion, params.VersionWithCommit(GitCommit, GitDate))
app.Name = "batch-submitter"
app.Usage = "Batch Submitter Service"
app.Description = "Service for generating and submitting batched transactions " +
"that synchronize L2 state to L1 contracts"
app.Action = batchsubmitter.Main(GitVersion)
err := app.Run(os.Args)
if err != nil {
log.Crit("Application failed", "message", err)
}
}
This diff is collapsed.
package batchsubmitter_test
import (
"fmt"
"testing"
batchsubmitter "github.com/ethereum-optimism/optimism/batch-submitter"
"github.com/stretchr/testify/require"
)
var validateConfigTests = []struct {
name string
cfg batchsubmitter.Config
expErr error
}{
{
name: "bad log level",
cfg: batchsubmitter.Config{
LogLevel: "unknown",
},
expErr: fmt.Errorf("unknown level: unknown"),
},
{
name: "sequencer priv key or mnemonic none set",
cfg: batchsubmitter.Config{
LogLevel: "info",
SequencerPrivateKey: "",
Mnemonic: "",
SequencerHDPath: "",
},
expErr: batchsubmitter.ErrSequencerPrivKeyOrMnemonic,
},
{
name: "sequencer priv key or mnemonic both set",
cfg: batchsubmitter.Config{
LogLevel: "info",
SequencerPrivateKey: "sequencer-privkey",
Mnemonic: "mnemonic",
SequencerHDPath: "sequencer-path",
},
expErr: batchsubmitter.ErrSequencerPrivKeyOrMnemonic,
},
{
name: "sequencer priv key or mnemonic only mnemonic set",
cfg: batchsubmitter.Config{
LogLevel: "info",
SequencerPrivateKey: "",
Mnemonic: "mnemonic",
SequencerHDPath: "",
},
expErr: batchsubmitter.ErrSequencerPrivKeyOrMnemonic,
},
{
name: "sequencer priv key or mnemonic only hdpath set",
cfg: batchsubmitter.Config{
LogLevel: "info",
SequencerPrivateKey: "",
Mnemonic: "",
SequencerHDPath: "sequencer-path",
},
expErr: batchsubmitter.ErrSequencerPrivKeyOrMnemonic,
},
{
name: "proposer priv key or mnemonic none set",
cfg: batchsubmitter.Config{
LogLevel: "info",
SequencerPrivateKey: "sequencer-privkey",
ProposerPrivateKey: "",
Mnemonic: "",
ProposerHDPath: "",
},
expErr: batchsubmitter.ErrProposerPrivKeyOrMnemonic,
},
{
name: "proposer priv key or mnemonic both set",
cfg: batchsubmitter.Config{
LogLevel: "info",
SequencerPrivateKey: "sequencer-privkey",
ProposerPrivateKey: "proposer-privkey",
Mnemonic: "mnemonic",
ProposerHDPath: "proposer-path",
},
expErr: batchsubmitter.ErrProposerPrivKeyOrMnemonic,
},
{
name: "proposer priv key or mnemonic only mnemonic set",
cfg: batchsubmitter.Config{
LogLevel: "info",
SequencerPrivateKey: "sequencer-privkey",
ProposerPrivateKey: "",
Mnemonic: "mnemonic",
ProposerHDPath: "",
},
expErr: batchsubmitter.ErrProposerPrivKeyOrMnemonic,
},
{
name: "proposer priv key or mnemonic only hdpath set",
cfg: batchsubmitter.Config{
LogLevel: "info",
SequencerPrivateKey: "sequencer-privkey",
ProposerPrivateKey: "",
Mnemonic: "",
ProposerHDPath: "proposer-path",
},
expErr: batchsubmitter.ErrProposerPrivKeyOrMnemonic,
},
{
name: "same sequencer and proposer hd path",
cfg: batchsubmitter.Config{
LogLevel: "info",
Mnemonic: "mnemonic",
SequencerHDPath: "path",
ProposerHDPath: "path",
},
expErr: batchsubmitter.ErrSameSequencerAndProposerHDPath,
},
{
name: "same sequencer and proposer privkey",
cfg: batchsubmitter.Config{
LogLevel: "info",
SequencerPrivateKey: "privkey",
ProposerPrivateKey: "privkey",
},
expErr: batchsubmitter.ErrSameSequencerAndProposerPrivKey,
},
{
name: "sentry-dsn not set when sentry-enable is true",
cfg: batchsubmitter.Config{
LogLevel: "info",
SequencerPrivateKey: "sequencer-privkey",
ProposerPrivateKey: "proposer-privkey",
SentryEnable: true,
SentryDsn: "",
},
expErr: batchsubmitter.ErrSentryDSNNotSet,
},
// Valid configs
{
name: "valid config with privkeys and no sentry",
cfg: batchsubmitter.Config{
LogLevel: "info",
SequencerPrivateKey: "sequencer-privkey",
ProposerPrivateKey: "proposer-privkey",
SentryEnable: false,
SentryDsn: "",
},
expErr: nil,
},
{
name: "valid config with mnemonic and no sentry",
cfg: batchsubmitter.Config{
LogLevel: "info",
Mnemonic: "mnemonic",
SequencerHDPath: "sequencer-path",
ProposerHDPath: "proposer-path",
SentryEnable: false,
SentryDsn: "",
},
expErr: nil,
},
{
name: "valid config with privkeys and sentry",
cfg: batchsubmitter.Config{
LogLevel: "info",
SequencerPrivateKey: "sequencer-privkey",
ProposerPrivateKey: "proposer-privkey",
SentryEnable: true,
SentryDsn: "batch-submitter",
},
expErr: nil,
},
{
name: "valid config with mnemonic and sentry",
cfg: batchsubmitter.Config{
LogLevel: "info",
Mnemonic: "mnemonic",
SequencerHDPath: "sequencer-path",
ProposerHDPath: "proposer-path",
SentryEnable: true,
SentryDsn: "batch-submitter",
},
expErr: nil,
},
}
// TestValidateConfig asserts the behavior of ValidateConfig by testing expected
// error and success configurations.
func TestValidateConfig(t *testing.T) {
for _, test := range validateConfigTests {
t.Run(test.name, func(t *testing.T) {
err := batchsubmitter.ValidateConfig(&test.cfg)
require.Equal(t, err, test.expErr)
})
}
}
package batchsubmitter
import (
"context"
"crypto/tls"
"net/http"
"strings"
"github.com/ethereum-optimism/optimism/bss-core/dial"
"github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum-optimism/optimism/l2geth/log"
"github.com/ethereum-optimism/optimism/l2geth/rpc"
)
// DialL2EthClientWithTimeout attempts to dial the L2 provider using the
// provided URL. If the dial doesn't complete within dial.DefaultTimeout seconds,
// this method will return an error.
func DialL2EthClientWithTimeout(ctx context.Context, url string, disableHTTP2 bool) (
*ethclient.Client, error) {
ctxt, cancel := context.WithTimeout(ctx, dial.DefaultTimeout)
defer cancel()
if strings.HasPrefix(url, "http") {
httpClient := new(http.Client)
if disableHTTP2 {
log.Info("Disabled HTTP/2 support in L2 eth client")
httpClient.Transport = &http.Transport{
TLSNextProto: make(map[string]func(authority string, c *tls.Conn) http.RoundTripper),
}
}
rpcClient, err := rpc.DialHTTPWithClient(url, httpClient)
if err != nil {
return nil, err
}
return ethclient.NewClient(rpcClient), nil
}
return ethclient.DialContext(ctxt, url)
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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