From d16800522fffefa43ad738b40808f5298e572886 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Fri, 28 May 2021 07:56:36 -0400 Subject: [PATCH 01/46] chore: reduce hardhat timeout to 20 seconds (#968) --- .changeset/cold-cows-cross.md | 5 +++++ integration-tests/hardhat.config.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/cold-cows-cross.md diff --git a/.changeset/cold-cows-cross.md b/.changeset/cold-cows-cross.md new file mode 100644 index 000000000000..cc67cf4d950d --- /dev/null +++ b/.changeset/cold-cows-cross.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/integration-tests': patch +--- + +Reduce test timeout from 100 to 20 seconds diff --git a/integration-tests/hardhat.config.ts b/integration-tests/hardhat.config.ts index b68e8646ba7b..0fcc9b23325f 100644 --- a/integration-tests/hardhat.config.ts +++ b/integration-tests/hardhat.config.ts @@ -10,7 +10,7 @@ const enableGasReport = !!process.env.ENABLE_GAS_REPORT const config: HardhatUserConfig = { mocha: { - timeout: 100000, + timeout: 20000, }, networks: { optimism: { From 245136f14afed2a731a5ca5f9b5b071a3131961b Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Fri, 28 May 2021 13:00:15 -0400 Subject: [PATCH 02/46] fix: force LF line endings for scripts to avoid docker problems on Windows (#974) * fix: use correct line endings for windows * chore: add changeset --- .changeset/thin-waves-bathe.md | 5 +++++ .gitattributes | 1 + packages/contracts/bin/deploy.ts | 2 -- packages/contracts/package.json | 4 ++-- 4 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 .changeset/thin-waves-bathe.md create mode 100644 .gitattributes diff --git a/.changeset/thin-waves-bathe.md b/.changeset/thin-waves-bathe.md new file mode 100644 index 000000000000..605e410ac5df --- /dev/null +++ b/.changeset/thin-waves-bathe.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/contracts': patch +--- + +Minor change to how deploy.ts is invoked diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000000..dfdb8b771ce0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf diff --git a/packages/contracts/bin/deploy.ts b/packages/contracts/bin/deploy.ts index 719ac691c4fa..7ac14d6615e1 100755 --- a/packages/contracts/bin/deploy.ts +++ b/packages/contracts/bin/deploy.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env ts-node-script - import { Wallet } from 'ethers' import path from 'path' import dirtree from 'directory-tree' diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 0dd1f1c288c4..e8f750f6d7a3 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -38,13 +38,13 @@ "lint:check": "yarn run lint:typescript", "lint:typescript": "tslint --format stylish --project .", "clean": "rm -rf ./dist ./artifacts ./artifacts-ovm ./cache ./cache-ovm ./tsconfig.build.tsbuildinfo", - "deploy": "./bin/deploy.ts && yarn generate-markdown", + "deploy": "ts-node \"./bin/deploy.ts\" && yarn generate-markdown", "serve": "./bin/serve_dump.sh", "prepublishOnly": "yarn copyfiles -u 2 \"contracts/optimistic-ethereum/**/*\" ./", "postpublish": "rimraf OVM iOVM libraries mockOVM", "prepack": "yarn prepublishOnly", "postpack": "yarn postpublish", - "generate-markdown": "node scripts/generate-markdown.js" + "generate-markdown": "node \"./scripts/generate-markdown.js\"" }, "dependencies": { "@eth-optimism/core-utils": "^0.4.4", From 4e03f8a97b3c2f17a2e758323706f2a880a834d9 Mon Sep 17 00:00:00 2001 From: Karl Floersch Date: Fri, 28 May 2021 20:05:36 -0400 Subject: [PATCH 03/46] feat: add hardhat deploy instructions to readme (#965) * feat: add deployment instructions to readme * Add changeset * fix style * Update README.md --- .changeset/late-countries-guess.md | 5 ++ packages/contracts/README.md | 76 ++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 .changeset/late-countries-guess.md diff --git a/.changeset/late-countries-guess.md b/.changeset/late-countries-guess.md new file mode 100644 index 000000000000..2edddd99dad3 --- /dev/null +++ b/.changeset/late-countries-guess.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/contracts': patch +--- + +Update contracts README to add deploy instructions. diff --git a/packages/contracts/README.md b/packages/contracts/README.md index 9c2280db7813..9c7ed7b7b1cc 100644 --- a/packages/contracts/README.md +++ b/packages/contracts/README.md @@ -84,5 +84,81 @@ You can also build specific components as follows: yarn build:contracts ``` +### Deploying the Contracts +To deploy the contracts first clone, install, and build the contracts package. + +Next set the following env vars: + +```bash +CONTRACTS_TARGET_NETWORK=... +CONTRACTS_DEPLOYER_KEY=... +CONTRACTS_RPC_URL=... +``` + +Then to perform the actual deployment run: + +```bash +npx hardhat deploy \ + --network ... \ # `network` MUST equal your env var `CONTRACTS_TARGET_NETWORK` + --ovm-address-manager-owner ... \ + --ovm-proposer-address ... \ + --ovm-relayer-address ... \ + --ovm-sequencer-address ... \ + --scc-fraud-proof-window ... \ + --scc-sequencer-publish-window ... +``` + +This will deploy the contracts to the network specified in your env and create +an artifacts directory in `./deployments`. + +To view all deployment options run: + +```bash +npx hardhat deploy --help + +Hardhat version 2.2.1 + +Usage: hardhat [GLOBAL OPTIONS] deploy [--ctc-force-inclusion-period-seconds ] [--ctc-max-transaction-gas-limit ] --deploy-scripts [--em-max-gas-per-queue-per-epoch ] [--em-max-transaction-gas-limit ] [--em-min-transaction-gas-limit ] [--em-ovm-chain-id ] [--em-seconds-per-epoch ] --export --export-all --gasprice [--l1-block-time-seconds ] [--no-compile] [--no-impersonation] --ovm-address-manager-owner --ovm-proposer-address --ovm-relayer-address --ovm-sequencer-address [--reset] [--scc-fraud-proof-window ] [--scc-sequencer-publish-window ] [--silent] --tags [--watch] --write + +OPTIONS: + + --ctc-force-inclusion-period-seconds Number of seconds that the sequencer has to include transactions before the L1 queue. (default: 2592000) + --ctc-max-transaction-gas-limit Max gas limit for L1 queue transactions. (default: 9000000) + --deploy-scripts override deploy script folder path + --em-max-gas-per-queue-per-epoch Maximum gas allowed in a given queue for each epoch. (default: 250000000) + --em-max-transaction-gas-limit Maximum allowed transaction gas limit. (default: 9000000) + --em-min-transaction-gas-limit Minimum allowed transaction gas limit. (default: 50000) + --em-ovm-chain-id Chain ID for the L2 network. (default: 420) + --em-seconds-per-epoch Number of seconds in each epoch. (default: 0) + --export export current network deployments + --export-all export all deployments into one file + --gasprice gas price to use for transactions + --l1-block-time-seconds Number of seconds on average between every L1 block. (default: 15) + --no-compile disable pre compilation + --no-impersonation do not impersonate unknown accounts + --ovm-address-manager-owner Address that will own the Lib_AddressManager. Must be provided or this deployment will fail. + --ovm-proposer-address Address of the account that will propose state roots. Must be provided or this deployment will fail. + --ovm-relayer-address Address of the message relayer. Must be provided or this deployment will fail. + --ovm-sequencer-address Address of the sequencer. Must be provided or this deployment will fail. + --reset whether to delete deployments files first + --scc-fraud-proof-window Number of seconds until a transaction is considered finalized. (default: 604800) + --scc-sequencer-publish-window Number of seconds that the sequencer is exclusively allowed to post state roots. (default: 1800) + --silent whether to remove log + --tags specify which deploy script to execute via tags, separated by commas + --watch redeploy on every change of contract or deploy script + --write whether to write deployments to file + +deploy: Deploy contracts + +For global options help run: hardhat help +``` + +### Verifying Deployments on Etherscan +If you are using a network which Etherscan supports you can verify your contracts with: + +```bash +npx hardhat etherscan-verify --api-key ... --network ... +``` + ## Security Please refer to our [Security Policy](https://github.com/ethereum-optimism/.github/security/policy) for information about how to disclose security issues with this code. From a64f8161514faee540a9ed90f0c460ff5597cb95 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Fri, 28 May 2021 19:43:41 -0700 Subject: [PATCH 04/46] feat: fees v2 (#976) * l2 geth: new fee logic * l2 geth: migrate to fees package * core-utils: new fee scheme * chore: add changeset * l2geth: delete dead code * integration-tests: fix typo * integration-tests: fixes * fees: use fee scalar * lint: fix * rollup: correct gas payment comparison * fix(integration-tests): do not hardcode gas price * core-utils: update with new scheme * l2geth: refactor rollup oracle * l2geth: clean up DoEstimateGas * l2geth: implement latest scheme * tests: fix up * lint: fix * l2geth: better sycn service test * optimism: rename to TxGasLimit * fee: fix docstring * tests: fix * variables: rename * l2geth: prevent users from sending txs with too high of a fee * integration-tests: fix import * integration-tests: fix type * integration-tests: fix gas limits * lint: fix * l2geth: log error Co-authored-by: Georgios Konstantopoulos --- .changeset/fuzzy-dogs-share.md | 6 + integration-tests/test/erc20.spec.ts | 6 +- integration-tests/test/fee-payment.spec.ts | 7 +- integration-tests/test/native-eth.spec.ts | 4 +- integration-tests/test/rpc.spec.ts | 48 +++++--- l2geth/core/rollup_fee.go | 126 ------------------- l2geth/core/rollup_fee_test.go | 121 ------------------- l2geth/eth/gasprice/rollup_gasprice.go | 53 ++++---- l2geth/internal/ethapi/api.go | 24 ++-- l2geth/rollup/client.go | 2 +- l2geth/rollup/fees/rollup_fee.go | 94 +++++++++++++++ l2geth/rollup/fees/rollup_fee_test.go | 103 ++++++++++++++++ l2geth/rollup/sync_service.go | 50 ++++---- l2geth/rollup/sync_service_test.go | 7 +- packages/core-utils/README.md | 39 +++--- packages/core-utils/src/fees.ts | 79 ++---------- packages/core-utils/test/fees/fees.spec.ts | 133 ++++++++++----------- 17 files changed, 402 insertions(+), 500 deletions(-) create mode 100644 .changeset/fuzzy-dogs-share.md delete mode 100644 l2geth/core/rollup_fee.go delete mode 100644 l2geth/core/rollup_fee_test.go create mode 100644 l2geth/rollup/fees/rollup_fee.go create mode 100644 l2geth/rollup/fees/rollup_fee_test.go diff --git a/.changeset/fuzzy-dogs-share.md b/.changeset/fuzzy-dogs-share.md new file mode 100644 index 000000000000..3b890e4e9e8d --- /dev/null +++ b/.changeset/fuzzy-dogs-share.md @@ -0,0 +1,6 @@ +--- +'@eth-optimism/l2geth': patch +'@eth-optimism/core-utils': patch +--- + +Implement the next fee spec in both geth and in core-utils diff --git a/integration-tests/test/erc20.spec.ts b/integration-tests/test/erc20.spec.ts index 925ec01e6f21..1c03476592a2 100644 --- a/integration-tests/test/erc20.spec.ts +++ b/integration-tests/test/erc20.spec.ts @@ -1,5 +1,6 @@ import { Contract, ContractFactory, Wallet } from 'ethers' import { ethers } from 'hardhat' +import { TxGasLimit, TxGasPrice } from '@eth-optimism/core-utils' import chai, { expect } from 'chai' import { GWEI } from './shared/utils' import { OptimismEnv } from './shared/env' @@ -64,7 +65,10 @@ describe('Basic ERC20 interactions', async () => { const receipt = await transfer.wait() // The expected fee paid is the value returned by eth_estimateGas - const expectedFeePaid = await ERC20.estimateGas.transfer(other.address, 100) + const gasLimit = await ERC20.estimateGas.transfer(other.address, 100) + const gasPrice = await wallet.getGasPrice() + expect(gasPrice).to.deep.equal(TxGasPrice) + const expectedFeePaid = gasLimit.mul(gasPrice) // There are two events from the transfer with the first being // the ETH fee paid and the second of the value transfered (100) diff --git a/integration-tests/test/fee-payment.spec.ts b/integration-tests/test/fee-payment.spec.ts index d57718539819..3583df4493c8 100644 --- a/integration-tests/test/fee-payment.spec.ts +++ b/integration-tests/test/fee-payment.spec.ts @@ -3,7 +3,7 @@ import chaiAsPromised from 'chai-as-promised' chai.use(chaiAsPromised) import { BigNumber, utils } from 'ethers' import { OptimismEnv } from './shared/env' -import { L2GasLimit } from '@eth-optimism/core-utils' +import { TxGasLimit } from '@eth-optimism/core-utils' describe('Fee Payment Integration Tests', async () => { let env: OptimismEnv @@ -29,7 +29,7 @@ describe('Fee Payment Integration Tests', async () => { ) const executionGas = await (env.ovmEth .provider as any).send('eth_estimateExecutionGas', [tx]) - const decoded = L2GasLimit.decode(gas) + const decoded = TxGasLimit.decode(gas) expect(BigNumber.from(executionGas)).deep.eq(decoded) }) @@ -38,8 +38,7 @@ describe('Fee Payment Integration Tests', async () => { const balanceBefore = await env.l2Wallet.getBalance() expect(balanceBefore.gt(amount)) - const gas = await env.ovmEth.estimateGas.transfer(other, amount) - const tx = await env.ovmEth.transfer(other, amount, { gasPrice: 1 }) + const tx = await env.ovmEth.transfer(other, amount) const receipt = await tx.wait() expect(receipt.status).to.eq(1) diff --git a/integration-tests/test/native-eth.spec.ts b/integration-tests/test/native-eth.spec.ts index 017199d28cf6..fc11c82c8be6 100644 --- a/integration-tests/test/native-eth.spec.ts +++ b/integration-tests/test/native-eth.spec.ts @@ -45,13 +45,13 @@ describe('Native ETH Integration Tests', async () => { const amount = utils.parseEther('0.5') const addr = '0x' + '1234'.repeat(10) const gas = await env.ovmEth.estimateGas.transfer(addr, amount) - expect(gas).to.be.deep.eq(BigNumber.from(0x23284d28fe6d)) + expect(gas).to.be.deep.eq(BigNumber.from(0x0ef897216d)) }) it('Should estimate gas for ETH withdraw', async () => { const amount = utils.parseEther('0.5') const gas = await env.ovmEth.estimateGas.withdraw(amount) - expect(gas).to.be.deep.eq(BigNumber.from(0x207ad91a77b4)) + expect(gas).to.be.deep.eq(BigNumber.from(61400489396)) }) }) diff --git a/integration-tests/test/rpc.spec.ts b/integration-tests/test/rpc.spec.ts index a65dcab5dfdf..4c88cc4952a7 100644 --- a/integration-tests/test/rpc.spec.ts +++ b/integration-tests/test/rpc.spec.ts @@ -1,12 +1,13 @@ import { injectL2Context, - L2GasLimit, - roundL1GasPrice, + TxGasLimit, + TxGasPrice, + toRpcHexString, } from '@eth-optimism/core-utils' import { Wallet, BigNumber, Contract } from 'ethers' import { ethers } from 'hardhat' import chai, { expect } from 'chai' -import { sleep, l2Provider, GWEI } from './shared/utils' +import { sleep, l2Provider, l1Provider } from './shared/utils' import chaiAsPromised from 'chai-as-promised' import { OptimismEnv } from './shared/env' import { @@ -130,11 +131,25 @@ describe('Basic RPC tests', () => { const tx = { ...DEFAULT_TRANSACTION, gasLimit: 1, - gasPrice: 1, + gasPrice: TxGasPrice, } + const fee = tx.gasPrice.mul(tx.gasLimit) + const gasLimit = 59300000001 await expect(env.l2Wallet.sendTransaction(tx)).to.be.rejectedWith( - 'fee too low: 1, use at least tx.gasLimit = 33600000000001 and tx.gasPrice = 1' + `fee too low: ${fee}, use at least tx.gasLimit = ${gasLimit} and tx.gasPrice = ${TxGasPrice.toString()}` + ) + }) + + it('should reject a transaction with an incorrect gas price', async () => { + const tx = { + ...DEFAULT_TRANSACTION, + gasLimit: 1, + gasPrice: TxGasPrice.sub(1), + } + + await expect(env.l2Wallet.sendTransaction(tx)).to.be.rejectedWith( + `tx.gasPrice must be ${TxGasPrice.toString()}` ) }) @@ -198,7 +213,7 @@ describe('Basic RPC tests', () => { it('correctly exposes revert data for contract calls', async () => { const req: TransactionRequest = { ...revertingTx, - gasLimit: 934111908999999, // override gas estimation + gasLimit: 59808999999, // override gas estimation } const tx = await wallet.sendTransaction(req) @@ -221,7 +236,7 @@ describe('Basic RPC tests', () => { it('correctly exposes revert data for contract creations', async () => { const req: TransactionRequest = { ...revertingDeployTx, - gasLimit: 1051391908999999, // override gas estimation + gasLimit: 177008999999, // override gas estimation } const tx = await wallet.sendTransaction(req) @@ -311,12 +326,14 @@ describe('Basic RPC tests', () => { }) describe('eth_gasPrice', () => { - it('gas price should be 1 gwei', async () => { - expect(await provider.getGasPrice()).to.be.deep.equal(1) + it('gas price should be the fee scalar', async () => { + expect(await provider.getGasPrice()).to.be.deep.equal( + TxGasPrice.toNumber() + ) }) }) - describe('eth_estimateGas (returns the fee)', () => { + describe('eth_estimateGas (returns the scaled fee)', () => { it('gas estimation is deterministic', async () => { let lastEstimate: BigNumber for (let i = 0; i < 10; i++) { @@ -338,7 +355,7 @@ describe('Basic RPC tests', () => { to: DEFAULT_TRANSACTION.to, value: 0, }) - expect(estimate).to.be.eq(33600000119751) + expect(estimate).to.be.eq(0x0dce9004c7) }) it('should return a gas estimate that grows with the size of data', async () => { @@ -349,7 +366,6 @@ describe('Basic RPC tests', () => { for (const data of dataLen) { const tx = { to: '0x' + '1234'.repeat(10), - gasPrice: '0x1', value: '0x0', data: '0x' + '00'.repeat(data), from: '0x' + '1234'.repeat(10), @@ -359,16 +375,16 @@ describe('Basic RPC tests', () => { tx, ]) - const decoded = L2GasLimit.decode(estimate) + const decoded = TxGasLimit.decode(estimate) expect(decoded).to.deep.eq(BigNumber.from(l2Gaslimit)) expect(estimate.toString().endsWith(l2Gaslimit.toString())) + const l2GasPrice = BigNumber.from(0) // The L2GasPrice should be fetched from the L2GasPrice oracle contract, // but it does not yet exist. Use the default value for now - const l2GasPrice = BigNumber.from(1) - const expected = L2GasLimit.encode({ + const expected = TxGasLimit.encode({ data: tx.data, - l1GasPrice: roundL1GasPrice(l1GasPrice), + l1GasPrice, l2GasLimit: BigNumber.from(l2Gaslimit), l2GasPrice, }) diff --git a/l2geth/core/rollup_fee.go b/l2geth/core/rollup_fee.go deleted file mode 100644 index 17662547df22..000000000000 --- a/l2geth/core/rollup_fee.go +++ /dev/null @@ -1,126 +0,0 @@ -package core - -import ( - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/params" -) - -// overhead represents the fixed cost of batch submission of a single -// transaction in gas -const overhead uint64 = 4200 - -// hundredMillion is a constant used in the gas encoding formula -const hundredMillion uint64 = 100_000_000 - -var bigHundredMillion = new(big.Int).SetUint64(hundredMillion) - -// errInvalidGasPrice is the error returned when a user submits an incorrect gas -// price. The gas price must satisfy a particular equation depending on if it -// is a L1 gas price or a L2 gas price -var errInvalidGasPrice = errors.New("rollup fee: invalid gas price") - -// CalculateFee calculates the fee that must be paid to the Rollup sequencer, taking into -// account the cost of publishing data to L1. -// l2_gas_price * l2_gas_limit + l1_gas_price * l1_gas_used -// where the l2 gas price must satisfy the equation `x % (10**8) = 1` -// and the l1 gas price must satisfy the equation `x % (10**8) = 0` -func CalculateRollupFee(data []byte, l1GasPrice, l2GasLimit, l2GasPrice *big.Int) (*big.Int, error) { - if err := VerifyL1GasPrice(l1GasPrice); err != nil { - return nil, fmt.Errorf("invalid L1 gas price %d: %w", l1GasPrice, err) - } - if err := VerifyL2GasPrice(l2GasPrice); err != nil { - return nil, fmt.Errorf("invalid L2 gas price %d: %w", l2GasPrice, err) - } - l1GasLimit := calculateL1GasLimit(data, overhead) - l1Fee := new(big.Int).Mul(l1GasPrice, l1GasLimit) - l2Fee := new(big.Int).Mul(l2GasLimit, l2GasPrice) - fee := new(big.Int).Add(l1Fee, l2Fee) - return fee, nil -} - -// calculateL1GasLimit computes the L1 gasLimit based on the calldata and -// constant sized overhead. The overhead can be decreased as the cost of the -// batch submission goes down via contract optimizations. This will not overflow -// under standard network conditions. -func calculateL1GasLimit(data []byte, overhead uint64) *big.Int { - zeroes, ones := zeroesAndOnes(data) - zeroesCost := zeroes * params.TxDataZeroGas - onesCost := ones * params.TxDataNonZeroGasEIP2028 - gasLimit := zeroesCost + onesCost + overhead - return new(big.Int).SetUint64(gasLimit) -} - -// ceilModOneHundredMillion rounds the input integer up to the nearest modulus -// of one hundred million -func ceilModOneHundredMillion(num *big.Int) *big.Int { - if new(big.Int).Mod(num, bigHundredMillion).Cmp(common.Big0) == 0 { - return num - } - sum := new(big.Int).Add(num, bigHundredMillion) - mod := new(big.Int).Mod(num, bigHundredMillion) - return new(big.Int).Sub(sum, mod) -} - -// VerifyL1GasPrice returns an error if the number is an invalid possible L1 gas -// price -func VerifyL1GasPrice(l1GasPrice *big.Int) error { - if new(big.Int).Mod(l1GasPrice, bigHundredMillion).Cmp(common.Big0) != 0 { - return errInvalidGasPrice - } - return nil -} - -// VerifyL2GasPrice returns an error if the number is an invalid possible L2 gas -// price -func VerifyL2GasPrice(l2GasPrice *big.Int) error { - isNonZero := l2GasPrice.Cmp(common.Big0) != 0 - isNotModHundredMillion := new(big.Int).Mod(l2GasPrice, bigHundredMillion).Cmp(common.Big1) != 0 - if isNonZero && isNotModHundredMillion { - return errInvalidGasPrice - } - if l2GasPrice.Cmp(common.Big0) == 0 { - return errInvalidGasPrice - } - return nil -} - -// RoundL1GasPrice returns a ceilModOneHundredMillion where 0 -// is the identity function -func RoundL1GasPrice(gasPrice *big.Int) *big.Int { - return ceilModOneHundredMillion(gasPrice) -} - -// RoundL2GasPriceBig implements the algorithm: -// if gasPrice is 0; return 1 -// if gasPrice is 1; return 10**8 + 1 -// return ceilModOneHundredMillion(gasPrice-1)+1 -func RoundL2GasPrice(gasPrice *big.Int) *big.Int { - if gasPrice.Cmp(common.Big0) == 0 { - return big.NewInt(1) - } - if gasPrice.Cmp(common.Big1) == 0 { - return new(big.Int).Add(bigHundredMillion, common.Big1) - } - gp := new(big.Int).Sub(gasPrice, common.Big1) - mod := ceilModOneHundredMillion(gp) - return new(big.Int).Add(mod, common.Big1) -} - -func DecodeL2GasLimit(gasLimit *big.Int) *big.Int { - return new(big.Int).Mod(gasLimit, bigHundredMillion) -} - -func zeroesAndOnes(data []byte) (uint64, uint64) { - var zeroes uint64 - for _, byt := range data { - if byt == 0 { - zeroes++ - } - } - ones := uint64(len(data)) - zeroes - return zeroes, ones -} diff --git a/l2geth/core/rollup_fee_test.go b/l2geth/core/rollup_fee_test.go deleted file mode 100644 index dfe68107ad3f..000000000000 --- a/l2geth/core/rollup_fee_test.go +++ /dev/null @@ -1,121 +0,0 @@ -package core - -import ( - "errors" - "math" - "math/big" - "testing" -) - -var roundingL1GasPriceTests = map[string]struct { - input uint64 - expect uint64 -}{ - "simple": {10, pow10(8)}, - "one-over": {pow10(8) + 1, 2 * pow10(8)}, - "exact": {pow10(8), pow10(8)}, - "one-under": {pow10(8) - 1, pow10(8)}, - "small": {3, pow10(8)}, - "two": {2, pow10(8)}, - "one": {1, pow10(8)}, - "zero": {0, 0}, -} - -func TestRoundL1GasPrice(t *testing.T) { - for name, tt := range roundingL1GasPriceTests { - t.Run(name, func(t *testing.T) { - got := RoundL1GasPrice(new(big.Int).SetUint64(tt.input)) - if got.Uint64() != tt.expect { - t.Fatalf("Mismatched rounding to nearest, got %d expected %d", got, tt.expect) - } - }) - } -} - -var roundingL2GasPriceTests = map[string]struct { - input uint64 - expect uint64 -}{ - "simple": {10, pow10(8) + 1}, - "one-over": {pow10(8) + 2, 2*pow10(8) + 1}, - "exact": {pow10(8) + 1, pow10(8) + 1}, - "one-under": {pow10(8), pow10(8) + 1}, - "small": {3, pow10(8) + 1}, - "two": {2, pow10(8) + 1}, - "one": {1, pow10(8) + 1}, - "zero": {0, 1}, -} - -func TestRoundL2GasPrice(t *testing.T) { - for name, tt := range roundingL2GasPriceTests { - t.Run(name, func(t *testing.T) { - got := RoundL2GasPrice(new(big.Int).SetUint64(tt.input)) - if got.Uint64() != tt.expect { - t.Fatalf("Mismatched rounding to nearest, got %d expected %d", got, tt.expect) - } - }) - } -} - -var l1GasLimitTests = map[string]struct { - data []byte - overhead uint64 - expect *big.Int -}{ - "simple": {[]byte{}, 0, big.NewInt(0)}, - "simple-overhead": {[]byte{}, 10, big.NewInt(10)}, - "zeros": {[]byte{0x00, 0x00, 0x00, 0x00}, 10, big.NewInt(26)}, - "ones": {[]byte{0x01, 0x02, 0x03, 0x04}, 200, big.NewInt(16*4 + 200)}, -} - -func TestL1GasLimit(t *testing.T) { - for name, tt := range l1GasLimitTests { - t.Run(name, func(t *testing.T) { - got := calculateL1GasLimit(tt.data, tt.overhead) - if got.Cmp(tt.expect) != 0 { - t.Fatal("Calculated gas limit does not match") - } - }) - } -} - -var feeTests = map[string]struct { - dataLen int - l1GasPrice uint64 - l2GasLimit uint64 - l2GasPrice uint64 - err error -}{ - "simple": {100, 100_000_000, 437118, 100_000_001, nil}, - "zero-l2-gasprice": {10, 100_000_000, 196205, 0, errInvalidGasPrice}, - "one-l2-gasprice": {10, 100_000_000, 196205, 1, nil}, - "zero-l1-gasprice": {10, 0, 196205, 100_000_001, nil}, - "one-l1-gasprice": {10, 1, 23255, 23254, errInvalidGasPrice}, -} - -func TestCalculateRollupFee(t *testing.T) { - for name, tt := range feeTests { - t.Run(name, func(t *testing.T) { - data := make([]byte, 0, tt.dataLen) - l1GasPrice := new(big.Int).SetUint64(tt.l1GasPrice) - l2GasLimit := new(big.Int).SetUint64(tt.l2GasLimit) - l2GasPrice := new(big.Int).SetUint64(tt.l2GasPrice) - - fee, err := CalculateRollupFee(data, l1GasPrice, l2GasLimit, l2GasPrice) - if !errors.Is(err, tt.err) { - t.Fatalf("Cannot calculate fee: %s", err) - } - - if err == nil { - decodedGasLimit := DecodeL2GasLimit(fee) - if l2GasLimit.Cmp(decodedGasLimit) != 0 { - t.Errorf("rollup fee check failed: expected %d, got %d", l2GasLimit.Uint64(), decodedGasLimit) - } - } - }) - } -} - -func pow10(x int) uint64 { - return uint64(math.Pow10(x)) -} diff --git a/l2geth/eth/gasprice/rollup_gasprice.go b/l2geth/eth/gasprice/rollup_gasprice.go index 388161febecc..7b0a062e6c41 100644 --- a/l2geth/eth/gasprice/rollup_gasprice.go +++ b/l2geth/eth/gasprice/rollup_gasprice.go @@ -5,62 +5,55 @@ import ( "math/big" "sync" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/log" ) // RollupOracle holds the L1 and L2 gas prices for fee calculation type RollupOracle struct { - dataPrice *big.Int - executionPrice *big.Int - dataPriceLock sync.RWMutex - executionPriceLock sync.RWMutex + l1GasPrice *big.Int + l2GasPrice *big.Int + l1GasPriceLock sync.RWMutex + l2GasPriceLock sync.RWMutex } // NewRollupOracle returns an initialized RollupOracle -func NewRollupOracle(dataPrice *big.Int, executionPrice *big.Int) *RollupOracle { +func NewRollupOracle(l1GasPrice *big.Int, l2GasPrice *big.Int) *RollupOracle { return &RollupOracle{ - dataPrice: dataPrice, - executionPrice: executionPrice, + l1GasPrice: l1GasPrice, + l2GasPrice: l2GasPrice, } } // SuggestL1GasPrice returns the gas price which should be charged per byte of published // data by the sequencer. func (gpo *RollupOracle) SuggestL1GasPrice(ctx context.Context) (*big.Int, error) { - gpo.dataPriceLock.RLock() - defer gpo.dataPriceLock.RUnlock() - return gpo.dataPrice, nil + gpo.l1GasPriceLock.RLock() + defer gpo.l1GasPriceLock.RUnlock() + return gpo.l1GasPrice, nil } // SetL1GasPrice returns the current L1 gas price -func (gpo *RollupOracle) SetL1GasPrice(dataPrice *big.Int) error { - gpo.dataPriceLock.Lock() - defer gpo.dataPriceLock.Unlock() - if err := core.VerifyL1GasPrice(dataPrice); err != nil { - return err - } - gpo.dataPrice = dataPrice - log.Info("Set L1 Gas Price", "gasprice", gpo.dataPrice) +func (gpo *RollupOracle) SetL1GasPrice(gasPrice *big.Int) error { + gpo.l1GasPriceLock.Lock() + defer gpo.l1GasPriceLock.Unlock() + gpo.l1GasPrice = gasPrice + log.Info("Set L1 Gas Price", "gasprice", gpo.l1GasPrice) return nil } // SuggestL2GasPrice returns the gas price which should be charged per unit of gas // set manually by the sequencer depending on congestion func (gpo *RollupOracle) SuggestL2GasPrice(ctx context.Context) (*big.Int, error) { - gpo.executionPriceLock.RLock() - defer gpo.executionPriceLock.RUnlock() - return gpo.executionPrice, nil + gpo.l2GasPriceLock.RLock() + defer gpo.l2GasPriceLock.RUnlock() + return gpo.l2GasPrice, nil } // SetL2GasPrice returns the current L2 gas price -func (gpo *RollupOracle) SetL2GasPrice(executionPrice *big.Int) error { - gpo.executionPriceLock.Lock() - defer gpo.executionPriceLock.Unlock() - if err := core.VerifyL2GasPrice(executionPrice); err != nil { - return err - } - gpo.executionPrice = executionPrice - log.Info("Set L2 Gas Price", "gasprice", gpo.executionPrice) +func (gpo *RollupOracle) SetL2GasPrice(gasPrice *big.Int) error { + gpo.l2GasPriceLock.Lock() + defer gpo.l2GasPriceLock.Unlock() + gpo.l2GasPrice = gasPrice + log.Info("Set L2 Gas Price", "gasprice", gpo.l2GasPrice) return nil } diff --git a/l2geth/internal/ethapi/api.go b/l2geth/internal/ethapi/api.go index 2828d83707bd..648ddea679cd 100644 --- a/l2geth/internal/ethapi/api.go +++ b/l2geth/internal/ethapi/api.go @@ -45,15 +45,17 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rollup/fees" "github.com/ethereum/go-ethereum/rpc" "github.com/tyler-smith/go-bip39" ) const ( - defaultGasPrice = params.Wei + defaultGasPrice = params.Wei * fees.TxGasPrice ) var errOVMUnsupported = errors.New("OVM: Unsupported RPC Method") +var bigDefaultGasPrice = new(big.Int).SetUint64(defaultGasPrice) // PublicEthereumAPI provides an API to access Ethereum related information. // It offers only methods that operate on public data that is freely available to anyone. @@ -68,7 +70,7 @@ func NewPublicEthereumAPI(b Backend) *PublicEthereumAPI { // GasPrice always returns 1 gwei. See `DoEstimateGas` below for context. func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) { - return (*hexutil.Big)(big.NewInt(defaultGasPrice)), nil + return (*hexutil.Big)(bigDefaultGasPrice), nil } // ProtocolVersion returns the current Ethereum protocol version this node supports @@ -1037,31 +1039,27 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNrOrHash if err != nil { return 0, err } - // 2a. fetch the data price, depends on how the sequencer has chosen to update their values based on the // l1 gas prices l1GasPrice, err := b.SuggestL1GasPrice(ctx) if err != nil { return 0, err } - // 2b. fetch the execution gas price, by the typical mempool dynamics l2GasPrice, err := b.SuggestL2GasPrice(ctx) if err != nil { return 0, err } - - var data []byte - if args.Data == nil { - data = []byte{} - } else { + data := []byte{} + if args.Data != nil { data = *args.Data } - // 3. calculate the fee + // 3. calculate the fee using just the calldata. The additional overhead of + // RLP encoding is covered inside of EncodeL2GasLimit l2GasLimit := new(big.Int).SetUint64(uint64(gasUsed)) - fee, err := core.CalculateRollupFee(data, l1GasPrice, l2GasLimit, l2GasPrice) - if err != nil { - return 0, err + fee := fees.EncodeTxGasLimit(data, l1GasPrice, l2GasLimit, l2GasPrice) + if !fee.IsUint64() { + return 0, fmt.Errorf("estimate gas overflow: %s", fee) } return (hexutil.Uint64)(fee.Uint64()), nil } diff --git a/l2geth/rollup/client.go b/l2geth/rollup/client.go index b8b08222c73b..2e6402bfd4e6 100644 --- a/l2geth/rollup/client.go +++ b/l2geth/rollup/client.go @@ -570,7 +570,7 @@ func (c *Client) GetTransactionBatch(index uint64) (*Batch, []*types.Transaction Get("/batch/transaction/index/{index}") if err != nil { - return nil, nil, fmt.Errorf("Cannot get transaction batch %d", index) + return nil, nil, fmt.Errorf("Cannot get transaction batch %d: %w", index, err) } txBatch, ok := response.Result().(*TransactionBatchResponse) if !ok { diff --git a/l2geth/rollup/fees/rollup_fee.go b/l2geth/rollup/fees/rollup_fee.go new file mode 100644 index 000000000000..2f5455057e7d --- /dev/null +++ b/l2geth/rollup/fees/rollup_fee.go @@ -0,0 +1,94 @@ +package fees + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/params" +) + +// overhead represents the fixed cost of batch submission of a single +// transaction in gas. +const overhead uint64 = 4200 + 200*params.TxDataNonZeroGasEIP2028 + +// hundredMillion is a constant used in the gas encoding formula +const hundredMillion uint64 = 100_000_000 + +// feeScalar is used to scale the calculations in EncodeL2GasLimit +// to prevent them from being too large +const feeScalar uint64 = 1000 + +// TxGasPrice is a constant that determines the result of `eth_gasPrice` +// It is scaled upwards by 50% +// tx.gasPrice is hard coded to 1500 * wei and all transactions must set that +// gas price. +const TxGasPrice uint64 = feeScalar + (feeScalar / 2) + +// BigTxGasPrice is the L2GasPrice as type big.Int +var BigTxGasPrice = new(big.Int).SetUint64(TxGasPrice) +var bigFeeScalar = new(big.Int).SetUint64(feeScalar) +var bigHundredMillion = new(big.Int).SetUint64(hundredMillion) + +// EncodeTxGasLimit computes the `tx.gasLimit` based on the L1/L2 gas prices and +// the L2 gas limit. The L2 gas limit is encoded inside of the lower order bits +// of the number like so: [ | l2GasLimit ] +// [ tx.gaslimit ] +// The lower order bits must be large enough to fit the L2 gas limit, so 10**8 +// is chosen. If higher order bits collide with any bits from the L2 gas limit, +// the L2 gas limit will not be able to be decoded. +// An explicit design goal of this scheme was to make the L2 gas limit be human +// readable. The entire number is interpreted as the gas limit when computing +// the fee, so increasing the L2 Gas limit will increase the fee paid. +// The calculation is: +// l1GasLimit = zero_count(data) * 4 + non_zero_count(data) * 16 + overhead +// l1Fee = l1GasPrice * l1GasLimit +// l2Fee = l2GasPrice * l2GasLimit +// sum = l1Fee + l2Fee +// scaled = sum / scalar +// rounded = ceilmod(scaled, hundredMillion) +// result = rounded + l2GasLimit +// Note that for simplicity purposes, only the calldata is passed into this +// function when in reality the RLP encoded transaction should be. The +// additional cost is added to the overhead constant to prevent the need to RLP +// encode transactions during calls to `eth_estimateGas` +func EncodeTxGasLimit(data []byte, l1GasPrice, l2GasLimit, l2GasPrice *big.Int) *big.Int { + l1GasLimit := calculateL1GasLimit(data, overhead) + l1Fee := new(big.Int).Mul(l1GasPrice, l1GasLimit) + l2Fee := new(big.Int).Mul(l2GasPrice, l2GasLimit) + sum := new(big.Int).Add(l1Fee, l2Fee) + scaled := new(big.Int).Div(sum, bigFeeScalar) + remainder := new(big.Int).Mod(scaled, bigHundredMillion) + scaledSum := new(big.Int).Add(scaled, bigHundredMillion) + rounded := new(big.Int).Sub(scaledSum, remainder) + result := new(big.Int).Add(rounded, l2GasLimit) + return result +} + +// DecodeL2GasLimit decodes the L2 gas limit from an encoded L2 gas limit +func DecodeL2GasLimit(gasLimit *big.Int) *big.Int { + return new(big.Int).Mod(gasLimit, bigHundredMillion) +} + +// calculateL1GasLimit computes the L1 gasLimit based on the calldata and +// constant sized overhead. The overhead can be decreased as the cost of the +// batch submission goes down via contract optimizations. This will not overflow +// under standard network conditions. +func calculateL1GasLimit(data []byte, overhead uint64) *big.Int { + zeroes, ones := zeroesAndOnes(data) + zeroesCost := zeroes * params.TxDataZeroGas + onesCost := ones * params.TxDataNonZeroGasEIP2028 + gasLimit := zeroesCost + onesCost + overhead + return new(big.Int).SetUint64(gasLimit) +} + +func zeroesAndOnes(data []byte) (uint64, uint64) { + var zeroes uint64 + var ones uint64 + for _, byt := range data { + if byt == 0 { + zeroes++ + } else { + ones++ + } + } + return zeroes, ones +} diff --git a/l2geth/rollup/fees/rollup_fee_test.go b/l2geth/rollup/fees/rollup_fee_test.go new file mode 100644 index 000000000000..138a0d0bb16a --- /dev/null +++ b/l2geth/rollup/fees/rollup_fee_test.go @@ -0,0 +1,103 @@ +package fees + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/params" +) + +var l1GasLimitTests = map[string]struct { + data []byte + overhead uint64 + expect *big.Int +}{ + "simple": {[]byte{}, 0, big.NewInt(0)}, + "simple-overhead": {[]byte{}, 10, big.NewInt(10)}, + "zeros": {[]byte{0x00, 0x00, 0x00, 0x00}, 10, big.NewInt(26)}, + "ones": {[]byte{0x01, 0x02, 0x03, 0x04}, 200, big.NewInt(16*4 + 200)}, +} + +func TestL1GasLimit(t *testing.T) { + for name, tt := range l1GasLimitTests { + t.Run(name, func(t *testing.T) { + got := calculateL1GasLimit(tt.data, tt.overhead) + if got.Cmp(tt.expect) != 0 { + t.Fatal("Calculated gas limit does not match") + } + }) + } +} + +var feeTests = map[string]struct { + dataLen int + l1GasPrice uint64 + l2GasLimit uint64 + l2GasPrice uint64 +}{ + "simple": { + dataLen: 10, + l1GasPrice: params.GWei, + l2GasLimit: 437118, + l2GasPrice: params.GWei, + }, + "zero-l2-gasprice": { + dataLen: 10, + l1GasPrice: params.GWei, + l2GasLimit: 196205, + l2GasPrice: 0, + }, + "one-l2-gasprice": { + dataLen: 10, + l1GasPrice: params.GWei, + l2GasLimit: 196205, + l2GasPrice: 1, + }, + "zero-l1-gasprice": { + dataLen: 10, + l1GasPrice: 0, + l2GasLimit: 196205, + l2GasPrice: params.GWei, + }, + "one-l1-gasprice": { + dataLen: 10, + l1GasPrice: 1, + l2GasLimit: 23255, + l2GasPrice: params.GWei, + }, + "zero-gasprices": { + dataLen: 10, + l1GasPrice: 0, + l2GasLimit: 23255, + l2GasPrice: 0, + }, + "max-gaslimit": { + dataLen: 10, + l1GasPrice: params.GWei, + l2GasLimit: 99999999, + l2GasPrice: params.GWei, + }, + "larger-divisor": { + dataLen: 10, + l1GasPrice: 0, + l2GasLimit: 10, + l2GasPrice: 0, + }, +} + +func TestCalculateRollupFee(t *testing.T) { + for name, tt := range feeTests { + t.Run(name, func(t *testing.T) { + data := make([]byte, tt.dataLen) + l1GasPrice := new(big.Int).SetUint64(tt.l1GasPrice) + l2GasLimit := new(big.Int).SetUint64(tt.l2GasLimit) + l2GasPrice := new(big.Int).SetUint64(tt.l2GasPrice) + + fee := EncodeTxGasLimit(data, l1GasPrice, l2GasLimit, l2GasPrice) + decodedGasLimit := DecodeL2GasLimit(fee) + if l2GasLimit.Cmp(decodedGasLimit) != 0 { + t.Errorf("rollup fee check failed: expected %d, got %d", l2GasLimit.Uint64(), decodedGasLimit) + } + }) + } +} diff --git a/l2geth/rollup/sync_service.go b/l2geth/rollup/sync_service.go index b3f831559a79..98460aa7387a 100644 --- a/l2geth/rollup/sync_service.go +++ b/l2geth/rollup/sync_service.go @@ -21,6 +21,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/rollup/fees" ) // errShortRemoteTip is an error for when the remote tip is shorter than the @@ -445,13 +446,7 @@ func (s *SyncService) updateL2GasPrice(hash *common.Hash) error { return err } result := state.GetState(s.gpoAddress, l2GasPriceSlot) - gasPrice := result.Big() - if err := core.VerifyL2GasPrice(gasPrice); err != nil { - gp := core.RoundL2GasPrice(gasPrice) - log.Warn("Invalid gas price detected in state", "state", gasPrice, "using", gp) - gasPrice = gp - } - s.RollupGpo.SetL2GasPrice(gasPrice) + s.RollupGpo.SetL2GasPrice(result.Big()) return nil } @@ -728,11 +723,18 @@ func (s *SyncService) applyBatchedTransaction(tx *types.Transaction) error { // verifyFee will verify that a valid fee is being paid. func (s *SyncService) verifyFee(tx *types.Transaction) error { - // Exit early if fees are enforced and the gasPrice is set to 0 - if s.enforceFees && tx.GasPrice().Cmp(common.Big0) == 0 { - return errors.New("cannot accept 0 gas price transaction") + if tx.GasPrice().Cmp(common.Big0) == 0 { + // Exit early if fees are enforced and the gasPrice is set to 0 + if s.enforceFees { + return errors.New("cannot accept 0 gas price transaction") + } + // If fees are not enforced and the gas price is 0, return early + return nil + } + // When the gas price is non zero, it must be equal to the constant + if tx.GasPrice().Cmp(fees.BigTxGasPrice) != 0 { + return fmt.Errorf("tx.gasPrice must be %d", fees.TxGasPrice) } - l1GasPrice, err := s.RollupGpo.SuggestL1GasPrice(context.Background()) if err != nil { return err @@ -743,22 +745,29 @@ func (s *SyncService) verifyFee(tx *types.Transaction) error { } // Calculate the fee based on decoded L2 gas limit gas := new(big.Int).SetUint64(tx.Gas()) - l2GasLimit := core.DecodeL2GasLimit(gas) - fee, err := core.CalculateRollupFee(tx.Data(), l1GasPrice, l2GasLimit, l2GasPrice) + l2GasLimit := fees.DecodeL2GasLimit(gas) + // Only count the calldata here as the overhead of the fully encoded + // RLP transaction is handled inside of EncodeL2GasLimit + fee := fees.EncodeTxGasLimit(tx.Data(), l1GasPrice, l2GasLimit, l2GasPrice) if err != nil { return err } - // If fees are not enforced and the gas price is 0, return early - if !s.enforceFees && tx.GasPrice().Cmp(common.Big0) == 0 { - return nil - } // This should only happen if the transaction fee is greater than 18.44 ETH if !fee.IsUint64() { return fmt.Errorf("fee overflow: %s", fee.String()) } - // Make sure that the fee is paid - if tx.Gas() < fee.Uint64() { - return fmt.Errorf("fee too low: %d, use at least tx.gasLimit = %d and tx.gasPrice = 1", tx.Gas(), fee.Uint64()) + // Compute the user's fee + paying := new(big.Int).Mul(new(big.Int).SetUint64(tx.Gas()), tx.GasPrice()) + // Compute the minimum expected fee + expecting := new(big.Int).Mul(fee, fees.BigTxGasPrice) + if paying.Cmp(expecting) == -1 { + return fmt.Errorf("fee too low: %d, use at least tx.gasLimit = %d and tx.gasPrice = %d", paying, fee.Uint64(), fees.BigTxGasPrice) + } + // Protect users from overpaying by too much + overpaying := new(big.Int).Sub(paying, expecting) + threshold := new(big.Int).Mul(expecting, common.Big3) + if overpaying.Cmp(threshold) == 1 { + return fmt.Errorf("fee too large: %d", paying) } return nil } @@ -776,7 +785,6 @@ func (s *SyncService) ValidateAndApplySequencerTransaction(tx *types.Transaction if err := s.verifyFee(tx); err != nil { return err } - s.txLock.Lock() defer s.txLock.Unlock() log.Trace("Sequencer transaction validation", "hash", tx.Hash().Hex()) diff --git a/l2geth/rollup/sync_service_test.go b/l2geth/rollup/sync_service_test.go index d23f5ad304d4..705539be7fe4 100644 --- a/l2geth/rollup/sync_service_test.go +++ b/l2geth/rollup/sync_service_test.go @@ -507,7 +507,8 @@ func TestSyncServiceL1GasPrice(t *testing.T) { t.Fatal(err) } - if gasAfter.Cmp(core.RoundL1GasPrice(big.NewInt(1))) != 0 { + expect, _ := service.client.GetL1GasPrice() + if gasAfter.Cmp(expect) != 0 { t.Fatal("expected 100 gas price, got", gasAfter) } } @@ -533,7 +534,7 @@ func TestSyncServiceL2GasPrice(t *testing.T) { if err != nil { t.Fatal("Cannot get state db") } - l2GasPrice := big.NewInt(100000001) + l2GasPrice := big.NewInt(100000000000) state.SetState(service.gpoAddress, l2GasPriceSlot, common.BigToHash(l2GasPrice)) root, _ := state.Commit(false) @@ -824,7 +825,7 @@ func (m *mockClient) SyncStatus(backend Backend) (*SyncStatus, error) { } func (m *mockClient) GetL1GasPrice() (*big.Int, error) { - price := core.RoundL1GasPrice(big.NewInt(2)) + price := big.NewInt(1) return price, nil } diff --git a/packages/core-utils/README.md b/packages/core-utils/README.md index 20a6539f5293..fc01bdfdfe78 100644 --- a/packages/core-utils/README.md +++ b/packages/core-utils/README.md @@ -25,36 +25,29 @@ $ yarn lint ### L2 Fees -The Layer 2 fee is encoded in `tx.gasLimit`. The Layer 2 `gasLimit` is encoded -in the lower order bits of the `tx.gasLimit`. For this scheme to work, both the -L1 gas price and the L2 gas price must satisfy specific equations. There are -functions that help ensure that the correct gas prices are used. - -- `roundL1GasPrice` -- `roundL2GasPrice` - -The Layer 2 fee is based on both the cost of submitting the data to L1 as well -as the cost of execution on L2. To make libraries like `ethers` just work, the -return value of `eth_estimateGas` has been modified to return the fee. A new RPC -endpoint `eth_estimateExecutionGas` has been added that returns the L2 gas used. - -To locally encode the `tx.gasLimit`, the `L2GasLimit` methods `encode` and -`decode` should be used. +`TxGasLimit` can be used to `encode` and `decode` the L2 Gas Limit +locally. ```typescript -import { L2GasLimit, roundL1GasPrice, roundL2GasPrice } from '@eth-optimism/core-utils' +import { TxGasLimit } from '@eth-optimism/core-utils' import { JsonRpcProvider } from 'ethers' -const provider = new JsonRpcProvider('https://mainnet.optimism.io') -const gasLimit = await provider.send('eth_estimateExecutionGas', [tx]) +const L2Provider = new JsonRpcProvider('https://mainnet.optimism.io') +const L1Provider = new JsonRpcProvider('http://127.0.0.1:8545') -const encoded = L2GasLimit.encode({ +const l2GasLimit = await L2Provider.send('eth_estimateExecutionGas', [tx]) +const l1GasPrice = await L1Provider.getGasPrice() + +const encoded = TxGasLimit.encode({ data: '0x', - l1GasPrice: roundL1GasPrice(1), - l2GasLimit: gasLimit, - l2GasPrice: roundL2GasPrice(1), + l1GasPrice, + l2GasLimit, + l2GasPrice: 10000000, }) -const decoded = L2GasLimit.decode(encoded) +const decoded = TxGasLimit.decode(encoded) assert(decoded.eq(gasLimit)) + +const estimate = await L2Provider.estimateGas() +assert(estimate.eq(encoded)) ``` diff --git a/packages/core-utils/src/fees.ts b/packages/core-utils/src/fees.ts index cc754e5fcb81..3910d19196df 100644 --- a/packages/core-utils/src/fees.ts +++ b/packages/core-utils/src/fees.ts @@ -6,9 +6,11 @@ import { BigNumber } from 'ethers' import { remove0x } from './common' const hundredMillion = BigNumber.from(100_000_000) +const feeScalar = 1000 +export const TxGasPrice = BigNumber.from(feeScalar + feeScalar / 2) const txDataZeroGas = 4 const txDataNonZeroGasEIP2028 = 16 -const overhead = 4200 +const overhead = 4200 + 200 * txDataNonZeroGasEIP2028 export interface EncodableL2GasLimit { data: Buffer | string @@ -29,17 +31,15 @@ function encode(input: EncodableL2GasLimit): BigNumber { if (typeof l2GasPrice === 'number') { l2GasPrice = BigNumber.from(l2GasPrice) } - - if (!verifyL2GasPrice(l2GasPrice)) { - throw new Error(`Invalid L2 Gas Price: ${l2GasPrice.toString()}`) - } - if (!verifyL1GasPrice(l1GasPrice)) { - throw new Error(`Invalid L1 Gas Price: ${l1GasPrice.toString()}`) - } const l1GasLimit = calculateL1GasLimit(data) - const l1Fee = l1GasPrice.mul(l1GasLimit) + const l1Fee = l1GasLimit.mul(l1GasPrice) const l2Fee = l2GasLimit.mul(l2GasPrice) - return l1Fee.add(l2Fee) + const sum = l1Fee.add(l2Fee) + const scaled = sum.div(feeScalar) + const remainder = scaled.mod(hundredMillion) + const scaledSum = scaled.add(hundredMillion) + const rounded = scaledSum.sub(remainder) + return rounded.add(l2GasLimit) } function decode(fee: BigNumber | number): BigNumber { @@ -49,39 +49,17 @@ function decode(fee: BigNumber | number): BigNumber { return fee.mod(hundredMillion) } -export const L2GasLimit = { +export const TxGasLimit = { encode, decode, } -export function verifyL2GasPrice(gasPrice: BigNumber | number): boolean { - if (typeof gasPrice === 'number') { - gasPrice = BigNumber.from(gasPrice) - } - // If the gas price is not equal to 0 and the gas price mod - // one hundred million is not one - if (!gasPrice.eq(0) && !gasPrice.mod(hundredMillion).eq(1)) { - return false - } - if (gasPrice.eq(0)) { - return false - } - return true -} - -export function verifyL1GasPrice(gasPrice: BigNumber | number): boolean { - if (typeof gasPrice === 'number') { - gasPrice = BigNumber.from(gasPrice) - } - return gasPrice.mod(hundredMillion).eq(0) -} - -export function calculateL1GasLimit(data: string | Buffer): number { +export function calculateL1GasLimit(data: string | Buffer): BigNumber { const [zeroes, ones] = zeroesAndOnes(data) const zeroesCost = zeroes * txDataZeroGas const onesCost = ones * txDataNonZeroGasEIP2028 const gasLimit = zeroesCost + onesCost + overhead - return gasLimit + return BigNumber.from(gasLimit) } export function zeroesAndOnes(data: Buffer | string): Array { @@ -99,34 +77,3 @@ export function zeroesAndOnes(data: Buffer | string): Array { } return [zeros, ones] } - -export function roundL1GasPrice(gasPrice: BigNumber | number): BigNumber { - if (typeof gasPrice === 'number') { - gasPrice = BigNumber.from(gasPrice) - } - return ceilModOneHundredMillion(gasPrice) -} - -function ceilModOneHundredMillion(num: BigNumber): BigNumber { - if (num.mod(hundredMillion).eq(0)) { - return num - } - const sum = num.add(hundredMillion) - const mod = num.mod(hundredMillion) - return sum.sub(mod) -} - -export function roundL2GasPrice(gasPrice: BigNumber | number): BigNumber { - if (typeof gasPrice === 'number') { - gasPrice = BigNumber.from(gasPrice) - } - if (gasPrice.eq(0)) { - return BigNumber.from(1) - } - if (gasPrice.eq(1)) { - return hundredMillion.add(1) - } - const gp = gasPrice.sub(1) - const mod = ceilModOneHundredMillion(gp) - return mod.add(1) -} diff --git a/packages/core-utils/test/fees/fees.spec.ts b/packages/core-utils/test/fees/fees.spec.ts index bc548c5baa67..c624c0b3128b 100644 --- a/packages/core-utils/test/fees/fees.spec.ts +++ b/packages/core-utils/test/fees/fees.spec.ts @@ -1,6 +1,9 @@ import { expect } from '../setup' import * as fees from '../../src/fees' -import { BigNumber } from 'ethers' +import { BigNumber, utils } from 'ethers' + +const hundredBillion = 10 ** 11 +const million = 10 ** 6 describe('Fees', () => { it('should count zeros and ones', () => { @@ -18,115 +21,99 @@ describe('Fees', () => { } }) - describe('Round L1 Gas Price', () => { - const roundL1GasPriceTests = [ - { input: 10, expect: 10 ** 8, name: 'simple' }, - { input: 10 ** 8 + 1, expect: 2 * 10 ** 8, name: 'one-over' }, - { input: 10 ** 8, expect: 10 ** 8, name: 'exact' }, - { input: 10 ** 8 - 1, expect: 10 ** 8, name: 'one-under' }, - { input: 3, expect: 10 ** 8, name: 'small' }, - { input: 2, expect: 10 ** 8, name: 'two' }, - { input: 1, expect: 10 ** 8, name: 'one' }, - { input: 0, expect: 0, name: 'zero' }, - ] - - for (const test of roundL1GasPriceTests) { - it(`should pass for ${test.name} case`, () => { - const got = fees.roundL1GasPrice(test.input) - const expected = BigNumber.from(test.expect) - expect(got).to.deep.equal(expected) - }) - } - }) - - describe('Round L2 Gas Price', () => { - const roundL2GasPriceTests = [ - { input: 10, expect: 10 ** 8 + 1, name: 'simple' }, - { input: 10 ** 8 + 2, expect: 2 * 10 ** 8 + 1, name: 'one-over' }, - { input: 10 ** 8 + 1, expect: 10 ** 8 + 1, name: 'exact' }, - { input: 10 ** 8, expect: 10 ** 8 + 1, name: 'one-under' }, - { input: 3, expect: 10 ** 8 + 1, name: 'small' }, - { input: 2, expect: 10 ** 8 + 1, name: 'two' }, - { input: 1, expect: 10 ** 8 + 1, name: 'one' }, - { input: 0, expect: 1, name: 'zero' }, - ] - - for (const test of roundL2GasPriceTests) { - it(`should pass for ${test.name} case`, () => { - const got = fees.roundL2GasPrice(test.input) - const expected = BigNumber.from(test.expect) - expect(got).to.deep.equal(expected) - }) - } - }) - describe('Rollup Fees', () => { const rollupFeesTests = [ { name: 'simple', dataLen: 10, - l1GasPrice: 100_000_000, - l2GasPrice: 100_000_001, + l1GasPrice: utils.parseUnits('1', 'gwei'), + l2GasPrice: utils.parseUnits('1', 'gwei'), l2GasLimit: 437118, - error: false, + }, + { + name: 'small-gasprices-max-gaslimit', + dataLen: 10, + l1GasPrice: utils.parseUnits('1', 'wei'), + l2GasPrice: utils.parseUnits('1', 'wei'), + l2GasLimit: 0x4ffffff, + }, + { + name: 'large-gasprices-max-gaslimit', + dataLen: 10, + l1GasPrice: utils.parseUnits('1', 'ether'), + l2GasPrice: utils.parseUnits('1', 'ether'), + l2GasLimit: 0x4ffffff, + }, + { + name: 'small-gasprices-max-gaslimit', + dataLen: 10, + l1GasPrice: utils.parseUnits('1', 'ether'), + l2GasPrice: utils.parseUnits('1', 'ether'), + l2GasLimit: 1, + }, + { + name: 'max-gas-limit', + dataLen: 10, + l1GasPrice: utils.parseUnits('5', 'ether'), + l2GasPrice: utils.parseUnits('5', 'ether'), + l2GasLimit: 10 ** 8 - 1, }, { name: 'zero-l2-gasprice', dataLen: 10, - l1GasPrice: 100_000_000, + l1GasPrice: hundredBillion, l2GasPrice: 0, l2GasLimit: 196205, - error: true, }, { name: 'one-l2-gasprice', dataLen: 10, - l1GasPrice: 100_000_000, + l1GasPrice: hundredBillion, l2GasPrice: 1, l2GasLimit: 196205, - error: false, }, { name: 'zero-l1-gasprice', dataLen: 10, l1GasPrice: 0, - l2GasPrice: 100_000_001, + l2GasPrice: hundredBillion, l2GasLimit: 196205, - error: false, }, { name: 'one-l1-gasprice', dataLen: 10, l1GasPrice: 1, - l2GasPrice: 23254, + l2GasPrice: hundredBillion, + l2GasLimit: 23255, + }, + { + name: 'zero-gasprices', + dataLen: 10, + l1GasPrice: 0, + l2GasPrice: 0, l2GasLimit: 23255, - error: true, + }, + { + name: 'larger-divisor', + dataLen: 10, + l1GasPrice: 0, + l2GasLimit: 10, + l2GasPrice: 0, }, ] for (const test of rollupFeesTests) { it(`should pass for ${test.name} case`, () => { const data = Buffer.alloc(test.dataLen) + const got = fees.TxGasLimit.encode({ + data, + l1GasPrice: test.l1GasPrice, + l2GasPrice: test.l2GasPrice, + l2GasLimit: test.l2GasLimit, + }) - let got - let err = false - try { - got = fees.L2GasLimit.encode({ - data, - l1GasPrice: test.l1GasPrice, - l2GasPrice: test.l2GasPrice, - l2GasLimit: test.l2GasLimit, - }) - } catch (e) { - err = true - } - - expect(err).to.equal(test.error) - - if (!err) { - const decoded = fees.L2GasLimit.decode(got) - expect(decoded).to.deep.eq(BigNumber.from(test.l2GasLimit)) - } + const decoded = fees.TxGasLimit.decode(got) + expect(decoded).to.deep.eq(BigNumber.from(test.l2GasLimit)) }) } }) From 11a9296fabe1a54c28ccd2043bd362dc7c42720a Mon Sep 17 00:00:00 2001 From: Elena Gesheva Date: Mon, 31 May 2021 16:12:21 +0300 Subject: [PATCH 05/46] Add static analysis action (#848) * Add static analysis github action setup python and install slither * Add nvmrc file for setting node to v14.17 * Update slither command run to link missing contract packages from monorepo root * Add steps for installing dependencies * Add yarn build step to github action * Enable colour in github action for static analysis * Disable certain detectors * Ensure slither does not fail build * Add instructions on running static analysis to monorepo readme --- .github/workflows/static-analysis.yml | 61 ++++++++++++++++++++++++++ .gitignore | 3 ++ .nvmrc | 1 + README.md | 14 ++++-- packages/contracts/package.json | 3 ++ packages/contracts/slither.config.json | 12 +++++ 6 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/static-analysis.yml create mode 100644 .nvmrc create mode 100644 packages/contracts/slither.config.json diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 000000000000..9eab9a0b3de9 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,61 @@ +name: Static analysis + +on: + push: + branches: + - master + - develop + pull_request: + workflow_dispatch: + +env: + PYTEST_ADDOPTS: "--color=yes" + +jobs: + slither: + name: Slither run + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Fetch history + run: git fetch + + - name: Setup node + uses: actions/setup-node@v1 + with: + node-version: '12.x' + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install Dependencies + # only install dependencies if there was a change in the deps + # if: steps.yarn-cache.outputs.cache-hit != 'true' + run: yarn install + + - name: Build + run: yarn build + + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: '3.8' + + - name: Install Slither + run: pip3 install slither-analyzer + + - name: Run analysis + working-directory: ./packages/contracts + shell: bash + run: yarn test:slither + continue-on-error: true diff --git a/.gitignore b/.gitignore index 18b731ead5fc..45c8671678a8 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ cache-ovm l2geth/build/bin packages/contracts/deployments/custom packages/contracts/coverage* +packages/contracts/@ens* +packages/contracts/@openzeppelin* +packages/contracts/hardhat* packages/data-transport-layer/db diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000000..62df50f1eefe --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +14.17.0 diff --git a/README.md b/README.md index d7b11ea584f2..8ed492095de5 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ Extensive documentation is available [here](http://community.optimism.io/docs/) * [`message-relayer`](./packages/message-relayer): Service for relaying L2 messages to L1 * [`l2geth`](./l2geth): Fork of [go-ethereum v1.9.10](https://github.com/ethereum/go-ethereum/tree/v1.9.10) implementing the [OVM](https://research.paradigm.xyz/optimism#optimistic-geth). * [`integration-tests`](./integration-tests): Integration tests between a L1 testnet, `l2geth`, -* [`ops`](./ops): Contains Dockerfiles for containerizing each service involved in the protocol, +* [`ops`](./ops): Contains Dockerfiles for containerizing each service involved in the protocol, as well as a docker-compose file for bringing up local testnets easily ## Quickstart ### Installation -Dependency management is done using `yarn`. +Dependency management is done using `yarn`. ```bash git clone git@github.com:ethereum-optimism/optimism.git @@ -67,7 +67,7 @@ you can run `yarn lerna run test --parallel --since master` #### Running the integration tests The integration tests first require bringing up the Optimism stack. This is done via -a Docker Compose network. For better performance, we also recommend enabling Docker +a Docker Compose network. For better performance, we also recommend enabling Docker BuildKit ```bash @@ -110,3 +110,11 @@ can be hard to filter through. In order to view the logs from a specific service ``` docker-compose logs --follow ``` +### Static analysis + +To run `slither` locally in `./packages/contracts` do + +``` +pip3 install slither-analyzer +yarn test:slither +``` diff --git a/packages/contracts/package.json b/packages/contracts/package.json index e8f750f6d7a3..ec5771a978c3 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -32,6 +32,9 @@ "test:contracts": "hardhat test --show-stack-traces", "test:gas": "hardhat test \"test/contracts/OVM/execution/OVM_StateManager.gas-spec.ts\" --no-compile --show-stack-traces", "test:coverage": "NODE_OPTIONS=--max_old_space_size=8192 hardhat coverage", + "test:slither": "slither .", + "pretest:slither": "rm -f @openzeppelin && rm -f @ens && rm -f hardhat && ln -s ../../node_modules/@openzeppelin @openzeppelin && ln -s ../../node_modules/@ens @ens && ln -s ../../node_modules/hardhat hardhat", + "posttest:slither": "rm -f @openzeppelin && rm -f @ens && rm -f hardhat", "lint": "yarn lint:fix && yarn lint:check", "lint:fix": "yarn run lint:fix:typescript", "lint:fix:typescript": "prettier --config .prettierrc.json --write \"hardhat.config.ts\" \"{src,test}/**/*.ts\"", diff --git a/packages/contracts/slither.config.json b/packages/contracts/slither.config.json new file mode 100644 index 000000000000..8827f71e57ec --- /dev/null +++ b/packages/contracts/slither.config.json @@ -0,0 +1,12 @@ +{ + "detectors_to_exclude": "conformance-to-solidity-naming-conventions,assembly-usage,low-level-calls,block-timestamp", + "exclude_informational": false, + "exclude_low": false, + "exclude_medium": false, + "exclude_high": false, + "solc_disable_warnings": false, + "hardhat_ignore_compile": true, + "disable_color": false, + "exclude_dependencies": true, + "filter_paths": "@openzeppelin|hardhat|contracts/test-helpers|contracts/test-libraries" +} From 5a7984973622d1d6e610ac98cfc206ab9a3bfe1a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 May 2021 19:21:45 +0300 Subject: [PATCH 06/46] build(deps): bump ws from 7.4.4 to 7.4.6 in /ops/docker/hardhat (#987) Bumps [ws](https://github.com/websockets/ws) from 7.4.4 to 7.4.6. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/7.4.4...7.4.6) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ops/docker/hardhat/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ops/docker/hardhat/yarn.lock b/ops/docker/hardhat/yarn.lock index bad1ef0d6d31..5d7149c252ea 100644 --- a/ops/docker/hardhat/yarn.lock +++ b/ops/docker/hardhat/yarn.lock @@ -2252,9 +2252,9 @@ wrappy@1: integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= ws@^7.2.1: - version "7.4.4" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" - integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0: version "4.0.2" From 86708bb5758cd2b647b3ca2be698beb5aa3af81f Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Tue, 1 Jun 2021 16:44:53 -0400 Subject: [PATCH 07/46] feat[message-relayer]: relay tx generator (#952) * feat[message-relayer]: relay tx generator * whoops, I burned our infura key * fix minor bug * add comments * add more comments and clean stuff up * add empty test descriptions * add tests * move smock to dev deps * chore: add changeset * minor cleanup to merkle tree proof function * use bignumber math to avoid nested await * use a better interface * minor fixes and simplifications --- .changeset/sharp-files-knock.md | 5 + packages/message-relayer/hardhat.config.ts | 15 + packages/message-relayer/package.json | 17 +- packages/message-relayer/src/index.ts | 1 + packages/message-relayer/src/relay-tx.ts | 418 ++++++++++++++++++ packages/message-relayer/test/setup.ts | 12 + .../MockL2CrossDomainMessenger.sol | 44 ++ .../test/unit-tests/relay-tx.spec.ts | 377 ++++++++++++++++ yarn.lock | 58 +++ 9 files changed, 945 insertions(+), 2 deletions(-) create mode 100644 .changeset/sharp-files-knock.md create mode 100644 packages/message-relayer/hardhat.config.ts create mode 100644 packages/message-relayer/src/index.ts create mode 100644 packages/message-relayer/src/relay-tx.ts create mode 100644 packages/message-relayer/test/setup.ts create mode 100644 packages/message-relayer/test/test-contracts/MockL2CrossDomainMessenger.sol create mode 100644 packages/message-relayer/test/unit-tests/relay-tx.spec.ts diff --git a/.changeset/sharp-files-knock.md b/.changeset/sharp-files-knock.md new file mode 100644 index 000000000000..c74d2c14e4c3 --- /dev/null +++ b/.changeset/sharp-files-knock.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/message-relayer': patch +--- + +Adds a new set of tools for generating messages to be relayed and their proofs diff --git a/packages/message-relayer/hardhat.config.ts b/packages/message-relayer/hardhat.config.ts new file mode 100644 index 000000000000..fac7c21376f0 --- /dev/null +++ b/packages/message-relayer/hardhat.config.ts @@ -0,0 +1,15 @@ +import { HardhatUserConfig } from 'hardhat/config' + +import '@nomiclabs/hardhat-ethers' +import '@nomiclabs/hardhat-waffle' + +const config: HardhatUserConfig = { + paths: { + sources: './test/test-contracts', + }, + solidity: { + version: '0.7.6', + }, +} + +export default config diff --git a/packages/message-relayer/package.json b/packages/message-relayer/package.json index 20ce2406e96b..6dae8b1eb7d6 100644 --- a/packages/message-relayer/package.json +++ b/packages/message-relayer/package.json @@ -14,7 +14,8 @@ "clean": "rimraf dist/ ./tsconfig.build.tsbuildinfo", "lint": "yarn lint:fix && yarn lint:check", "lint:fix": "prettier --config .prettierrc.json --write \"{src,exec,test}/**/*.ts\"", - "lint:check": "tslint --format stylish --project ." + "lint:check": "tslint --format stylish --project .", + "test": "hardhat test --show-stack-traces" }, "keywords": [ "optimism", @@ -30,9 +31,9 @@ }, "dependencies": { "@eth-optimism/common-ts": "^0.1.2", - "bcfg": "^0.1.6", "@eth-optimism/contracts": "^0.3.3", "@eth-optimism/core-utils": "^0.4.3", + "bcfg": "^0.1.6", "dotenv": "^8.2.0", "ethers": "^5.1.0", "google-spreadsheet": "^3.1.15", @@ -40,6 +41,18 @@ "rlp": "^2.2.6" }, "devDependencies": { + "@eth-optimism/smock": "^1.1.4", + "@nomiclabs/hardhat-ethers": "^2.0.2", + "@nomiclabs/hardhat-waffle": "^2.0.1", + "@types/chai": "^4.2.18", + "@types/chai-as-promised": "^7.1.4", + "@types/mocha": "^8.2.2", + "chai": "^4.3.4", + "chai-as-promised": "^7.1.1", + "ethereum-waffle": "^3.3.0", + "hardhat": "^2.3.0", + "lodash": "^4.17.21", + "mocha": "^8.4.0", "prettier": "^2.2.1", "tslint": "^6.1.3", "tslint-config-prettier": "^1.18.0", diff --git a/packages/message-relayer/src/index.ts b/packages/message-relayer/src/index.ts new file mode 100644 index 000000000000..419b5715f12d --- /dev/null +++ b/packages/message-relayer/src/index.ts @@ -0,0 +1 @@ +export * from './relay-tx' diff --git a/packages/message-relayer/src/relay-tx.ts b/packages/message-relayer/src/relay-tx.ts new file mode 100644 index 000000000000..547006ed7735 --- /dev/null +++ b/packages/message-relayer/src/relay-tx.ts @@ -0,0 +1,418 @@ +/* Imports: External */ +import { ethers } from 'ethers' +import { + fromHexString, + remove0x, + toHexString, + toRpcHexString, +} from '@eth-optimism/core-utils' +import { getContractInterface, predeploys } from '@eth-optimism/contracts' +import * as rlp from 'rlp' +import { MerkleTree } from 'merkletreejs' + +// Number of blocks added to the L2 chain before the first L2 transaction. Genesis are added to the +// chain to initialize the system. However, they create a discrepancy between the L2 block number +// the index of the transaction that corresponds to that block number. For example, if there's 1 +// genesis block, then the transaction with an index of 0 corresponds to the block with index 1. +const NUM_L2_GENESIS_BLOCKS = 1 + +interface StateRootBatchHeader { + batchIndex: ethers.BigNumber + batchRoot: string + batchSize: ethers.BigNumber + prevTotalElements: ethers.BigNumber + extraData: string +} + +interface StateRootBatch { + header: StateRootBatchHeader + stateRoots: string[] +} + +interface CrossDomainMessage { + target: string + sender: string + message: string + messageNonce: number +} + +interface CrossDomainMessageProof { + stateRoot: string + stateRootBatchHeader: StateRootBatchHeader + stateRootProof: { + index: number + siblings: string[] + } + stateTrieWitness: string + storageTrieWitness: string +} + +interface CrossDomainMessagePair { + message: CrossDomainMessage + proof: CrossDomainMessageProof +} + +interface StateTrieProof { + accountProof: string + storageProof: string +} + +/** + * Finds all L2 => L1 messages triggered by a given L2 transaction, if the message exists. + * @param l2RpcProvider L2 RPC provider. + * @param l2CrossDomainMessengerAddress Address of the L2CrossDomainMessenger. + * @param l2TransactionHash Hash of the L2 transaction to find a message for. + * @returns Messages associated with the transaction. + */ +export const getMessagesByTransactionHash = async ( + l2RpcProvider: ethers.providers.JsonRpcProvider, + l2CrossDomainMessengerAddress: string, + l2TransactionHash: string +): Promise => { + // Complain if we can't find the given transaction. + const transaction = await l2RpcProvider.getTransaction(l2TransactionHash) + if (transaction === null) { + throw new Error(`unable to find tx with hash: ${l2TransactionHash}`) + } + + const l2CrossDomainMessenger = new ethers.Contract( + l2CrossDomainMessengerAddress, + getContractInterface('OVM_L2CrossDomainMessenger'), + l2RpcProvider + ) + + // Find all SentMessage events created in the same block as the given transaction. This is + // reliable because we should only have one transaction per block. + const sentMessageEvents = await l2CrossDomainMessenger.queryFilter( + l2CrossDomainMessenger.filters.SentMessage(), + transaction.blockNumber, + transaction.blockNumber + ) + + // Decode the messages and turn them into a nicer struct. + const sentMessages = sentMessageEvents.map((sentMessageEvent) => { + const encodedMessage = sentMessageEvent.args.message + const decodedMessage = l2CrossDomainMessenger.interface.decodeFunctionData( + 'relayMessage', + encodedMessage + ) + + return { + target: decodedMessage._target, + sender: decodedMessage._sender, + message: decodedMessage._message, + messageNonce: decodedMessage._messageNonce.toNumber(), + } + }) + + return sentMessages +} + +/** + * Encodes a cross domain message. + * @param message Message to encode. + * @returns Encoded message. + */ +const encodeCrossDomainMessage = (message: CrossDomainMessage): string => { + return getContractInterface( + 'OVM_L2CrossDomainMessenger' + ).encodeFunctionData('relayMessage', [ + message.target, + message.sender, + message.message, + message.messageNonce, + ]) +} + +/** + * Finds the StateBatchAppended event associated with a given L2 transaction. + * @param l1RpcProvider L1 RPC provider. + * @param l1StateCommitmentChainAddress Address of the L1StateCommitmentChain. + * @param l2TransactionIndex Index of the L2 transaction to find a StateBatchAppended event for. + * @returns StateBatchAppended event for the given transaction or null if no such event exists. + */ +export const getStateBatchAppendedEventByTransactionIndex = async ( + l1RpcProvider: ethers.providers.JsonRpcProvider, + l1StateCommitmentChainAddress: string, + l2TransactionIndex: number +): Promise => { + const l1StateCommitmentChain = new ethers.Contract( + l1StateCommitmentChainAddress, + getContractInterface('OVM_StateCommitmentChain'), + l1RpcProvider + ) + + const getStateBatchAppendedEventByBatchIndex = async ( + index: number + ): Promise => { + const eventQueryResult = await l1StateCommitmentChain.queryFilter( + l1StateCommitmentChain.filters.StateBatchAppended(index) + ) + if (eventQueryResult.length === 0) { + return null + } else { + return eventQueryResult[0] + } + } + + const isEventHi = (event: ethers.Event, index: number) => { + const prevTotalElements = event.args._prevTotalElements.toNumber() + return index < prevTotalElements + } + + const isEventLo = (event: ethers.Event, index: number) => { + const prevTotalElements = event.args._prevTotalElements.toNumber() + const batchSize = event.args._batchSize.toNumber() + return index >= prevTotalElements + batchSize + } + + const totalBatches: ethers.BigNumber = await l1StateCommitmentChain.getTotalBatches() + if (totalBatches.eq(0)) { + return null + } + + let lowerBound = 0 + let upperBound = totalBatches.toNumber() - 1 + let batchEvent: ethers.Event | null = await getStateBatchAppendedEventByBatchIndex( + upperBound + ) + + if (isEventLo(batchEvent, l2TransactionIndex)) { + // Upper bound is too low, means this transaction doesn't have a corresponding state batch yet. + return null + } else if (!isEventHi(batchEvent, l2TransactionIndex)) { + // Upper bound is not too low and also not too high. This means the upper bound event is the + // one we're looking for! Return it. + return batchEvent + } + + // Binary search to find the right event. The above checks will guarantee that the event does + // exist and that we'll find it during this search. + while (lowerBound < upperBound) { + const middleOfBounds = Math.floor((lowerBound + upperBound) / 2) + batchEvent = await getStateBatchAppendedEventByBatchIndex(middleOfBounds) + + if (isEventHi(batchEvent, l2TransactionIndex)) { + upperBound = middleOfBounds + } else if (isEventLo(batchEvent, l2TransactionIndex)) { + lowerBound = middleOfBounds + } else { + break + } + } + + return batchEvent +} + +/** + * Finds the full state root batch associated with a given transaction index. + * @param l1RpcProvider L1 RPC provider. + * @param l1StateCommitmentChainAddress Address of the L1StateCommitmentChain. + * @param l2TransactionIndex Index of the L2 transaction to find a state root batch for. + * @returns State root batch associated with the given transaction index or null if no state root + * batch exists. + */ +export const getStateRootBatchByTransactionIndex = async ( + l1RpcProvider: ethers.providers.JsonRpcProvider, + l1StateCommitmentChainAddress: string, + l2TransactionIndex: number +): Promise => { + const l1StateCommitmentChain = new ethers.Contract( + l1StateCommitmentChainAddress, + getContractInterface('OVM_StateCommitmentChain'), + l1RpcProvider + ) + + const stateBatchAppendedEvent = await getStateBatchAppendedEventByTransactionIndex( + l1RpcProvider, + l1StateCommitmentChainAddress, + l2TransactionIndex + ) + if (stateBatchAppendedEvent === null) { + return null + } + + const stateBatchTransaction = await stateBatchAppendedEvent.getTransaction() + const [stateRoots] = l1StateCommitmentChain.interface.decodeFunctionData( + 'appendStateBatch', + stateBatchTransaction.data + ) + + return { + header: { + batchIndex: stateBatchAppendedEvent.args._batchIndex, + batchRoot: stateBatchAppendedEvent.args._batchRoot, + batchSize: stateBatchAppendedEvent.args._batchSize, + prevTotalElements: stateBatchAppendedEvent.args._prevTotalElements, + extraData: stateBatchAppendedEvent.args._extraData, + }, + stateRoots, + } +} + +/** + * Generates a Merkle proof (using the particular scheme we use within Lib_MerkleTree). + * @param leaves Leaves of the merkle tree. + * @param index Index to generate a proof for. + * @returns Merkle proof sibling leaves, as hex strings. + */ +const getMerkleTreeProof = (leaves: string[], index: number): string[] => { + // Our specific Merkle tree implementation requires that the number of leaves is a power of 2. + // If the number of given leaves is less than a power of 2, we need to round up to the next + // available power of 2. We fill the remaining space with the hash of bytes32(0). + const correctedTreeSize = Math.pow(2, Math.ceil(Math.log2(leaves.length))) + const parsedLeaves = [] + for (let i = 0; i < correctedTreeSize; i++) { + if (i < leaves.length) { + parsedLeaves.push(leaves[i]) + } else { + parsedLeaves.push(ethers.utils.keccak256('0x' + '00'.repeat(32))) + } + } + + // merkletreejs prefers things to be Buffers. + const bufLeaves = parsedLeaves.map(fromHexString) + const tree = new MerkleTree( + bufLeaves, + (el: Buffer | string): Buffer => { + return fromHexString(ethers.utils.keccak256(el)) + } + ) + + const proof = tree.getProof(bufLeaves[index], index).map((element: any) => { + return toHexString(element.data) + }) + + return proof +} + +/** + * Generates a Merkle-Patricia trie proof for a given account and storage slot. + * @param l2RpcProvider L2 RPC provider. + * @param blockNumber Block number to generate the proof at. + * @param address Address to generate the proof for. + * @param slot Storage slot to generate the proof for. + * @returns Account proof and storage proof. + */ +const getStateTrieProof = async ( + l2RpcProvider: ethers.providers.JsonRpcProvider, + blockNumber: number, + address: string, + slot: string +): Promise => { + const proof = await l2RpcProvider.send('eth_getProof', [ + address, + [slot], + toRpcHexString(blockNumber), + ]) + + return { + accountProof: toHexString(rlp.encode(proof.accountProof)), + storageProof: toHexString(rlp.encode(proof.storageProof[0].proof)), + } +} + +/** + * Finds all L2 => L1 messages sent in a given L2 transaction and generates proofs for each of + * those messages. + * @param l1RpcProvider L1 RPC provider. + * @param l2RpcProvider L2 RPC provider. + * @param l1StateCommitmentChainAddress Address of the StateCommitmentChain. + * @param l2CrossDomainMessengerAddress Address of the L2CrossDomainMessenger. + * @param l2TransactionHash L2 transaction hash to generate a relay transaction for. + * @returns An array of messages sent in the transaction and a proof of inclusion for each. + */ +export const getMessagesAndProofsForL2Transaction = async ( + l1RpcProvider: ethers.providers.JsonRpcProvider, + l2RpcProvider: ethers.providers.JsonRpcProvider, + l1StateCommitmentChainAddress: string, + l2CrossDomainMessengerAddress: string, + l2TransactionHash: string +): Promise => { + const l2Transaction = await l2RpcProvider.getTransaction(l2TransactionHash) + if (l2Transaction === null) { + throw new Error(`unable to find tx with hash: ${l2TransactionHash}`) + } + + // Need to find the state batch for the given transaction. If no state batch has been published + // yet then we will not be able to generate a proof. + const batch = await getStateRootBatchByTransactionIndex( + l1RpcProvider, + l1StateCommitmentChainAddress, + l2Transaction.blockNumber - NUM_L2_GENESIS_BLOCKS + ) + if (batch === null) { + throw new Error( + `unable to find state root batch for tx with hash: ${l2TransactionHash}` + ) + } + + // Adjust the transaction index based on the number of L2 genesis block we have. "Index" here + // refers to the position of the transaction within the *Canonical Transaction Chain*. + const l2TransactionIndex = l2Transaction.blockNumber - NUM_L2_GENESIS_BLOCKS + + // Here the index refers to the position of the state root that corresponds to this transaction + // within the batch of state roots in which that state root was published. + const txIndexInBatch = + l2TransactionIndex - batch.header.prevTotalElements.toNumber() + + // Find every message that was sent during this transaction. We'll then attach a proof for each. + const messages = await getMessagesByTransactionHash( + l2RpcProvider, + l2CrossDomainMessengerAddress, + l2TransactionHash + ) + + const messagePairs: CrossDomainMessagePair[] = [] + for (const message of messages) { + // We need to calculate the specific storage slot that demonstrates that this message was + // actually included in the L2 chain. The following calculation is based on the fact that + // messages are stored in the following mapping on L2: + // https://github.com/ethereum-optimism/optimism/blob/c84d3450225306abbb39b4e7d6d82424341df2be/packages/contracts/contracts/optimistic-ethereum/OVM/predeploys/OVM_L2ToL1MessagePasser.sol#L23 + // You can read more about how Solidity storage slots are computed for mappings here: + // https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#mappings-and-dynamic-arrays + const messageSlot = ethers.utils.keccak256( + ethers.utils.keccak256( + encodeCrossDomainMessage(message) + + remove0x(l2CrossDomainMessengerAddress) + ) + '00'.repeat(32) + ) + + // We need a Merkle trie proof for the given storage slot. This allows us to prove to L1 that + // the message was actually sent on L2. + const stateTrieProof = await getStateTrieProof( + l2RpcProvider, + l2Transaction.blockNumber, + predeploys.OVM_L2ToL1MessagePasser, + messageSlot + ) + + // State roots are published in batches to L1 and correspond 1:1 to transactions. We compute a + // Merkle root for these state roots so that we only need to store the minimum amount of + // information on-chain. So we need to create a Merkle proof for the specific state root that + // corresponds to this transaction. + const stateRootMerkleProof = getMerkleTreeProof( + batch.stateRoots, + txIndexInBatch + ) + + // We now have enough information to create the message proof. + const proof: CrossDomainMessageProof = { + stateRoot: batch.stateRoots[txIndexInBatch], + stateRootBatchHeader: batch.header, + stateRootProof: { + index: txIndexInBatch, + siblings: stateRootMerkleProof, + }, + stateTrieWitness: stateTrieProof.accountProof, + storageTrieWitness: stateTrieProof.storageProof, + } + + messagePairs.push({ + message, + proof, + }) + } + + return messagePairs +} diff --git a/packages/message-relayer/test/setup.ts b/packages/message-relayer/test/setup.ts new file mode 100644 index 000000000000..f5952afdbe6b --- /dev/null +++ b/packages/message-relayer/test/setup.ts @@ -0,0 +1,12 @@ +/* External Imports */ +import chai = require('chai') +import Mocha from 'mocha' +import { solidity } from 'ethereum-waffle' +import chaiAsPromised from 'chai-as-promised' + +chai.use(solidity) +chai.use(chaiAsPromised) +const should = chai.should() +const expect = chai.expect + +export { should, expect, Mocha } diff --git a/packages/message-relayer/test/test-contracts/MockL2CrossDomainMessenger.sol b/packages/message-relayer/test/test-contracts/MockL2CrossDomainMessenger.sol new file mode 100644 index 000000000000..7545691229b8 --- /dev/null +++ b/packages/message-relayer/test/test-contracts/MockL2CrossDomainMessenger.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity >0.7.0 <0.9.0; +pragma experimental ABIEncoderV2; + +contract MockL2CrossDomainMessenger { + struct MessageData { + address target; + address sender; + bytes message; + uint256 messageNonce; + } + + event SentMessage(bytes message); + + function emitSentMessageEvent( + MessageData memory _message + ) + public + { + emit SentMessage( + abi.encodeWithSignature( + "relayMessage(address,address,bytes,uint256)", + _message.target, + _message.sender, + _message.message, + _message.messageNonce + ) + ); + } + + function emitMultipleSentMessageEvents( + MessageData[] memory _messages + ) + public + { + for (uint256 i = 0; i < _messages.length; i++) { + emitSentMessageEvent( + _messages[i] + ); + } + } + + function doNothing() public {} +} diff --git a/packages/message-relayer/test/unit-tests/relay-tx.spec.ts b/packages/message-relayer/test/unit-tests/relay-tx.spec.ts new file mode 100644 index 000000000000..1745a620f5f2 --- /dev/null +++ b/packages/message-relayer/test/unit-tests/relay-tx.spec.ts @@ -0,0 +1,377 @@ +import { expect } from '../setup' + +/* Imports: External */ +import hre from 'hardhat' +import { Contract, Signer } from 'ethers' +import { getContractFactory } from '@eth-optimism/contracts' +import { smockit } from '@eth-optimism/smock' +import { toPlainObject } from 'lodash' + +/* Imports: Internal */ +import { + getMessagesAndProofsForL2Transaction, + getStateRootBatchByTransactionIndex, + getStateBatchAppendedEventByTransactionIndex, + getMessagesByTransactionHash, +} from '../../src/relay-tx' + +describe('relay transaction generation functions', () => { + const ethers = (hre as any).ethers + const l1RpcProvider = ethers.provider + const l2RpcProvider = ethers.provider + + let signer1: Signer + before(async () => { + ;[signer1] = await ethers.getSigners() + }) + + let MockL2CrossDomainMessenger: Contract + beforeEach(async () => { + const factory = await ethers.getContractFactory( + 'MockL2CrossDomainMessenger' + ) + MockL2CrossDomainMessenger = await factory.deploy() + }) + + let StateCommitmentChain: Contract + beforeEach(async () => { + const factory1 = getContractFactory('Lib_AddressManager') + const factory2 = getContractFactory('OVM_ChainStorageContainer') + const factory3 = getContractFactory('OVM_StateCommitmentChain') + + const mockBondManager = await smockit(getContractFactory('OVM_BondManager')) + const mockCanonicalTransactionChain = await smockit( + getContractFactory('OVM_CanonicalTransactionChain') + ) + + mockBondManager.smocked.isCollateralized.will.return.with(true) + mockCanonicalTransactionChain.smocked.getTotalElements.will.return.with( + 999999 + ) + + const AddressManager = await factory1.connect(signer1).deploy() + const ChainStorageContainer = await factory2 + .connect(signer1) + .deploy(AddressManager.address, 'OVM_StateCommitmentChain') + StateCommitmentChain = await factory3 + .connect(signer1) + .deploy(AddressManager.address, 0, 0) + + await AddressManager.setAddress( + 'OVM_ChainStorageContainer:SCC:batches', + ChainStorageContainer.address + ) + + await AddressManager.setAddress( + 'OVM_StateCommitmentChain', + StateCommitmentChain.address + ) + + await AddressManager.setAddress('OVM_BondManager', mockBondManager.address) + + await AddressManager.setAddress( + 'OVM_CanonicalTransactionChain', + mockCanonicalTransactionChain.address + ) + }) + + describe('getMessageByTransactionHash', () => { + it('should throw an error if a transaction with the given hash does not exist', async () => { + await expect( + getMessagesByTransactionHash( + l2RpcProvider, + MockL2CrossDomainMessenger.address, + ethers.constants.HashZero + ) + ).to.be.rejected + }) + + it('should return null if the transaction did not emit a SentMessage event', async () => { + const tx = await MockL2CrossDomainMessenger.doNothing() + + expect( + await getMessagesByTransactionHash( + l2RpcProvider, + MockL2CrossDomainMessenger.address, + tx.hash + ) + ).to.deep.equal([]) + }) + + it('should return the parsed event if the transaction emitted exactly one SentMessage event', async () => { + const message = { + target: ethers.constants.AddressZero, + sender: ethers.constants.AddressZero, + message: '0x', + messageNonce: 0, + } + const tx = await MockL2CrossDomainMessenger.emitSentMessageEvent(message) + + expect( + await getMessagesByTransactionHash( + l2RpcProvider, + MockL2CrossDomainMessenger.address, + tx.hash + ) + ).to.deep.equal([message]) + }) + + it('should return the parsed events if the transaction emitted more than one SentMessage event', async () => { + const messages = [ + { + target: ethers.constants.AddressZero, + sender: ethers.constants.AddressZero, + message: '0x', + messageNonce: 0, + }, + { + target: ethers.constants.AddressZero, + sender: ethers.constants.AddressZero, + message: '0x', + messageNonce: 1, + }, + ] + + const tx = await MockL2CrossDomainMessenger.emitMultipleSentMessageEvents( + messages + ) + + expect( + await getMessagesByTransactionHash( + l2RpcProvider, + MockL2CrossDomainMessenger.address, + tx.hash + ) + ).to.deep.equal(messages) + }) + }) + + describe('getStateBatchAppendedEventByTransactionIndex', () => { + it('should return null when there are no batches yet', async () => { + expect( + await getStateBatchAppendedEventByTransactionIndex( + l1RpcProvider, + StateCommitmentChain.address, + 0 + ) + ).to.equal(null) + }) + + it('should return null if a batch for the index does not exist', async () => { + // Should have a total of 1 element now. + await StateCommitmentChain.appendStateBatch( + [ethers.constants.HashZero], + 0 + ) + + expect( + await getStateBatchAppendedEventByTransactionIndex( + l1RpcProvider, + StateCommitmentChain.address, + 1 // Index 0 is ok but 1 should return null + ) + ).to.equal(null) + }) + + it('should return the batch if the index is part of the first batch', async () => { + // 5 elements + await StateCommitmentChain.appendStateBatch( + [ + ethers.constants.HashZero, + ethers.constants.HashZero, + ethers.constants.HashZero, + ethers.constants.HashZero, + ethers.constants.HashZero, + ], + 0 + ) + + // Add another 5 so we have two batches and can isolate tests against the first. + await StateCommitmentChain.appendStateBatch( + [ + ethers.constants.HashZero, + ethers.constants.HashZero, + ethers.constants.HashZero, + ethers.constants.HashZero, + ethers.constants.HashZero, + ], + 5 + ) + + const event = await getStateBatchAppendedEventByTransactionIndex( + l1RpcProvider, + StateCommitmentChain.address, + 1 + ) + + expect(toPlainObject(event.args)).to.deep.include({ + _batchIndex: ethers.BigNumber.from(0), + _batchSize: ethers.BigNumber.from(5), + _prevTotalElements: ethers.BigNumber.from(0), + }) + }) + + it('should return the batch if the index is part of the last batch', async () => { + // 5 elements + await StateCommitmentChain.appendStateBatch( + [ + ethers.constants.HashZero, + ethers.constants.HashZero, + ethers.constants.HashZero, + ethers.constants.HashZero, + ethers.constants.HashZero, + ], + 0 + ) + + // Add another 5 so we have two batches and can isolate tests against the second. + await StateCommitmentChain.appendStateBatch( + [ + ethers.constants.HashZero, + ethers.constants.HashZero, + ethers.constants.HashZero, + ethers.constants.HashZero, + ethers.constants.HashZero, + ], + 5 + ) + + const event = await getStateBatchAppendedEventByTransactionIndex( + l1RpcProvider, + StateCommitmentChain.address, + 7 + ) + + expect(toPlainObject(event.args)).to.deep.include({ + _batchIndex: ethers.BigNumber.from(1), + _batchSize: ethers.BigNumber.from(5), + _prevTotalElements: ethers.BigNumber.from(5), + }) + }) + + for (const numBatches of [1, 2, 8]) { + const elementsPerBatch = 8 + describe(`when there are ${numBatches} batch(es) of ${elementsPerBatch} elements each`, () => { + const totalElements = numBatches * elementsPerBatch + beforeEach(async () => { + for (let i = 0; i < numBatches; i++) { + await StateCommitmentChain.appendStateBatch( + new Array(elementsPerBatch).fill(ethers.constants.HashZero), + i * elementsPerBatch + ) + } + }) + + for (let i = 0; i < totalElements; i += elementsPerBatch) { + it(`should be able to get the correct event for the ${i}th/st/rd/whatever element`, async () => { + const event = await getStateBatchAppendedEventByTransactionIndex( + l1RpcProvider, + StateCommitmentChain.address, + i + ) + + expect(toPlainObject(event.args)).to.deep.include({ + _batchIndex: ethers.BigNumber.from(i / elementsPerBatch), + _batchSize: ethers.BigNumber.from(elementsPerBatch), + _prevTotalElements: ethers.BigNumber.from(i), + }) + }) + } + }) + } + }) + + describe('getStateRootBatchByTransactionIndex', () => { + it('should return null if a batch for the index does not exist', async () => { + // Should have a total of 1 element now. + await StateCommitmentChain.appendStateBatch( + [ethers.constants.HashZero], + 0 + ) + + expect( + await getStateRootBatchByTransactionIndex( + l1RpcProvider, + StateCommitmentChain.address, + 1 // Index 0 is ok but 1 should return null + ) + ).to.equal(null) + }) + + it('should return the full batch for a given index when it exists', async () => { + // Should have a total of 1 element now. + await StateCommitmentChain.appendStateBatch( + [ethers.constants.HashZero], + 0 + ) + + const batch = await getStateRootBatchByTransactionIndex( + l1RpcProvider, + StateCommitmentChain.address, + 0 // Index 0 is ok but 1 should return null + ) + + expect(batch.header).to.deep.include({ + batchIndex: ethers.BigNumber.from(0), + batchSize: ethers.BigNumber.from(1), + prevTotalElements: ethers.BigNumber.from(0), + }) + + expect(batch.stateRoots).to.deep.equal([ethers.constants.HashZero]) + }) + }) + + describe('makeRelayTransactionData', () => { + it('should throw an error if the transaction does not exist', async () => { + await expect( + getMessagesAndProofsForL2Transaction( + l1RpcProvider, + l2RpcProvider, + StateCommitmentChain.address, + MockL2CrossDomainMessenger.address, + ethers.constants.HashZero + ) + ).to.be.rejected + }) + + it('should throw an error if the transaction did not send a message', async () => { + const tx = await MockL2CrossDomainMessenger.doNothing() + + await expect( + getMessagesAndProofsForL2Transaction( + l1RpcProvider, + l2RpcProvider, + StateCommitmentChain.address, + MockL2CrossDomainMessenger.address, + tx.hash + ) + ).to.be.rejected + }) + + it('should throw an error if the corresponding state batch has not been submitted', async () => { + const tx = await MockL2CrossDomainMessenger.emitSentMessageEvent({ + target: ethers.constants.AddressZero, + sender: ethers.constants.AddressZero, + message: '0x', + messageNonce: 0, + }) + + await expect( + getMessagesAndProofsForL2Transaction( + l1RpcProvider, + l2RpcProvider, + StateCommitmentChain.address, + MockL2CrossDomainMessenger.address, + tx.hash + ) + ).to.be.rejected + }) + + // Unfortunately this is hard to test here because hardhat doesn't support eth_getProof. + // Because this function is embedded into the message relayer, we should be able to use + // integration tests to sufficiently test this. + it.skip('should otherwise return the encoded transaction data', () => { + // TODO? + }) + }) +}) diff --git a/yarn.lock b/yarn.lock index eaf60064a1bc..9c0b57790d9f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2248,6 +2248,13 @@ dependencies: "@types/chai" "*" +"@types/chai-as-promised@^7.1.4": + version "7.1.4" + resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.4.tgz#caf64e76fb056b8c8ced4b761ed499272b737601" + integrity sha512-1y3L1cHePcIm5vXkh1DSGf/zQq5n5xDKG1fpCvf18+uOkpce0Z1ozNFPkyWsVswK7ntN1sZBw3oU6gmN+pDUcA== + dependencies: + "@types/chai" "*" + "@types/chai@*", "@types/chai@^4.1.7": version "4.2.16" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.16.tgz#f09cc36e18d28274f942e7201147cce34d97e8c8" @@ -7178,6 +7185,57 @@ hardhat@^2.2.1: uuid "^3.3.2" ws "^7.2.1" +hardhat@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.3.0.tgz#5c29f8b4d08155c3dc8c908af9713fd5079522d5" + integrity sha512-nc4ro2bM4wPaA6/0Y22o5F5QrifQk2KCyPUUKLPUeFFZoGNGYB8vmeW/k9gV9DdMukdWTzfYlKc2Jn4bfb6tDQ== + dependencies: + "@ethereumjs/block" "^3.2.1" + "@ethereumjs/blockchain" "^5.2.1" + "@ethereumjs/common" "^2.2.0" + "@ethereumjs/tx" "^3.1.3" + "@ethereumjs/vm" "^5.3.2" + "@sentry/node" "^5.18.1" + "@solidity-parser/parser" "^0.11.0" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "^5.1.0" + abort-controller "^3.0.0" + adm-zip "^0.4.16" + ansi-escapes "^4.3.0" + chalk "^2.4.2" + chokidar "^3.4.0" + ci-info "^2.0.0" + debug "^4.1.1" + enquirer "^2.3.0" + env-paths "^2.2.0" + eth-sig-util "^2.5.2" + ethereum-cryptography "^0.1.2" + ethereumjs-abi "^0.6.8" + ethereumjs-util "^7.0.10" + find-up "^2.1.0" + fp-ts "1.19.3" + fs-extra "^7.0.1" + glob "^7.1.3" + immutable "^4.0.0-rc.12" + io-ts "1.10.4" + lodash "^4.17.11" + merkle-patricia-tree "^4.1.0" + mnemonist "^0.38.0" + mocha "^7.1.2" + node-fetch "^2.6.0" + qs "^6.7.0" + raw-body "^2.4.1" + resolve "1.17.0" + semver "^6.3.0" + slash "^3.0.0" + solc "0.7.3" + source-map-support "^0.5.13" + stacktrace-parser "^0.1.10" + "true-case-path" "^2.2.1" + tsort "0.0.1" + uuid "^3.3.2" + ws "^7.2.1" + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" From 1293825cee3bd79ec3cd66a985b77febcbf66824 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Tue, 1 Jun 2021 16:12:33 -0700 Subject: [PATCH 08/46] backwards compatible dtl syncing (#986) * kovan: fix attempt * kovan: db fix * kovan: types are strings from db * l2geth: parse things as strings * chore: add changeset * dtl: also stringify the range query * geth: dereference * geth: assign err * dtl: handle null * dtl: fix unit tests --- .changeset/nasty-dots-grow.md | 6 ++++ l2geth/rollup/client.go | 4 +-- .../src/db/transport-db.ts | 29 +++++++++++++++++-- .../handlers/sequencer-batch-appended.ts | 6 ++-- .../handlers/transaction-enqueued.ts | 2 +- .../l2-ingestion/handlers/transaction.ts | 4 +-- .../src/types/database-types.ts | 4 +-- .../src/types/event-handler-types.ts | 2 +- .../handlers/sequencer-batch-appended.spec.ts | 2 +- .../handlers/transaction-enqueued.spec.ts | 4 +-- 10 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 .changeset/nasty-dots-grow.md diff --git a/.changeset/nasty-dots-grow.md b/.changeset/nasty-dots-grow.md new file mode 100644 index 000000000000..417ca35c1d1f --- /dev/null +++ b/.changeset/nasty-dots-grow.md @@ -0,0 +1,6 @@ +--- +'@eth-optimism/l2geth': patch +'@eth-optimism/data-transport-layer': patch +--- + +Fix gasLimit overflow diff --git a/l2geth/rollup/client.go b/l2geth/rollup/client.go index 2e6402bfd4e6..4a1a0f5b14e5 100644 --- a/l2geth/rollup/client.go +++ b/l2geth/rollup/client.go @@ -69,7 +69,7 @@ type transaction struct { BlockNumber uint64 `json:"blockNumber"` Timestamp uint64 `json:"timestamp"` Value hexutil.Uint64 `json:"value"` - GasLimit uint64 `json:"gasLimit"` + GasLimit uint64 `json:"gasLimit,string"` Target common.Address `json:"target"` Origin *common.Address `json:"origin"` Data hexutil.Bytes `json:"data"` @@ -84,7 +84,7 @@ type Enqueue struct { Index *uint64 `json:"ctcIndex"` Target *common.Address `json:"target"` Data *hexutil.Bytes `json:"data"` - GasLimit *uint64 `json:"gasLimit"` + GasLimit *uint64 `json:"gasLimit,string"` Origin *common.Address `json:"origin"` BlockNumber *uint64 `json:"blockNumber"` Timestamp *uint64 `json:"timestamp"` diff --git a/packages/data-transport-layer/src/db/transport-db.ts b/packages/data-transport-layer/src/db/transport-db.ts index be80e6314b55..777ba4423dde 100644 --- a/packages/data-transport-layer/src/db/transport-db.ts +++ b/packages/data-transport-layer/src/db/transport-db.ts @@ -379,8 +379,9 @@ export class TransportDB { if (index === null) { return null } - - return this.db.get(`${key}:index`, index) + let entry = await this.db.get(`${key}:index`, index) + entry = stringify(entry) + return entry } private async _getEntries( @@ -388,6 +389,28 @@ export class TransportDB { startIndex: number, endIndex: number ): Promise { - return this.db.range(`${key}:index`, startIndex, endIndex) + const entries = await this.db.range( + `${key}:index`, + startIndex, + endIndex + ) + const results = [] + for (const entry of entries) { + results.push(stringify(entry)) + } + return results + } +} + +function stringify(entry) { + if (entry === null || entry === undefined) { + return entry + } + if (entry.gasLimit) { + entry.gasLimit = BigNumber.from(entry.gasLimit).toString() + } + if (entry.decoded) { + entry.decoded.gasLimit = BigNumber.from(entry.decoded.gasLimit).toString() } + return entry } diff --git a/packages/data-transport-layer/src/services/l1-ingestion/handlers/sequencer-batch-appended.ts b/packages/data-transport-layer/src/services/l1-ingestion/handlers/sequencer-batch-appended.ts index 71b483f4dcde..23230efd02fe 100644 --- a/packages/data-transport-layer/src/services/l1-ingestion/handlers/sequencer-batch-appended.ts +++ b/packages/data-transport-layer/src/services/l1-ingestion/handlers/sequencer-batch-appended.ts @@ -69,7 +69,7 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet< submitter: l1Transaction.from, l1TransactionHash: l1Transaction.hash, l1TransactionData: l1Transaction.data, - gasLimit: SEQUENCER_GAS_LIMIT, + gasLimit: `${SEQUENCER_GAS_LIMIT}`, prevTotalElements: batchSubmissionEvent.args._prevTotalElements, batchIndex: batchSubmissionEvent.args._batchIndex, @@ -115,7 +115,7 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet< batchIndex: extraData.batchIndex.toNumber(), blockNumber: BigNumber.from(context.blockNumber).toNumber(), timestamp: BigNumber.from(context.timestamp).toNumber(), - gasLimit: BigNumber.from(extraData.gasLimit).toNumber(), + gasLimit: BigNumber.from(extraData.gasLimit).toString(), target: SEQUENCER_ENTRYPOINT_ADDRESS, origin: null, data: toHexString(sequencerTransaction), @@ -147,7 +147,7 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet< batchIndex: extraData.batchIndex.toNumber(), blockNumber: BigNumber.from(0).toNumber(), timestamp: BigNumber.from(0).toNumber(), - gasLimit: BigNumber.from(0).toNumber(), + gasLimit: BigNumber.from(0).toString(), target: constants.AddressZero, origin: constants.AddressZero, data: '0x', diff --git a/packages/data-transport-layer/src/services/l1-ingestion/handlers/transaction-enqueued.ts b/packages/data-transport-layer/src/services/l1-ingestion/handlers/transaction-enqueued.ts index e343a7e78fbf..7e6bcb9b2b66 100644 --- a/packages/data-transport-layer/src/services/l1-ingestion/handlers/transaction-enqueued.ts +++ b/packages/data-transport-layer/src/services/l1-ingestion/handlers/transaction-enqueued.ts @@ -17,7 +17,7 @@ export const handleEventsTransactionEnqueued: EventHandlerSet< index: event.args._queueIndex.toNumber(), target: event.args._target, data: event.args._data, - gasLimit: event.args._gasLimit.toNumber(), + gasLimit: event.args._gasLimit.toString(), origin: event.args._l1TxOrigin, blockNumber: BigNumber.from(event.blockNumber).toNumber(), timestamp: event.args._timestamp.toNumber(), diff --git a/packages/data-transport-layer/src/services/l2-ingestion/handlers/transaction.ts b/packages/data-transport-layer/src/services/l2-ingestion/handlers/transaction.ts index 0022afc285d7..be127cc2d0dd 100644 --- a/packages/data-transport-layer/src/services/l2-ingestion/handlers/transaction.ts +++ b/packages/data-transport-layer/src/services/l2-ingestion/handlers/transaction.ts @@ -57,7 +57,7 @@ export const handleSequencerBlock = { transactionEntry = { ...transactionEntry, - gasLimit: SEQUENCER_GAS_LIMIT, // ? + gasLimit: `${SEQUENCER_GAS_LIMIT}`, // ? target: SEQUENCER_ENTRYPOINT_ADDRESS, origin: null, data: serialize( @@ -82,7 +82,7 @@ export const handleSequencerBlock = { } else { transactionEntry = { ...transactionEntry, - gasLimit: BigNumber.from(transaction.gas).toNumber(), + gasLimit: BigNumber.from(transaction.gas).toString(), target: ethers.utils.getAddress(transaction.to), origin: ethers.utils.getAddress(transaction.l1TxOrigin), data: transaction.input, diff --git a/packages/data-transport-layer/src/types/database-types.ts b/packages/data-transport-layer/src/types/database-types.ts index 32e52288ee4f..470b402c3590 100644 --- a/packages/data-transport-layer/src/types/database-types.ts +++ b/packages/data-transport-layer/src/types/database-types.ts @@ -16,7 +16,7 @@ export interface EnqueueEntry { index: number target: string data: string - gasLimit: number + gasLimit: string origin: string blockNumber: number timestamp: number @@ -28,7 +28,7 @@ export interface TransactionEntry { data: string blockNumber: number timestamp: number - gasLimit: number + gasLimit: string target: string origin: string value: string diff --git a/packages/data-transport-layer/src/types/event-handler-types.ts b/packages/data-transport-layer/src/types/event-handler-types.ts index 8b2e190b6efd..82bf5bf7f198 100644 --- a/packages/data-transport-layer/src/types/event-handler-types.ts +++ b/packages/data-transport-layer/src/types/event-handler-types.ts @@ -40,7 +40,7 @@ export interface SequencerBatchAppendedExtraData { submitter: string l1TransactionData: string l1TransactionHash: string - gasLimit: number + gasLimit: string // Stuff from TransactionBatchAppended. prevTotalElements: BigNumber diff --git a/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/sequencer-batch-appended.spec.ts b/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/sequencer-batch-appended.spec.ts index 0a6690bf832e..813f3c65c63a 100644 --- a/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/sequencer-batch-appended.spec.ts +++ b/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/sequencer-batch-appended.spec.ts @@ -18,7 +18,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.SequencerBatchAppended', submitter: '0xfd7d4de366850c08ee2cba32d851385a3071ec8d', l1TransactionHash: '0x6effe006836b841205ace4d99d7ae1b74ee96aac499a3f358b97fccd32ee9af2', - gasLimit: 548976, + gasLimit: '548976', prevTotalElements: BigNumber.from(73677), batchIndex: BigNumber.from(743), batchSize: BigNumber.from(101), diff --git a/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/transaction-enqueued.spec.ts b/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/transaction-enqueued.spec.ts index 52e5da27739e..b14d6ba505b9 100644 --- a/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/transaction-enqueued.spec.ts +++ b/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/transaction-enqueued.spec.ts @@ -93,7 +93,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', () } }) - it('should have a gasLimit equal to the integer value of the _gasLimit argument', () => { + it('should have a gasLimit equal to the string value of the _gasLimit argument', () => { for ( let i = 0; i < Number.MAX_SAFE_INTEGER; @@ -113,7 +113,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', () const output1 = handleEventsTransactionEnqueued.parseEvent(...input1) - const expected1 = BigNumber.from(i).toNumber() + const expected1 = BigNumber.from(i).toString() expect(output1).to.have.property('gasLimit', expected1) } From 5e3c5d1c65ef3352947989ba9f51a1047bf7ba0c Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Tue, 1 Jun 2021 21:01:58 -0400 Subject: [PATCH 09/46] fix[smock]: fix broken call assertions for overloaded functions (#996) * fix[smock]: fix broken call assertions for overloaded functions * chore: add changeset * minor correction and add a test * add a test for non-overloaded functions --- .changeset/cool-baboons-guess.md | 5 ++ packages/smock/src/smockit/smockit.ts | 11 ++- .../test/contracts/TestHelpers_MockCaller.sol | 8 +++ .../test/smockit/call-assertions.spec.ts | 72 +++++++++++++++++++ 4 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 .changeset/cool-baboons-guess.md create mode 100644 packages/smock/test/contracts/TestHelpers_MockCaller.sol create mode 100644 packages/smock/test/smockit/call-assertions.spec.ts diff --git a/.changeset/cool-baboons-guess.md b/.changeset/cool-baboons-guess.md new file mode 100644 index 000000000000..aa0ea6cea3e9 --- /dev/null +++ b/.changeset/cool-baboons-guess.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/smock': patch +--- + +Fixes a bug that would break call assertions for overloaded smocked functions diff --git a/packages/smock/src/smockit/smockit.ts b/packages/smock/src/smockit/smockit.ts index 9ca98ba8c087..1673064518aa 100644 --- a/packages/smock/src/smockit/smockit.ts +++ b/packages/smock/src/smockit/smockit.ts @@ -79,18 +79,25 @@ const smockifyFunction = ( let data: any = toHexString(calldataBuf) try { - data = contract.interface.decodeFunctionData(fragment.name, data) + data = contract.interface.decodeFunctionData( + fragment.format(), + data + ) } catch (e) { console.error(e) } return { functionName: fragment.name, + functionSignature: fragment.format(), data, } }) .filter((functionResult: any) => { - return functionResult.functionName === functionName + return ( + functionResult.functionName === functionName || + functionResult.functionSignature === functionName + ) }) .map((functionResult: any) => { return functionResult.data diff --git a/packages/smock/test/contracts/TestHelpers_MockCaller.sol b/packages/smock/test/contracts/TestHelpers_MockCaller.sol new file mode 100644 index 000000000000..818f3d77087a --- /dev/null +++ b/packages/smock/test/contracts/TestHelpers_MockCaller.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract TestHelpers_MockCaller { + function callMock(address _target, bytes memory _data) public { + _target.call(_data); + } +} diff --git a/packages/smock/test/smockit/call-assertions.spec.ts b/packages/smock/test/smockit/call-assertions.spec.ts new file mode 100644 index 000000000000..f4c920410669 --- /dev/null +++ b/packages/smock/test/smockit/call-assertions.spec.ts @@ -0,0 +1,72 @@ +/* Imports: External */ +import hre from 'hardhat' +import { expect } from 'chai' +import { Contract } from 'ethers' + +/* Imports: Internal */ +import { MockContract, smockit } from '../../src' + +describe('[smock]: call assertion tests', () => { + const ethers = (hre as any).ethers + + let mock: MockContract + beforeEach(async () => { + mock = await smockit('TestHelpers_BasicReturnContract') + }) + + let mockCaller: Contract + before(async () => { + const mockCallerFactory = await ethers.getContractFactory( + 'TestHelpers_MockCaller' + ) + mockCaller = await mockCallerFactory.deploy() + }) + + describe('call assertions for functions', () => { + it('should be able to make assertions about a non-overloaded function', async () => { + mock.smocked.getInputtedUint256.will.return.with(0) + + const expected1 = ethers.BigNumber.from(1234) + await mockCaller.callMock( + mock.address, + mock.interface.encodeFunctionData('getInputtedUint256(uint256)', [ + expected1, + ]) + ) + + expect(mock.smocked.getInputtedUint256.calls[0]).to.deep.equal([ + expected1, + ]) + }) + + it('should be able to make assertions about both versions of an overloaded function', async () => { + mock.smocked['overloadedFunction(uint256)'].will.return.with(0) + mock.smocked['overloadedFunction(uint256,uint256)'].will.return.with(0) + + const expected1 = ethers.BigNumber.from(1234) + await mockCaller.callMock( + mock.address, + mock.interface.encodeFunctionData('overloadedFunction(uint256)', [ + expected1, + ]) + ) + + expect( + mock.smocked['overloadedFunction(uint256)'].calls[0] + ).to.deep.equal([expected1]) + + const expected2 = ethers.BigNumber.from(5678) + await mockCaller.callMock( + mock.address, + mock.interface.encodeFunctionData( + 'overloadedFunction(uint256,uint256)', + [expected2, expected2] + ) + ) + + expect( + mock.smocked['overloadedFunction(uint256,uint256)'].calls[0] + ).to.deep.equal([expected2, expected2]) + }) + }) +}) From 064c03afb0afef9a7151ef71d441c918266633ac Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Tue, 1 Jun 2021 22:19:16 -0400 Subject: [PATCH 10/46] fix[message-relayer]: remove spreadsheet mode (#998) * fix[message-relayer]: remove spreadsheet mode * chore: add changeset --- .changeset/ten-spiders-boil.md | 5 + packages/message-relayer/package.json | 2 - packages/message-relayer/src/exec/run.ts | 31 ----- packages/message-relayer/src/service.ts | 139 +++++++------------- packages/message-relayer/src/spreadsheet.ts | 25 ---- yarn.lock | 113 +--------------- 6 files changed, 55 insertions(+), 260 deletions(-) create mode 100644 .changeset/ten-spiders-boil.md delete mode 100644 packages/message-relayer/src/spreadsheet.ts diff --git a/.changeset/ten-spiders-boil.md b/.changeset/ten-spiders-boil.md new file mode 100644 index 000000000000..757827799789 --- /dev/null +++ b/.changeset/ten-spiders-boil.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/message-relayer': patch +--- + +Removes spreadsheet mode from the message relayer diff --git a/packages/message-relayer/package.json b/packages/message-relayer/package.json index 6dae8b1eb7d6..7cd1a0444edf 100644 --- a/packages/message-relayer/package.json +++ b/packages/message-relayer/package.json @@ -1,7 +1,6 @@ { "name": "@eth-optimism/message-relayer", "version": "0.1.3", - "private": true, "description": "[Optimism] Cross Domain Message Relayer service", "main": "dist/index", "types": "dist/index", @@ -36,7 +35,6 @@ "bcfg": "^0.1.6", "dotenv": "^8.2.0", "ethers": "^5.1.0", - "google-spreadsheet": "^3.1.15", "merkletreejs": "^0.2.18", "rlp": "^2.2.6" }, diff --git a/packages/message-relayer/src/exec/run.ts b/packages/message-relayer/src/exec/run.ts index c12c67d09215..0ee3a0abeefc 100644 --- a/packages/message-relayer/src/exec/run.ts +++ b/packages/message-relayer/src/exec/run.ts @@ -1,7 +1,6 @@ import { Wallet, providers } from 'ethers' import { MessageRelayerService } from '../service' import { Bcfg } from '@eth-optimism/core-utils' -import SpreadSheet from '../spreadsheet' import * as dotenv from 'dotenv' import Config from 'bcfg' @@ -49,18 +48,6 @@ const main = async () => { parseInt(env.FROM_L2_TRANSACTION_INDEX, 10) || 0 ) - // Spreadsheet configuration - const SPREADSHEET_MODE = config.bool( - 'spreadsheet-mode', - !!env.SPREADSHEET_MODE || false - ) - const SHEET_ID = config.str('sheet-id', env.SHEET_ID) - const CLIENT_EMAIL = config.str('client-email', env.CLIENT_EMAIL) - const CLIENT_PRIVATE_KEY = config.str( - 'client-private-key', - env.CLIENT_PRIVATE_KEY - ) - if (!ADDRESS_MANAGER_ADDRESS) { throw new Error('Must pass ADDRESS_MANAGER_ADDRESS') } @@ -84,22 +71,6 @@ const main = async () => { throw new Error('Must pass one of L1_WALLET_KEY or MNEMONIC') } - let spreadsheet = null - if (SPREADSHEET_MODE) { - if (!SHEET_ID) { - throw new Error('Must pass SHEET_ID') - } - if (!CLIENT_EMAIL) { - throw new Error('Must pass CLIENT_EMAIL') - } - if (!CLIENT_PRIVATE_KEY) { - throw new Error('Must pass CLIENT_PRIVATE_KEY') - } - const privateKey = CLIENT_PRIVATE_KEY.replace(/\\n/g, '\n') - spreadsheet = new SpreadSheet(SHEET_ID) - await spreadsheet.init(CLIENT_EMAIL, privateKey) - } - const service = new MessageRelayerService({ l1RpcProvider: l1Provider, l2RpcProvider: l2Provider, @@ -111,8 +82,6 @@ const main = async () => { l2BlockOffset: L2_BLOCK_OFFSET, l1StartOffset: L1_START_OFFSET, getLogsInterval: GET_LOGS_INTERVAL, - spreadsheetMode: !!SPREADSHEET_MODE, - spreadsheet, }) await service.start() diff --git a/packages/message-relayer/src/service.ts b/packages/message-relayer/src/service.ts index 9568f3e1ea23..1851dc7f0005 100644 --- a/packages/message-relayer/src/service.ts +++ b/packages/message-relayer/src/service.ts @@ -6,7 +6,6 @@ import { MerkleTree } from 'merkletreejs' /* Imports: Internal */ import { fromHexString, sleep } from '@eth-optimism/core-utils' import { BaseService } from '@eth-optimism/common-ts' -import SpreadSheet from './spreadsheet' import { loadContract, loadContractFromManager } from '@eth-optimism/contracts' import { StateRootBatchHeader, SentMessage, SentMessageProof } from './types' @@ -41,10 +40,6 @@ interface MessageRelayerOptions { // Number of blocks within each getLogs query - max is 2000 getLogsInterval?: number - - // Append txs to a spreadsheet instead of submitting transactions - spreadsheetMode?: boolean - spreadsheet?: SpreadSheet } const optionSettings = { @@ -54,7 +49,6 @@ const optionSettings = { l2BlockOffset: { default: 1 }, l1StartOffset: { default: 0 }, getLogsInterval: { default: 2000 }, - spreadsheetMode: { default: false }, } export class MessageRelayerService extends BaseService { @@ -62,9 +56,6 @@ export class MessageRelayerService extends BaseService { super('Message_Relayer', options, optionSettings) } - protected spreadsheetMode: boolean - protected spreadsheet: SpreadSheet - private state: { lastFinalizedTxHeight: number nextUnfinalizedTxHeight: number @@ -84,7 +75,6 @@ export class MessageRelayerService extends BaseService { pollingInterval: this.options.pollingInterval, l2BlockOffset: this.options.l2BlockOffset, getLogsInterval: this.options.getLogsInterval, - spreadSheetMode: this.options.spreadsheetMode, }) // Need to improve this, sorry. this.state = {} as any @@ -141,10 +131,6 @@ export class MessageRelayerService extends BaseService { this.logger.info('Connected to all contracts.') - if (this.options.spreadsheetMode) { - this.logger.info('Running in spreadsheet mode') - } - this.state.lastQueriedL1Block = this.options.l1StartOffset this.state.eventCache = [] @@ -494,67 +480,12 @@ export class MessageRelayerService extends BaseService { message: SentMessage, proof: SentMessageProof ): Promise { - if (this.options.spreadsheetMode) { - try { - await this.options.spreadsheet.addRow({ - target: message.target, - sender: message.sender, - message: message.message, - messageNonce: message.messageNonce.toString(), - encodedMessage: message.encodedMessage, - encodedMessageHash: message.encodedMessageHash, - parentTransactionIndex: message.parentTransactionIndex, - parentTransactionHash: message.parentTransactionIndex, - stateRoot: proof.stateRoot, - batchIndex: proof.stateRootBatchHeader.batchIndex.toString(), - batchRoot: proof.stateRootBatchHeader.batchRoot, - batchSize: proof.stateRootBatchHeader.batchSize.toString(), - prevTotalElements: proof.stateRootBatchHeader.prevTotalElements.toString(), - extraData: proof.stateRootBatchHeader.extraData, - index: proof.stateRootProof.index, - siblings: proof.stateRootProof.siblings.join(','), - stateTrieWitness: proof.stateTrieWitness.toString('hex'), - storageTrieWitness: proof.storageTrieWitness.toString('hex'), - }) - this.logger.info('Submitted relay message to spreadsheet') - } catch (e) { - this.logger.error('Cannot submit message to spreadsheet') - this.logger.error(e.message) - } - } else { - try { - this.logger.info( - 'Dry-run, checking to make sure proof would succeed...' - ) - - await this.state.OVM_L1CrossDomainMessenger.connect( - this.options.l1Wallet - ).callStatic.relayMessage( - message.target, - message.sender, - message.message, - message.messageNonce, - proof, - { - gasLimit: this.options.relayGasLimit, - } - ) + try { + this.logger.info('Dry-run, checking to make sure proof would succeed...') - this.logger.info( - 'Proof should succeed. Submitting for real this time...' - ) - } catch (err) { - this.logger.error('Proof would fail, skipping', { - message: err.toString(), - stack: err.stack, - code: err.code, - }) - return - } - - const result = await this.state.OVM_L1CrossDomainMessenger.connect( + await this.state.OVM_L1CrossDomainMessenger.connect( this.options.l1Wallet - ).relayMessage( + ).callStatic.relayMessage( message.target, message.sender, message.message, @@ -565,29 +496,51 @@ export class MessageRelayerService extends BaseService { } ) - this.logger.info('Relay message transaction sent', { - transactionHash: result, + this.logger.info('Proof should succeed. Submitting for real this time...') + } catch (err) { + this.logger.error('Proof would fail, skipping', { + message: err.toString(), + stack: err.stack, + code: err.code, }) + return + } - try { - const receipt = await result.wait() - - this.logger.info('Relay message included in block', { - transactionHash: receipt.transactionHash, - blockNumber: receipt.blockNumber, - gasUsed: receipt.gasUsed.toString(), - confirmations: receipt.confirmations, - status: receipt.status, - }) - } catch (err) { - this.logger.error('Real relay attempt failed, skipping.', { - message: err.toString(), - stack: err.stack, - code: err.code, - }) - return + const result = await this.state.OVM_L1CrossDomainMessenger.connect( + this.options.l1Wallet + ).relayMessage( + message.target, + message.sender, + message.message, + message.messageNonce, + proof, + { + gasLimit: this.options.relayGasLimit, } - this.logger.info('Message successfully relayed to Layer 1!') + ) + + this.logger.info('Relay message transaction sent', { + transactionHash: result, + }) + + try { + const receipt = await result.wait() + + this.logger.info('Relay message included in block', { + transactionHash: receipt.transactionHash, + blockNumber: receipt.blockNumber, + gasUsed: receipt.gasUsed.toString(), + confirmations: receipt.confirmations, + status: receipt.status, + }) + } catch (err) { + this.logger.error('Real relay attempt failed, skipping.', { + message: err.toString(), + stack: err.stack, + code: err.code, + }) + return } + this.logger.info('Message successfully relayed to Layer 1!') } } diff --git a/packages/message-relayer/src/spreadsheet.ts b/packages/message-relayer/src/spreadsheet.ts deleted file mode 100644 index bf1260c88746..000000000000 --- a/packages/message-relayer/src/spreadsheet.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { GoogleSpreadsheet } from 'google-spreadsheet' - -export default class SpreadSheet { - public doc - public sheet - - constructor(id) { - this.doc = new GoogleSpreadsheet(id) - this.sheet = null - } - - async init(email, privateKey) { - await this.doc.useServiceAccountAuth({ - client_email: email, - private_key: privateKey, - }) - - await this.doc.loadInfo() - this.sheet = this.doc.sheetsByIndex[0] - } - - async addRow(row) { - return this.sheet.addRow(row) - } -} diff --git a/yarn.lock b/yarn.lock index 9c0b57790d9f..678b6dfabc8f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2960,7 +2960,7 @@ arrify@^1.0.0, arrify@^1.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= -arrify@^2.0.0, arrify@^2.0.1: +arrify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== @@ -3608,7 +3608,7 @@ base-x@^3.0.2, base-x@^3.0.8: dependencies: safe-buffer "^5.0.1" -base64-js@^1.3.0, base64-js@^1.3.1: +base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -3901,11 +3901,6 @@ bsert@~0.0.10: resolved "https://registry.yarnpkg.com/bsert/-/bsert-0.0.10.tgz#231ac82873a1418c6ade301ab5cd9ae385895597" integrity sha512-NHNwlac+WPy4t2LoNh8pXk8uaIGH3NSaIUbTTRXGpE2WEbq0te/tDykYHkFK57YKLPjv/aGHmbqvnGeVWDz57Q== -buffer-equal-constant-time@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" - integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= - buffer-from@^1.0.0, buffer-from@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -5241,13 +5236,6 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" - integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== - dependencies: - safe-buffer "^5.0.1" - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -6213,7 +6201,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.2, extend@~3.0.2: +extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -6305,11 +6293,6 @@ fast-safe-stringify@^2.0.7: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== -fast-text-encoding@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" - integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== - fastq@^1.6.0: version "1.11.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" @@ -6707,25 +6690,6 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -gaxios@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.2.0.tgz#33bdc4fc241fc33b8915a4b8c07cfb368b932e46" - integrity sha512-Ms7fNifGv0XVU+6eIyL9LB7RVESeML9+cMvkwGS70xyD6w2Z80wl6RiqiJ9k1KFlJCUTQqFFc8tXmPQfSKUe8g== - dependencies: - abort-controller "^3.0.0" - extend "^3.0.2" - https-proxy-agent "^5.0.0" - is-stream "^2.0.0" - node-fetch "^2.3.0" - -gcp-metadata@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-4.2.1.tgz#31849fbcf9025ef34c2297c32a89a1e7e9f2cd62" - integrity sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw== - dependencies: - gaxios "^4.0.0" - json-bigint "^1.0.0" - get-caller-file@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" @@ -6978,37 +6942,6 @@ globby@^11.0.0, globby@^11.0.2: merge2 "^1.3.0" slash "^3.0.0" -google-auth-library@^6.1.3: - version "6.1.6" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-6.1.6.tgz#deacdcdb883d9ed6bac78bb5d79a078877fdf572" - integrity sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ== - dependencies: - arrify "^2.0.0" - base64-js "^1.3.0" - ecdsa-sig-formatter "^1.0.11" - fast-text-encoding "^1.0.0" - gaxios "^4.0.0" - gcp-metadata "^4.2.0" - gtoken "^5.0.4" - jws "^4.0.0" - lru-cache "^6.0.0" - -google-p12-pem@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-3.0.3.tgz#673ac3a75d3903a87f05878f3c75e06fc151669e" - integrity sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA== - dependencies: - node-forge "^0.10.0" - -google-spreadsheet@^3.1.15: - version "3.1.15" - resolved "https://registry.yarnpkg.com/google-spreadsheet/-/google-spreadsheet-3.1.15.tgz#e7a86f750d8166faaa3e16929561baceb807bf5a" - integrity sha512-S5477f3Gf3Mz6AXgCw7dbaYnzu5aHou1AX4sDqrGboQWnAytkxqJGKQiXN+zzRTTcYzSTJCe0g7KqCPZO9xiOw== - dependencies: - axios "^0.21.1" - google-auth-library "^6.1.3" - lodash "^4.17.20" - got@9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -7061,15 +6994,6 @@ growl@1.10.5: resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== -gtoken@^5.0.4: - version "5.2.1" - resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.2.1.tgz#4dae1fea17270f457954b4a45234bba5fc796d16" - integrity sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw== - dependencies: - gaxios "^4.0.0" - google-p12-pem "^3.0.3" - jws "^4.0.0" - handlebars@^4.0.1, handlebars@^4.7.6: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" @@ -8114,13 +8038,6 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= -json-bigint@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" - integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== - dependencies: - bignumber.js "^9.0.0" - json-buffer@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" @@ -8254,23 +8171,6 @@ just-extend@^4.0.2: resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== -jwa@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" - integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== - dependencies: - buffer-equal-constant-time "1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jws@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" - integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== - dependencies: - jwa "^2.0.0" - safe-buffer "^5.0.1" - keccak@3.0.1, keccak@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.1.tgz#ae30a0e94dbe43414f741375cff6d64c8bea0bff" @@ -9745,7 +9645,7 @@ node-fetch@2.1.2: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U= -node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1: +node-fetch@^2.6.0, node-fetch@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== @@ -9758,11 +9658,6 @@ node-fetch@~1.7.1: encoding "^0.1.11" is-stream "^1.0.1" -node-forge@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" - integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== - node-gyp-build@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" From 8082d16c306aab1e53a019ecbce7038d2f1415ca Mon Sep 17 00:00:00 2001 From: Kevin Ho Date: Tue, 1 Jun 2021 19:21:01 -0700 Subject: [PATCH 11/46] Lower local rollup timestamp refresh (#985) * update rollup timestamp refresh * increase refresh time to 5s --- ops/docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/ops/docker-compose.yml b/ops/docker-compose.yml index 2470c5e9dc8a..4acad64c523d 100644 --- a/ops/docker-compose.yml +++ b/ops/docker-compose.yml @@ -80,6 +80,7 @@ services: - ./envs/geth.env environment: ETH1_HTTP: http://l1_chain:8545 + ROLLUP_TIMESTAMP_REFRESH: 5s ROLLUP_STATE_DUMP_PATH: http://deployer:8081/state-dump.latest.json # used for getting the addresses URL: http://deployer:8081/addresses.json From c2b6e14bc470d71957928d51937293404d5e1a25 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Tue, 1 Jun 2021 22:22:23 -0700 Subject: [PATCH 12/46] feat: fees v3 (#999) * core-utils: fee impl v3 * l2geth: fees v3 impl * integration-tests: update for fees v3 * chore: add changeset * fix: typo * integration-tests: fix and generalize * fees: update fee scalar * l2geth: check gas in the mempool behind usingovm * tests: fix up * l2geth: remove dead var * truffle: fix config --- .changeset/smooth-ears-pay.md | 7 ++++ examples/truffle/truffle-config-ovm.js | 3 +- integration-tests/test/fee-payment.spec.ts | 8 ++-- integration-tests/test/native-eth.spec.ts | 4 +- integration-tests/test/rpc.spec.ts | 9 +++-- l2geth/core/tx_pool.go | 17 ++++---- l2geth/core/types/transaction.go | 3 +- l2geth/internal/ethapi/api.go | 12 +++++- l2geth/rollup/fees/rollup_fee.go | 47 +++++++++++++++------- l2geth/rollup/fees/rollup_fee_test.go | 5 ++- packages/core-utils/src/fees.ts | 32 +++++++++++---- packages/core-utils/test/fees/fees.spec.ts | 5 ++- 12 files changed, 106 insertions(+), 46 deletions(-) create mode 100644 .changeset/smooth-ears-pay.md diff --git a/.changeset/smooth-ears-pay.md b/.changeset/smooth-ears-pay.md new file mode 100644 index 000000000000..9df7b7cc9289 --- /dev/null +++ b/.changeset/smooth-ears-pay.md @@ -0,0 +1,7 @@ +--- +'@eth-optimism/integration-tests': patch +'@eth-optimism/l2geth': patch +'@eth-optimism/core-utils': patch +--- + +Implement the latest fee spec such that the L2 gas limit is scaled and the tx.gasPrice/tx.gasLimit show correctly in metamask diff --git a/examples/truffle/truffle-config-ovm.js b/examples/truffle/truffle-config-ovm.js index b5dc2736725c..b7b849d43ee4 100644 --- a/examples/truffle/truffle-config-ovm.js +++ b/examples/truffle/truffle-config-ovm.js @@ -17,6 +17,7 @@ module.exports = { host: '127.0.0.1', port: 8545, gasPrice: 0, + gas: 54180127, } }, compilers: { @@ -31,4 +32,4 @@ module.exports = { } } } -} \ No newline at end of file +} diff --git a/integration-tests/test/fee-payment.spec.ts b/integration-tests/test/fee-payment.spec.ts index 3583df4493c8..f7de4d3611a2 100644 --- a/integration-tests/test/fee-payment.spec.ts +++ b/integration-tests/test/fee-payment.spec.ts @@ -3,7 +3,7 @@ import chaiAsPromised from 'chai-as-promised' chai.use(chaiAsPromised) import { BigNumber, utils } from 'ethers' import { OptimismEnv } from './shared/env' -import { TxGasLimit } from '@eth-optimism/core-utils' +import { TxGasLimit, TxGasPrice } from '@eth-optimism/core-utils' describe('Fee Payment Integration Tests', async () => { let env: OptimismEnv @@ -13,9 +13,9 @@ describe('Fee Payment Integration Tests', async () => { env = await OptimismEnv.new() }) - it('Should return a gasPrice of 1 wei', async () => { + it(`Should return a gasPrice of ${TxGasPrice.toString()} wei`, async () => { const gasPrice = await env.l2Wallet.getGasPrice() - expect(gasPrice.eq(1)) + expect(gasPrice).to.deep.eq(TxGasPrice) }) it('Should estimateGas with recoverable L2 gasLimit', async () => { @@ -28,7 +28,7 @@ describe('Fee Payment Integration Tests', async () => { utils.parseEther('0.5') ) const executionGas = await (env.ovmEth - .provider as any).send('eth_estimateExecutionGas', [tx]) + .provider as any).send('eth_estimateExecutionGas', [tx, true]) const decoded = TxGasLimit.decode(gas) expect(BigNumber.from(executionGas)).deep.eq(decoded) }) diff --git a/integration-tests/test/native-eth.spec.ts b/integration-tests/test/native-eth.spec.ts index fc11c82c8be6..68c27f52a639 100644 --- a/integration-tests/test/native-eth.spec.ts +++ b/integration-tests/test/native-eth.spec.ts @@ -45,13 +45,13 @@ describe('Native ETH Integration Tests', async () => { const amount = utils.parseEther('0.5') const addr = '0x' + '1234'.repeat(10) const gas = await env.ovmEth.estimateGas.transfer(addr, amount) - expect(gas).to.be.deep.eq(BigNumber.from(0x0ef897216d)) + expect(gas).to.be.deep.eq(BigNumber.from(6430020)) }) it('Should estimate gas for ETH withdraw', async () => { const amount = utils.parseEther('0.5') const gas = await env.ovmEth.estimateGas.withdraw(amount) - expect(gas).to.be.deep.eq(BigNumber.from(61400489396)) + expect(gas).to.be.deep.eq(BigNumber.from(6140049)) }) }) diff --git a/integration-tests/test/rpc.spec.ts b/integration-tests/test/rpc.spec.ts index 4c88cc4952a7..9866ba906f5d 100644 --- a/integration-tests/test/rpc.spec.ts +++ b/integration-tests/test/rpc.spec.ts @@ -134,7 +134,7 @@ describe('Basic RPC tests', () => { gasPrice: TxGasPrice, } const fee = tx.gasPrice.mul(tx.gasLimit) - const gasLimit = 59300000001 + const gasLimit = 5920001 await expect(env.l2Wallet.sendTransaction(tx)).to.be.rejectedWith( `fee too low: ${fee}, use at least tx.gasLimit = ${gasLimit} and tx.gasPrice = ${TxGasPrice.toString()}` @@ -213,7 +213,7 @@ describe('Basic RPC tests', () => { it('correctly exposes revert data for contract calls', async () => { const req: TransactionRequest = { ...revertingTx, - gasLimit: 59808999999, // override gas estimation + gasLimit: 5980899, // override gas estimation } const tx = await wallet.sendTransaction(req) @@ -236,7 +236,7 @@ describe('Basic RPC tests', () => { it('correctly exposes revert data for contract creations', async () => { const req: TransactionRequest = { ...revertingDeployTx, - gasLimit: 177008999999, // override gas estimation + gasLimit: 17700899, // override gas estimation } const tx = await wallet.sendTransaction(req) @@ -355,7 +355,7 @@ describe('Basic RPC tests', () => { to: DEFAULT_TRANSACTION.to, value: 0, }) - expect(estimate).to.be.eq(0x0dce9004c7) + expect(estimate).to.be.eq(5920012) }) it('should return a gas estimate that grows with the size of data', async () => { @@ -373,6 +373,7 @@ describe('Basic RPC tests', () => { const estimate = await l2Provider.estimateGas(tx) const l2Gaslimit = await l2Provider.send('eth_estimateExecutionGas', [ tx, + true, ]) const decoded = TxGasLimit.decode(estimate) diff --git a/l2geth/core/tx_pool.go b/l2geth/core/tx_pool.go index c92e3fde1c5c..1244e27835ca 100644 --- a/l2geth/core/tx_pool.go +++ b/l2geth/core/tx_pool.go @@ -92,9 +92,8 @@ var ( ) var ( - evictionInterval = time.Minute // Time interval to check for evictable transactions - statsReportInterval = 8 * time.Second // Time interval to report transaction pool stats - gwei = big.NewInt(params.GWei) // 1 gwei, used as a flag for "rollup" transactions + evictionInterval = time.Minute // Time interval to check for evictable transactions + statsReportInterval = 8 * time.Second // Time interval to report transaction pool stats ) var ( @@ -540,10 +539,14 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { } // Ensure the transaction doesn't exceed the current block limit gas. - // We skip this condition check if the transaction's gasPrice is set to 1gwei, - // which indicates a "rollup" transaction that's paying for its data. - if pool.currentMaxGas < tx.L2Gas() && tx.GasPrice().Cmp(gwei) != 0 { - return ErrGasLimit + if vm.UsingOVM { + if pool.currentMaxGas < tx.L2Gas() { + return ErrGasLimit + } + } else { + if pool.currentMaxGas < tx.Gas() { + return ErrGasLimit + } } // Make sure the transaction is signed properly diff --git a/l2geth/core/types/transaction.go b/l2geth/core/types/transaction.go index a3fc9874acf6..6f30076f9bf5 100644 --- a/l2geth/core/types/transaction.go +++ b/l2geth/core/types/transaction.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rollup/fees" ) //go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go @@ -225,7 +226,7 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) } func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit } -func (tx *Transaction) L2Gas() uint64 { return tx.data.GasLimit % 100_000_000 } +func (tx *Transaction) L2Gas() uint64 { return fees.DecodeL2GasLimitU64(tx.data.GasLimit) } func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) } func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) } func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce } diff --git a/l2geth/internal/ethapi/api.go b/l2geth/internal/ethapi/api.go index 648ddea679cd..abec7e0b1c06 100644 --- a/l2geth/internal/ethapi/api.go +++ b/l2geth/internal/ethapi/api.go @@ -1148,9 +1148,17 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h // EstimateExecutionGas returns an estimate of the amount of gas needed to execute the // given transaction against the current pending block. -func (s *PublicBlockChainAPI) EstimateExecutionGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) { +func (s *PublicBlockChainAPI) EstimateExecutionGas(ctx context.Context, args CallArgs, round *bool) (hexutil.Uint64, error) { blockNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) - return legacyDoEstimateGas(ctx, s.b, args, blockNrOrHash, s.b.RPCGasCap()) + estimate, err := legacyDoEstimateGas(ctx, s.b, args, blockNrOrHash, s.b.RPCGasCap()) + if err != nil { + return estimate, err + } + if round != nil && *round { + rounded := fees.Ceilmod(new(big.Int).SetUint64(uint64(estimate)), fees.BigTenThousand) + estimate = (hexutil.Uint64)(rounded.Uint64()) + } + return estimate, nil } // ExecutionResult groups all structured logs emitted by the EVM diff --git a/l2geth/rollup/fees/rollup_fee.go b/l2geth/rollup/fees/rollup_fee.go index 2f5455057e7d..13feafbbdef7 100644 --- a/l2geth/rollup/fees/rollup_fee.go +++ b/l2geth/rollup/fees/rollup_fee.go @@ -3,6 +3,7 @@ package fees import ( "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/params" ) @@ -10,12 +11,9 @@ import ( // transaction in gas. const overhead uint64 = 4200 + 200*params.TxDataNonZeroGasEIP2028 -// hundredMillion is a constant used in the gas encoding formula -const hundredMillion uint64 = 100_000_000 - // feeScalar is used to scale the calculations in EncodeL2GasLimit // to prevent them from being too large -const feeScalar uint64 = 1000 +const feeScalar uint64 = 10_000_000 // TxGasPrice is a constant that determines the result of `eth_gasPrice` // It is scaled upwards by 50% @@ -26,7 +24,10 @@ const TxGasPrice uint64 = feeScalar + (feeScalar / 2) // BigTxGasPrice is the L2GasPrice as type big.Int var BigTxGasPrice = new(big.Int).SetUint64(TxGasPrice) var bigFeeScalar = new(big.Int).SetUint64(feeScalar) -var bigHundredMillion = new(big.Int).SetUint64(hundredMillion) + +const tenThousand = 10000 + +var BigTenThousand = new(big.Int).SetUint64(tenThousand) // EncodeTxGasLimit computes the `tx.gasLimit` based on the L1/L2 gas prices and // the L2 gas limit. The L2 gas limit is encoded inside of the lower order bits @@ -40,32 +41,50 @@ var bigHundredMillion = new(big.Int).SetUint64(hundredMillion) // the fee, so increasing the L2 Gas limit will increase the fee paid. // The calculation is: // l1GasLimit = zero_count(data) * 4 + non_zero_count(data) * 16 + overhead +// roundedL2GasLimit = ceilmod(l2GasLimit, 10_000) // l1Fee = l1GasPrice * l1GasLimit -// l2Fee = l2GasPrice * l2GasLimit +// l2Fee = l2GasPrice * roundedL2GasLimit // sum = l1Fee + l2Fee // scaled = sum / scalar -// rounded = ceilmod(scaled, hundredMillion) -// result = rounded + l2GasLimit +// rounded = ceilmod(scaled, tenThousand) +// roundedScaledL2GasLimit = roundedL2GasLimit / tenThousand +// result = rounded + roundedScaledL2GasLimit // Note that for simplicity purposes, only the calldata is passed into this // function when in reality the RLP encoded transaction should be. The // additional cost is added to the overhead constant to prevent the need to RLP // encode transactions during calls to `eth_estimateGas` func EncodeTxGasLimit(data []byte, l1GasPrice, l2GasLimit, l2GasPrice *big.Int) *big.Int { l1GasLimit := calculateL1GasLimit(data, overhead) + roundedL2GasLimit := Ceilmod(l2GasLimit, BigTenThousand) l1Fee := new(big.Int).Mul(l1GasPrice, l1GasLimit) - l2Fee := new(big.Int).Mul(l2GasPrice, l2GasLimit) + l2Fee := new(big.Int).Mul(l2GasPrice, roundedL2GasLimit) sum := new(big.Int).Add(l1Fee, l2Fee) scaled := new(big.Int).Div(sum, bigFeeScalar) - remainder := new(big.Int).Mod(scaled, bigHundredMillion) - scaledSum := new(big.Int).Add(scaled, bigHundredMillion) - rounded := new(big.Int).Sub(scaledSum, remainder) - result := new(big.Int).Add(rounded, l2GasLimit) + rounded := Ceilmod(scaled, BigTenThousand) + roundedScaledL2GasLimit := new(big.Int).Div(roundedL2GasLimit, BigTenThousand) + result := new(big.Int).Add(rounded, roundedScaledL2GasLimit) return result } +func Ceilmod(a, b *big.Int) *big.Int { + remainder := new(big.Int).Mod(a, b) + if remainder.Cmp(common.Big0) == 0 { + return a + } + sum := new(big.Int).Add(a, b) + rounded := new(big.Int).Sub(sum, remainder) + return rounded +} + // DecodeL2GasLimit decodes the L2 gas limit from an encoded L2 gas limit func DecodeL2GasLimit(gasLimit *big.Int) *big.Int { - return new(big.Int).Mod(gasLimit, bigHundredMillion) + scaled := new(big.Int).Mod(gasLimit, BigTenThousand) + return new(big.Int).Mul(scaled, BigTenThousand) +} + +func DecodeL2GasLimitU64(gasLimit uint64) uint64 { + scaled := gasLimit % tenThousand + return scaled * tenThousand } // calculateL1GasLimit computes the L1 gasLimit based on the calldata and diff --git a/l2geth/rollup/fees/rollup_fee_test.go b/l2geth/rollup/fees/rollup_fee_test.go index 138a0d0bb16a..254d418ad8fa 100644 --- a/l2geth/rollup/fees/rollup_fee_test.go +++ b/l2geth/rollup/fees/rollup_fee_test.go @@ -74,7 +74,7 @@ var feeTests = map[string]struct { "max-gaslimit": { dataLen: 10, l1GasPrice: params.GWei, - l2GasLimit: 99999999, + l2GasLimit: 99_970_000, l2GasPrice: params.GWei, }, "larger-divisor": { @@ -95,7 +95,8 @@ func TestCalculateRollupFee(t *testing.T) { fee := EncodeTxGasLimit(data, l1GasPrice, l2GasLimit, l2GasPrice) decodedGasLimit := DecodeL2GasLimit(fee) - if l2GasLimit.Cmp(decodedGasLimit) != 0 { + roundedL2GasLimit := Ceilmod(l2GasLimit, BigTenThousand) + if roundedL2GasLimit.Cmp(decodedGasLimit) != 0 { t.Errorf("rollup fee check failed: expected %d, got %d", l2GasLimit.Uint64(), decodedGasLimit) } }) diff --git a/packages/core-utils/src/fees.ts b/packages/core-utils/src/fees.ts index 3910d19196df..5a1008b1aa5a 100644 --- a/packages/core-utils/src/fees.ts +++ b/packages/core-utils/src/fees.ts @@ -6,11 +6,12 @@ import { BigNumber } from 'ethers' import { remove0x } from './common' const hundredMillion = BigNumber.from(100_000_000) -const feeScalar = 1000 +const feeScalar = 10_000_000 export const TxGasPrice = BigNumber.from(feeScalar + feeScalar / 2) const txDataZeroGas = 4 const txDataNonZeroGasEIP2028 = 16 const overhead = 4200 + 200 * txDataNonZeroGasEIP2028 +const tenThousand = BigNumber.from(10_000) export interface EncodableL2GasLimit { data: Buffer | string @@ -32,21 +33,22 @@ function encode(input: EncodableL2GasLimit): BigNumber { l2GasPrice = BigNumber.from(l2GasPrice) } const l1GasLimit = calculateL1GasLimit(data) + const roundedL2GasLimit = ceilmod(l2GasLimit, tenThousand) const l1Fee = l1GasLimit.mul(l1GasPrice) - const l2Fee = l2GasLimit.mul(l2GasPrice) + const l2Fee = roundedL2GasLimit.mul(l2GasPrice) const sum = l1Fee.add(l2Fee) const scaled = sum.div(feeScalar) - const remainder = scaled.mod(hundredMillion) - const scaledSum = scaled.add(hundredMillion) - const rounded = scaledSum.sub(remainder) - return rounded.add(l2GasLimit) + const rounded = ceilmod(scaled, tenThousand) + const roundedScaledL2GasLimit = roundedL2GasLimit.div(tenThousand) + return rounded.add(roundedScaledL2GasLimit) } function decode(fee: BigNumber | number): BigNumber { if (typeof fee === 'number') { fee = BigNumber.from(fee) } - return fee.mod(hundredMillion) + const scaled = fee.mod(tenThousand) + return scaled.mul(tenThousand) } export const TxGasLimit = { @@ -54,6 +56,22 @@ export const TxGasLimit = { decode, } +export function ceilmod(a: BigNumber | number, b: BigNumber | number) { + if (typeof a === 'number') { + a = BigNumber.from(a) + } + if (typeof b === 'number') { + b = BigNumber.from(b) + } + const remainder = a.mod(b) + if (remainder.eq(0)) { + return a + } + const sum = a.add(b) + const rounded = sum.sub(remainder) + return rounded +} + export function calculateL1GasLimit(data: string | Buffer): BigNumber { const [zeroes, ones] = zeroesAndOnes(data) const zeroesCost = zeroes * txDataZeroGas diff --git a/packages/core-utils/test/fees/fees.spec.ts b/packages/core-utils/test/fees/fees.spec.ts index c624c0b3128b..3870092dfc57 100644 --- a/packages/core-utils/test/fees/fees.spec.ts +++ b/packages/core-utils/test/fees/fees.spec.ts @@ -56,7 +56,7 @@ describe('Fees', () => { dataLen: 10, l1GasPrice: utils.parseUnits('5', 'ether'), l2GasPrice: utils.parseUnits('5', 'ether'), - l2GasLimit: 10 ** 8 - 1, + l2GasLimit: 99_970_000, }, { name: 'zero-l2-gasprice', @@ -113,7 +113,8 @@ describe('Fees', () => { }) const decoded = fees.TxGasLimit.decode(got) - expect(decoded).to.deep.eq(BigNumber.from(test.l2GasLimit)) + const roundedL2GasLimit = fees.ceilmod(test.l2GasLimit, 10_000) + expect(decoded).to.deep.eq(roundedL2GasLimit) }) } }) From 750a5021214f3db5eed3faac9e6608b617abd2e0 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Tue, 1 Jun 2021 22:36:03 -0700 Subject: [PATCH 13/46] fix: remove dead coders (#1001) * chore: delete dead coders * chore: add changeset * dtl: remove dead imports * core-utils: delete dead tests * batch-submitter: remove txtype * chore: add changeset --- .changeset/fair-maps-pretend.md | 6 + .changeset/silent-masks-hunt.md | 5 + .../batch-submitter/batch-submitter.spec.ts | 7 +- packages/core-utils/src/coders/ecdsa-coder.ts | 243 ------------------ packages/core-utils/src/coders/index.ts | 1 - packages/core-utils/src/coders/types.ts | 5 - .../test/coders/batch-encoder.spec.ts | 89 ------- .../handlers/sequencer-batch-appended.ts | 2 - 8 files changed, 14 insertions(+), 344 deletions(-) create mode 100644 .changeset/fair-maps-pretend.md create mode 100644 .changeset/silent-masks-hunt.md delete mode 100644 packages/core-utils/src/coders/ecdsa-coder.ts diff --git a/.changeset/fair-maps-pretend.md b/.changeset/fair-maps-pretend.md new file mode 100644 index 000000000000..2feeebb11b22 --- /dev/null +++ b/.changeset/fair-maps-pretend.md @@ -0,0 +1,6 @@ +--- +'@eth-optimism/batch-submitter': patch +'@eth-optimism/data-transport-layer': patch +--- + +Remove dead imports from core-utils diff --git a/.changeset/silent-masks-hunt.md b/.changeset/silent-masks-hunt.md new file mode 100644 index 000000000000..46f0382c5a79 --- /dev/null +++ b/.changeset/silent-masks-hunt.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/core-utils': patch +--- + +Delete dead transaction coders. These are no longer used now that RLP encoded transactions are used diff --git a/packages/batch-submitter/test/batch-submitter/batch-submitter.spec.ts b/packages/batch-submitter/test/batch-submitter/batch-submitter.spec.ts index f0b0e4686e8e..dffbdc33867c 100644 --- a/packages/batch-submitter/test/batch-submitter/batch-submitter.spec.ts +++ b/packages/batch-submitter/test/batch-submitter/batch-submitter.spec.ts @@ -33,7 +33,6 @@ import { QueueOrigin, Batch, Signature, - TxType, remove0x, } from '@eth-optimism/core-utils' import { Logger, Metrics } from '@eth-optimism/common-ts' @@ -253,7 +252,7 @@ describe('BatchSubmitter', () => { { rawTransaction: '0x1234', l1BlockNumber: nextQueueElement.blockNumber - 1, - txType: TxType.EIP155, + txType: 0, queueOrigin: QueueOrigin.Sequencer, l1TxOrigin: null, } as any, @@ -302,7 +301,7 @@ describe('BatchSubmitter', () => { { rawTransaction: '0x1234', l1BlockNumber: nextQueueElement.blockNumber - 1, - txType: TxType.EthSign, + txType: 1, queueOrigin: QueueOrigin.Sequencer, l1TxOrigin: null, } as any, @@ -406,7 +405,7 @@ describe('BatchSubmitter', () => { { rawTransaction: '0x1234', l1BlockNumber: nextQueueElement.blockNumber - 1, - txType: TxType.EIP155, + txType: 0, queueOrigin: QueueOrigin.Sequencer, l1TxOrigin: null, } as any, diff --git a/packages/core-utils/src/coders/ecdsa-coder.ts b/packages/core-utils/src/coders/ecdsa-coder.ts deleted file mode 100644 index be381a107723..000000000000 --- a/packages/core-utils/src/coders/ecdsa-coder.ts +++ /dev/null @@ -1,243 +0,0 @@ -/* Internal Imports */ -import { add0x, remove0x, toVerifiedBytes, encodeHex, getLen } from '../common' -import { Coder, Signature, Uint16, Uint8, Uint24, Address } from './types' - -/*********************** - * TxTypes and TxData * - **********************/ - -export enum TxType { - EIP155 = 0, - EthSign = 1, - EthSign2 = 2, -} - -export const txTypePlainText = { - 0: TxType.EIP155, - 1: TxType.EthSign, - 2: TxType.EthSign2, - EIP155: TxType.EIP155, - EthSign: TxType.EthSign, -} - -export interface DefaultEcdsaTxData { - sig: Signature - gasLimit: Uint16 - gasPrice: Uint8 - nonce: Uint24 - target: Address - data: string - type: TxType -} - -export interface EIP155TxData extends DefaultEcdsaTxData {} -export interface EthSignTxData extends DefaultEcdsaTxData {} - -/*********************** - * Encoding Positions * - **********************/ - -/* - * The positions in the tx data for the different transaction types - */ - -export const TX_TYPE_POSITION = { start: 0, end: 1 } - -/* - * The positions in the tx data for the EIP155TxData and EthSignTxData - */ - -export const SIGNATURE_FIELD_POSITIONS = { - r: { start: 1, end: 33 }, // 32 bytes - s: { start: 33, end: 65 }, // 32 bytes - v: { start: 65, end: 66 }, // 1 byte -} - -export const DEFAULT_ECDSA_TX_FIELD_POSITIONS = { - txType: TX_TYPE_POSITION, // 1 byte - sig: SIGNATURE_FIELD_POSITIONS, // 65 bytes - gasLimit: { start: 66, end: 69 }, // 3 bytes - gasPrice: { start: 69, end: 72 }, // 3 byte - nonce: { start: 72, end: 75 }, // 3 bytes - target: { start: 75, end: 95 }, // 20 bytes - data: { start: 95 }, // byte 95 onward -} - -export const EIP155_TX_FIELD_POSITIONS = DEFAULT_ECDSA_TX_FIELD_POSITIONS -export const ETH_SIGN_TX_FIELD_POSITIONS = DEFAULT_ECDSA_TX_FIELD_POSITIONS -export const CTC_TX_GAS_PRICE_MULT_FACTOR = 1_000_000 - -/*************** - * EcdsaCoders * - **************/ - -class DefaultEcdsaTxCoder implements Coder { - constructor(readonly txType: TxType) {} - - public encode(txData: DefaultEcdsaTxData): string { - const txType = encodeHex( - this.txType, - getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.txType) - ) - - const r = toVerifiedBytes( - txData.sig.r, - getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.sig.r) - ) - const s = toVerifiedBytes( - txData.sig.s, - getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.sig.s) - ) - const v = encodeHex( - txData.sig.v, - getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.sig.v) - ) - - const gasLimit = encodeHex( - txData.gasLimit, - getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.gasLimit) - ) - if (txData.gasPrice % CTC_TX_GAS_PRICE_MULT_FACTOR !== 0) { - throw new Error(`Gas Price ${txData.gasPrice} cannot be encoded`) - } - const gasPrice = encodeHex( - txData.gasPrice / CTC_TX_GAS_PRICE_MULT_FACTOR, - getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.gasPrice) - ) - const nonce = encodeHex( - txData.nonce, - getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.nonce) - ) - const target = toVerifiedBytes( - txData.target, - getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.target) - ) - // Make sure that the data is even - if (txData.data.length % 2 !== 0) { - throw new Error('Non-even hex string for tx data!') - } - const encoding = - '0x' + - txType + - r + - s + - v + - gasLimit + - gasPrice + - nonce + - target + - remove0x(txData.data) - return encoding - } - - public decode(txData: string): DefaultEcdsaTxData { - txData = remove0x(txData) - const sliceBytes = (position: { start; end? }): string => - txData.slice(position.start * 2, position.end * 2) - - const pos = DEFAULT_ECDSA_TX_FIELD_POSITIONS - if (parseInt(sliceBytes(pos.txType), 16) !== this.txType) { - throw new Error('Invalid tx type') - } - - return { - sig: { - r: add0x(sliceBytes(pos.sig.r)), - s: add0x(sliceBytes(pos.sig.s)), - v: parseInt(sliceBytes(pos.sig.v), 16), - }, - gasLimit: parseInt(sliceBytes(pos.gasLimit), 16), - gasPrice: - parseInt(sliceBytes(pos.gasPrice), 16) * CTC_TX_GAS_PRICE_MULT_FACTOR, - nonce: parseInt(sliceBytes(pos.nonce), 16), - target: add0x(sliceBytes(pos.target)), - data: add0x(txData.slice(pos.data.start * 2)), - type: this.txType, - } - } -} - -class EthSignTxCoder extends DefaultEcdsaTxCoder { - constructor() { - super(TxType.EthSign) - } - - public encode(txData: EthSignTxData): string { - return super.encode(txData) - } - - public decode(txData: string): EthSignTxData { - return super.decode(txData) - } -} - -class EthSign2TxCoder extends DefaultEcdsaTxCoder { - constructor() { - super(TxType.EthSign2) - } - - public encode(txData: EthSignTxData): string { - return super.encode(txData) - } - - public decode(txData: string): EthSignTxData { - return super.decode(txData) - } -} - -class Eip155TxCoder extends DefaultEcdsaTxCoder { - constructor() { - super(TxType.EIP155) - } - - public encode(txData: EIP155TxData): string { - return super.encode(txData) - } - - public decode(txData: string): EIP155TxData { - return super.decode(txData) - } -} - -/************* - * ctcCoder * - ************/ - -function encode(data: EIP155TxData): string { - if (data.type === TxType.EIP155) { - return new Eip155TxCoder().encode(data) - } - if (data.type === TxType.EthSign) { - return new EthSignTxCoder().encode(data) - } - return null -} - -function decode(data: string | Buffer): EIP155TxData { - if (Buffer.isBuffer(data)) { - data = data.toString() - } - data = remove0x(data) - const type = parseInt(data.slice(0, 2), 16) - if (type === TxType.EIP155) { - return new Eip155TxCoder().decode(data) - } - if (type === TxType.EthSign) { - return new EthSignTxCoder().decode(data) - } - if (type === TxType.EthSign2) { - return new EthSign2TxCoder().decode(data) - } - return null -} - -/* - * Encoding and decoding functions for all txData types. - */ -export const ctcCoder = { - eip155TxData: new Eip155TxCoder(), - ethSignTxData: new EthSignTxCoder(), - ethSign2TxData: new EthSign2TxCoder(), - encode, - decode, -} diff --git a/packages/core-utils/src/coders/index.ts b/packages/core-utils/src/coders/index.ts index 4d9613d932e7..dfa4404fbe81 100644 --- a/packages/core-utils/src/coders/index.ts +++ b/packages/core-utils/src/coders/index.ts @@ -1,3 +1,2 @@ -export * from './ecdsa-coder' export * from './types' export * from './sequencer-batch' diff --git a/packages/core-utils/src/coders/types.ts b/packages/core-utils/src/coders/types.ts index 928c2cc58266..1280fe3d7099 100644 --- a/packages/core-utils/src/coders/types.ts +++ b/packages/core-utils/src/coders/types.ts @@ -8,8 +8,3 @@ export type Uint16 = number export type Uint8 = number export type Uint24 = number export type Address = string - -export interface Coder { - encode: Function - decode: Function -} diff --git a/packages/core-utils/test/coders/batch-encoder.spec.ts b/packages/core-utils/test/coders/batch-encoder.spec.ts index 2ad7c2eeeeb9..ce277a72d051 100644 --- a/packages/core-utils/test/coders/batch-encoder.spec.ts +++ b/packages/core-utils/test/coders/batch-encoder.spec.ts @@ -2,63 +2,13 @@ import '../setup' /* Internal Imports */ import { - ctcCoder, encodeAppendSequencerBatch, decodeAppendSequencerBatch, - TxType, sequencerBatch, } from '../../src' import { expect } from 'chai' describe('BatchEncoder', () => { - describe('eip155TxData', () => { - it('should encode & then decode to the correct value', () => { - const eip155TxData = { - sig: { - v: 1, - r: '0x' + '11'.repeat(32), - s: '0x' + '22'.repeat(32), - }, - gasLimit: 500, - gasPrice: 1000000, - nonce: 100, - target: '0x' + '12'.repeat(20), - data: '0x' + '99'.repeat(10), - type: TxType.EIP155, - } - const encoded = ctcCoder.eip155TxData.encode(eip155TxData) - const decoded = ctcCoder.eip155TxData.decode(encoded) - expect(eip155TxData).to.deep.equal(decoded) - }) - - it('should fail encoding a bad gas price', () => { - const badGasPrice = 1000001 - const eip155TxData = { - sig: { - v: 1, - r: '0x' + '11'.repeat(32), - s: '0x' + '22'.repeat(32), - }, - gasLimit: 500, - gasPrice: badGasPrice, - nonce: 100, - target: '0x' + '12'.repeat(20), - data: '0x' + '99'.repeat(10), - type: TxType.EIP155, - } - - let error - try { - ctcCoder.eip155TxData.encode(eip155TxData) - } catch (e) { - error = e - } - expect(error.message).to.equal( - `Gas Price ${badGasPrice} cannot be encoded` - ) - }) - }) - describe('appendSequencerBatch', () => { it('should work with the simple case', () => { const batch = { @@ -100,43 +50,4 @@ describe('BatchEncoder', () => { } }) }) - - describe('generic ctcCoder', () => { - it('should decode EIP155 txs to the correct value', () => { - const eip155TxData = { - sig: { - v: 1, - r: '0x' + '11'.repeat(32), - s: '0x' + '22'.repeat(32), - }, - gasLimit: 500, - gasPrice: 1000000, - nonce: 100, - target: '0x' + '12'.repeat(20), - data: '0x' + '99'.repeat(10), - type: TxType.EIP155, - } - const encoded = ctcCoder.encode(eip155TxData) - const decoded = ctcCoder.decode(encoded) - expect(eip155TxData).to.deep.equal(decoded) - }) - - it('should return null when encoding an unknown type', () => { - const weirdTypeTxData = { - sig: { - v: 1, - r: '0x' + '11'.repeat(32), - s: '0x' + '22'.repeat(32), - }, - gasLimit: 500, - gasPrice: 100, - nonce: 100, - target: '0x' + '12'.repeat(20), - data: '0x' + '99'.repeat(10), - type: 420, - } - const encoded = ctcCoder.encode(weirdTypeTxData) - expect(encoded).to.be.null - }) - }) }) diff --git a/packages/data-transport-layer/src/services/l1-ingestion/handlers/sequencer-batch-appended.ts b/packages/data-transport-layer/src/services/l1-ingestion/handlers/sequencer-batch-appended.ts index 23230efd02fe..0c90fa286277 100644 --- a/packages/data-transport-layer/src/services/l1-ingestion/handlers/sequencer-batch-appended.ts +++ b/packages/data-transport-layer/src/services/l1-ingestion/handlers/sequencer-batch-appended.ts @@ -2,11 +2,9 @@ import { BigNumber, ethers, constants } from 'ethers' import { getContractFactory } from '@eth-optimism/contracts' import { - ctcCoder, fromHexString, toHexString, toRpcHexString, - TxType, EventArgsSequencerBatchAppended, } from '@eth-optimism/core-utils' From 9d39121b80a84691cf59681e6b98215db1e6d7d7 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Wed, 2 Jun 2021 03:56:39 -0400 Subject: [PATCH 14/46] docs[message-relayer]: add a README and improve the interface for generating proofs (#1002) * docs[message-relayer]: add basic docs and clean up an interface * chore: add changeset --- .changeset/great-shrimps-rule.md | 5 ++ packages/message-relayer/README.md | 69 ++++++++++++++++++++++++ packages/message-relayer/src/relay-tx.ts | 11 +++- 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 .changeset/great-shrimps-rule.md create mode 100644 packages/message-relayer/README.md diff --git a/.changeset/great-shrimps-rule.md b/.changeset/great-shrimps-rule.md new file mode 100644 index 000000000000..388ad60eeaa7 --- /dev/null +++ b/.changeset/great-shrimps-rule.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/message-relayer': patch +--- + +Adds a README and cleans up the interface for generating messages and proofs diff --git a/packages/message-relayer/README.md b/packages/message-relayer/README.md new file mode 100644 index 000000000000..964f1a67e5e9 --- /dev/null +++ b/packages/message-relayer/README.md @@ -0,0 +1,69 @@ +# @eth-optimism/message-relayer + +This package contains: + +1. A service for relaying messages from L2 to L1. +2. Utilities for finding these messages and relaying them. + +## Installation + +``` +yarn add @eth-optimism/message-relayer +``` + +## Relay Utilities + +### getMessagesAndProofsForL2Transaction + +Finds all L2 => L1 messages sent in a given L2 transaction and generates proof for each. + +#### Usage + +```typescript +import { getMessagesAndProofsForL2Transaction } from '@eth-optimism/message-relayer' + +const main = async () => { + const l1RpcProviderUrl = 'https://layer1.endpoint' + const l2RpcProviderUrl = 'https://layer2.endpoint' + const l1StateCommitmentChainAddress = 'address of OVM_StateCommitmentChain from deployments page' + const l2CrossDomainMessengerAddress = 'address of OVM_L2CrossDomainMessenger from deployments page' + const l2TransactionHash = 'hash of the transaction with messages to relay' + + const messagePairs = await getMessagesAndProofsForL2Transaction( + l1RpcProviderUrl, + l2RpcProviderUrl, + l1StateCommitmentChainAddress, + l2CrossDomainMessengerAddress, + l2TransactionHash + ) + + console.log(messagePairs) + // Will log something along the lines of: + // [ + // { + // message: { + // target: '0x...', + // sender: '0x...', + // message: '0x...', + // messageNonce: 1234... + // }, + // proof: { + // // complicated + // } + // } + // ] + + // You can then do something along the lines of: + // for (const { message, proof } of messagePairs) { + // await l1CrossDomainMessenger.relayMessage( + // message.target, + // message.sender, + // message.message, + // message.messageNonce, + // proof + // ) + // } +} + +main() +``` diff --git a/packages/message-relayer/src/relay-tx.ts b/packages/message-relayer/src/relay-tx.ts index 547006ed7735..92c83e189add 100644 --- a/packages/message-relayer/src/relay-tx.ts +++ b/packages/message-relayer/src/relay-tx.ts @@ -323,12 +323,19 @@ const getStateTrieProof = async ( * @returns An array of messages sent in the transaction and a proof of inclusion for each. */ export const getMessagesAndProofsForL2Transaction = async ( - l1RpcProvider: ethers.providers.JsonRpcProvider, - l2RpcProvider: ethers.providers.JsonRpcProvider, + l1RpcProvider: ethers.providers.JsonRpcProvider | string, + l2RpcProvider: ethers.providers.JsonRpcProvider | string, l1StateCommitmentChainAddress: string, l2CrossDomainMessengerAddress: string, l2TransactionHash: string ): Promise => { + if (typeof l1RpcProvider === 'string') { + l1RpcProvider = new ethers.providers.JsonRpcProvider(l1RpcProvider) + } + if (typeof l2RpcProvider === 'string') { + l2RpcProvider = new ethers.providers.JsonRpcProvider(l2RpcProvider) + } + const l2Transaction = await l2RpcProvider.getTransaction(l2TransactionHash) if (l2Transaction === null) { throw new Error(`unable to find tx with hash: ${l2TransactionHash}`) From e52ccd98c997789b17ce6d38beb1d49fa8c52fc4 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Wed, 2 Jun 2021 02:02:26 -0700 Subject: [PATCH 15/46] dtl: log error stack for failed http request (#995) * dtl: log error stack for failed http request * chore: add changeset --- .changeset/slimy-rivers-promise.md | 5 +++++ packages/data-transport-layer/src/services/server/service.ts | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/slimy-rivers-promise.md diff --git a/.changeset/slimy-rivers-promise.md b/.changeset/slimy-rivers-promise.md new file mode 100644 index 000000000000..38b393bac00e --- /dev/null +++ b/.changeset/slimy-rivers-promise.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/data-transport-layer': patch +--- + +Logs the error stacktrace for a failed HTTP request diff --git a/packages/data-transport-layer/src/services/server/service.ts b/packages/data-transport-layer/src/services/server/service.ts index 8774181ade3e..4e781114d92d 100644 --- a/packages/data-transport-layer/src/services/server/service.ts +++ b/packages/data-transport-layer/src/services/server/service.ts @@ -198,6 +198,7 @@ export class L1TransportServer extends BaseService { url: req.url, elapsed, msg: e.toString(), + stack: e.stack, }) return res.status(400).json({ error: e.toString(), From a02392b070382112003c4fdd246cf3ad5ec261b5 Mon Sep 17 00:00:00 2001 From: Ben Wilson <82120899+optimisticben@users.noreply.github.com> Date: Wed, 2 Jun 2021 05:19:53 -0400 Subject: [PATCH 16/46] Add rpc-proxy service for whitelisting JSON RPC methods to the sequencer. (#945) * Add healthcheck endpoint for rpc-proxy Added ethereum-nginx-proxy source updated README and docker image build * Check ETH_CALLS_ALLOWED is set, clean up comments, remove old Dockerfile --- .github/workflows/release.yml | 8 ++ ops/README.md | 2 + ops/docker-compose-rpc-proxy.yml | 17 ++++ ops/docker-compose.yml | 1 + ops/docker/Dockerfile.rpc-proxy | 21 +++++ ops/docker/rpc-proxy/docker-entrypoint.sh | 19 +++++ ops/docker/rpc-proxy/eth-jsonrpc-access.lua | 91 +++++++++++++++++++++ ops/docker/rpc-proxy/nginx.template.conf | 80 ++++++++++++++++++ 8 files changed, 239 insertions(+) create mode 100644 ops/docker-compose-rpc-proxy.yml create mode 100644 ops/docker/Dockerfile.rpc-proxy create mode 100755 ops/docker/rpc-proxy/docker-entrypoint.sh create mode 100644 ops/docker/rpc-proxy/eth-jsonrpc-access.lua create mode 100644 ops/docker/rpc-proxy/nginx.template.conf diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2bdc59c027b4..65bfc5216d1f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -92,6 +92,14 @@ jobs: push: true tags: ethereumoptimism/l2geth:${{ needs.release.outputs.l2geth }} + - name: Publish rpc-proxy + uses: docker/build-push-action@v2 + with: + context: . + file: ./ops/docker/Dockerfile.rpc-proxy + push: true + tags: ethereumoptimism/rpc-proxy:${{ needs.release.outputs.l2geth }} + # pushes the base builder image to dockerhub builder: name: Prepare the base builder image for the services diff --git a/ops/README.md b/ops/README.md index 488e3a6f2a62..1dc7858efb0e 100644 --- a/ops/README.md +++ b/ops/README.md @@ -14,6 +14,8 @@ The base `docker-compose.yml` file will start the required components for a full Supplementing the base configuration is an additional metric enabling file, `docker-compose-metrics.yml`. Adding this configuration to the stack will enable metric emission for l2geth and start grafana (for metrics visualisation) and influxdb (for metric collection) instances. +Also available for testing is the `rpc-proxy` service in the `docker-compose-rpc-proxy.yml` file. It can be used to restrict what RPC methods are allowed to the Sequencer. + The base stack can be started and stopped with a command like this (there is no need to specify the default docker-compose.yml) ``` docker-compose \ diff --git a/ops/docker-compose-rpc-proxy.yml b/ops/docker-compose-rpc-proxy.yml new file mode 100644 index 000000000000..dccd31c1cb07 --- /dev/null +++ b/ops/docker-compose-rpc-proxy.yml @@ -0,0 +1,17 @@ +version: "3" +services: + rpc-proxy: + depends_on: + - l1_chain + - deployer + - l2geth + image: rpc-proxy + build: + context: .. + dockerfile: ./ops/docker/Dockerfile.rpc-proxy + environment: + SEQUENCER: l2geth:8545 + ETH_CALLS_ALLOWED: eth_blockNumber,eth_sendRawTransaction + ports: + - 9546:8080 + - 9145:9145 diff --git a/ops/docker-compose.yml b/ops/docker-compose.yml index 4acad64c523d..496f3738705f 100644 --- a/ops/docker-compose.yml +++ b/ops/docker-compose.yml @@ -173,3 +173,4 @@ services: URL: http://deployer:8081/addresses.json ENABLE_GAS_REPORT: 1 NO_NETWORK: 1 + diff --git a/ops/docker/Dockerfile.rpc-proxy b/ops/docker/Dockerfile.rpc-proxy new file mode 100644 index 000000000000..c0e03b93bd14 --- /dev/null +++ b/ops/docker/Dockerfile.rpc-proxy @@ -0,0 +1,21 @@ +FROM openresty/openresty:buster +LABEL maintainer="Optimistic Systems " +ARG GOTEMPLATE_VERSION=v3.9.0 + +RUN DEBIAN_FRONTEND=noninteractive apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + openresty-opm \ + && opm get knyar/nginx-lua-prometheus + +RUN curl -o /usr/local/bin/gomplate \ + -sSL https://github.com/hairyhenderson/gomplate/releases/download/$GOTEMPLATE_VERSION/gomplate_linux-amd64-slim \ + && chmod +x /usr/local/bin/gomplate + +RUN mkdir -p /var/log/nginx/ \ + && ln -sf /dev/stdout /var/log/nginx/access.log \ + && ln -sf /dev/stderr /var/log/nginx/error.log + +COPY ./ops/docker/rpc-proxy/eth-jsonrpc-access.lua /usr/local/openresty/nginx/eth-jsonrpc-access.lua +COPY ./ops/docker/rpc-proxy/nginx.template.conf /docker-entrypoint.d/nginx.template.conf +COPY ./ops/docker/rpc-proxy/docker-entrypoint.sh /docker-entrypoint.sh +ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/ops/docker/rpc-proxy/docker-entrypoint.sh b/ops/docker/rpc-proxy/docker-entrypoint.sh new file mode 100755 index 000000000000..477f1dd8f934 --- /dev/null +++ b/ops/docker/rpc-proxy/docker-entrypoint.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -eo pipefail + +if [ -z "$SEQUENCER" ];then + echo "SEQUENCER env must be set, exiting" + exit 1 +fi + +if [ -z "$ETH_CALLS_ALLOWED" ];then + echo "ETH_CALLS_ALLOWED env must be set, exiting" + exit 1 +fi + +gomplate -f /docker-entrypoint.d/nginx.template.conf > /usr/local/openresty/nginx/conf/nginx.conf + +cat /usr/local/openresty/nginx/conf/nginx.conf + +exec openresty "$@" diff --git a/ops/docker/rpc-proxy/eth-jsonrpc-access.lua b/ops/docker/rpc-proxy/eth-jsonrpc-access.lua new file mode 100644 index 000000000000..3f2280ec815f --- /dev/null +++ b/ops/docker/rpc-proxy/eth-jsonrpc-access.lua @@ -0,0 +1,91 @@ +-- Source: https://github.com/adetante/ethereum-nginx-proxy +local cjson = require('cjson') + +local function empty(s) + return s == nil or s == '' +end + +local function split(s) + local res = {} + local i = 1 + for v in string.gmatch(s, "([^,]+)") do + res[i] = v + i = i + 1 + end + return res +end + +local function contains(arr, val) + for i, v in ipairs (arr) do + if v == val then + return true + end + end + return false +end + +-- parse conf +local blacklist, whitelist = nil +if not empty(ngx.var.jsonrpc_blacklist) then + blacklist = split(ngx.var.jsonrpc_blacklist) +end +if not empty(ngx.var.jsonrpc_whitelist) then + whitelist = split(ngx.var.jsonrpc_whitelist) +end + +-- check conf +if blacklist ~= nil and whitelist ~= nil then + ngx.log(ngx.ERR, 'invalid conf: jsonrpc_blacklist and jsonrpc_whitelist are both set') + ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) + return +end + +-- get request content +ngx.req.read_body() + +-- try to parse the body as JSON +local success, body = pcall(cjson.decode, ngx.var.request_body); +if not success then + ngx.log(ngx.ERR, 'invalid JSON request') + ngx.exit(ngx.HTTP_BAD_REQUEST) + return +end + +local method = body['method'] +local version = body['jsonrpc'] + +-- check we have a method and a version +if empty(method) or empty(version) then + ngx.log(ngx.ERR, 'no method and/or jsonrpc attribute') + ngx.exit(ngx.HTTP_BAD_REQUEST) + return +end + +metric_sequencer_requests:inc(1, {method, ngx.var.server_name, ngx.var.status}) + +-- check the version is supported +if version ~= "2.0" then + ngx.log(ngx.ERR, 'jsonrpc version not supported: ' .. version) + ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) + return +end + +-- if whitelist is configured, check that the method is whitelisted +if whitelist ~= nil then + if not contains(whitelist, method) then + ngx.log(ngx.ERR, 'jsonrpc method is not whitelisted: ' .. method) + ngx.exit(ngx.HTTP_FORBIDDEN) + return + end +end + +-- if blacklist is configured, check that the method is not blacklisted +if blacklist ~= nil then + if contains(blacklist, method) then + ngx.log(ngx.ERR, 'jsonrpc method is blacklisted: ' .. method) + ngx.exit(ngx.HTTP_FORBIDDEN) + return + end +end + +return diff --git a/ops/docker/rpc-proxy/nginx.template.conf b/ops/docker/rpc-proxy/nginx.template.conf new file mode 100644 index 000000000000..4530b5ddb849 --- /dev/null +++ b/ops/docker/rpc-proxy/nginx.template.conf @@ -0,0 +1,80 @@ +worker_processes 5; +daemon off; +error_log /var/log/nginx/error.log; +worker_rlimit_nofile 8192; +pcre_jit on; + +events { + worker_connections 4096; +} + +http { + include mime.types; + index index.html; + + # See Move default writable paths to a dedicated directory (#119) + # https://github.com/openresty/docker-openresty/issues/119 + client_body_temp_path /var/run/openresty/nginx-client-body; + proxy_temp_path /var/run/openresty/nginx-proxy; + fastcgi_temp_path /var/run/openresty/nginx-fastcgi; + uwsgi_temp_path /var/run/openresty/nginx-uwsgi; + scgi_temp_path /var/run/openresty/nginx-scgi; + + keepalive_timeout 0; + + default_type application/octet-stream; + log_format main '$remote_addr - $remote_user [$time_local] $status ' + '"$request" $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /var/log/nginx/access.log main; + sendfile on; + tcp_nopush on; + + lua_shared_dict prometheus_metrics 10M; + init_worker_by_lua_block { + prometheus = require("prometheus").init("prometheus_metrics") + metric_requests = prometheus:counter( + "nginx_http_requests_total", "Number of HTTP requests", {"host", "status"}) + metric_sequencer_requests = prometheus:counter( + "nginx_eth_sequencer_requests", "Number of requests going to the sequencer", {"method", "host", "status"}) + metric_replica_requests = prometheus:counter( + "nginx_eth_replica_requests", "Number of requests going to the replicas", {"host", "status"}) + metric_latency = prometheus:histogram( + "nginx_http_request_duration_seconds", "HTTP request latency", {"host"}) + metric_connections = prometheus:gauge( + "nginx_http_connections", "Number of HTTP connections", {"state"}) + } + log_by_lua_block { + metric_requests:inc(1, {ngx.var.server_name, ngx.var.status}) + metric_latency:observe(tonumber(ngx.var.request_time), {ngx.var.server_name}) + } + + upstream sequencer { + server {{env.Getenv "SEQUENCER"}}; + } + + server { # RPC proxy server + listen 8080; + location = /healthz { + return 200 'healthz'; + } + location / { + set $jsonrpc_whitelist {{env.Getenv "ETH_CALLS_ALLOWED"}}; + access_by_lua_file 'eth-jsonrpc-access.lua'; + proxy_pass http://sequencer; + } + } + + server { # Metrics server + listen 9145; + location /metrics { + content_by_lua_block { + metric_connections:set(ngx.var.connections_reading, {"reading"}) + metric_connections:set(ngx.var.connections_waiting, {"waiting"}) + metric_connections:set(ngx.var.connections_writing, {"writing"}) + prometheus:collect() + } + } + } + +} \ No newline at end of file From 8e2bfd07ee99a37d9f4e79856dc822ce773cc6de Mon Sep 17 00:00:00 2001 From: Karl Floersch Date: Wed, 2 Jun 2021 14:48:15 -0400 Subject: [PATCH 17/46] feat: deployment config for fee oracle contract (#936) * feat[contracts]: add GasPriceOracle w/o predeploy Based on #912 * feat[contracts]: congestion price oracle * chore: add changeset * contracts: gas price oracle (#917) * contracts: gas price oracle * tests: update * fees: fix tests * contracts: simplify gas price oracle * lint: fix * test: execution price is at the 1st storage slot * chore: rename predeploy to GasPriceOracle * chore: rename gas price oracle test name Co-authored-by: Mark Tyneway Co-authored-by: Georgios Konstantopoulos * Add an L2 deploy script for gas oracle contract * Add a kovan deployment artifact * Add deployment to readme * Add extra validation & initial execution price * Update README.md * Fix execution price logic * Perform new deployment with final contract * contracts: better require in ovm gas price oracle * Deploy L2GasPriceOracle * Update contract to use new fee logic & rename to gas * Deploy updated contract * Fix lint * gas price oracle: do not restrict gas price * gas price oracle: new deployment * tests: delete dead test Co-authored-by: smartcontracts Co-authored-by: Mark Tyneway Co-authored-by: Georgios Konstantopoulos --- .changeset/seven-carpets-tell.md | 5 + .../OVM/predeploys/OVM_GasPriceOracle.sol | 60 +++++++ .../000-OVM_GasPriceOracle.deploy.ts | 31 ++++ packages/contracts/deployments/README.md | 9 + .../deployments/optimistic-kovan/.chainId | 1 + .../optimistic-kovan/OVM_GasPriceOracle.json | 164 ++++++++++++++++++ packages/contracts/tasks/deploy.ts | 6 + .../precompiles/OVM_GasPriceOracle.spec.ts | 80 +++++++++ 8 files changed, 356 insertions(+) create mode 100644 .changeset/seven-carpets-tell.md create mode 100644 packages/contracts/contracts/optimistic-ethereum/OVM/predeploys/OVM_GasPriceOracle.sol create mode 100644 packages/contracts/deploy-l2/000-OVM_GasPriceOracle.deploy.ts create mode 100644 packages/contracts/deployments/optimistic-kovan/.chainId create mode 100644 packages/contracts/deployments/optimistic-kovan/OVM_GasPriceOracle.json create mode 100644 packages/contracts/test/contracts/OVM/precompiles/OVM_GasPriceOracle.spec.ts diff --git a/.changeset/seven-carpets-tell.md b/.changeset/seven-carpets-tell.md new file mode 100644 index 000000000000..9be51c0a6974 --- /dev/null +++ b/.changeset/seven-carpets-tell.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/contracts': patch +--- + +Introduces the congestion price oracle contract diff --git a/packages/contracts/contracts/optimistic-ethereum/OVM/predeploys/OVM_GasPriceOracle.sol b/packages/contracts/contracts/optimistic-ethereum/OVM/predeploys/OVM_GasPriceOracle.sol new file mode 100644 index 000000000000..03cc96a860b0 --- /dev/null +++ b/packages/contracts/contracts/optimistic-ethereum/OVM/predeploys/OVM_GasPriceOracle.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity >0.5.0 <0.8.0; + +/* External Imports */ +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; + +/** + * @title OVM_GasPriceOracle + * @dev This contract exposes the current l2 gas price, a measure of how congested the network + * currently is. This measure is used by the Sequencer to determine what fee to charge for + * transactions. When the system is more congested, the l2 gas price will increase and fees + * will also increase as a result. + * + * Compiler used: optimistic-solc + * Runtime target: OVM + */ +contract OVM_GasPriceOracle is Ownable { + + /************* + * Variables * + *************/ + + // Current l2 gas price + uint256 public gasPrice; + + /*************** + * Constructor * + ***************/ + + /** + * @param _owner Address that will initially own this contract. + */ + constructor( + address _owner, + uint256 _initialGasPrice + ) + Ownable() + { + setGasPrice(_initialGasPrice); + transferOwnership(_owner); + } + + + /******************** + * Public Functions * + ********************/ + + /** + * Allows the owner to modify the l2 gas price. + * @param _gasPrice New l2 gas price. + */ + function setGasPrice( + uint256 _gasPrice + ) + public + onlyOwner + { + gasPrice = _gasPrice; + } +} diff --git a/packages/contracts/deploy-l2/000-OVM_GasPriceOracle.deploy.ts b/packages/contracts/deploy-l2/000-OVM_GasPriceOracle.deploy.ts new file mode 100644 index 000000000000..96eb8fd1b175 --- /dev/null +++ b/packages/contracts/deploy-l2/000-OVM_GasPriceOracle.deploy.ts @@ -0,0 +1,31 @@ +/* Imports: External */ +import { DeployFunction } from 'hardhat-deploy/dist/types' + +/* Imports: Internal */ +import { getContractDefinition } from '../src' + +const deployFn: DeployFunction = async (hre: any) => { + const { deployments, getNamedAccounts } = hre + const { deploy } = deployments + const { deployer } = await getNamedAccounts() + + const gasPriceOracle = getContractDefinition('OVM_GasPriceOracle', true) + + const gasOracleOwner = (hre as any).deployConfig.ovmSequencerAddress + const initialGasPrice = (hre as any).deployConfig.initialGasPriceOracleGasPrice + + if (!gasOracleOwner || !initialGasPrice) { + throw new Error('initialGasPrice & ovmSequencerAddress required to deploy gas price oracle') + } + + await deploy('OVM_GasPriceOracle', { + contract: gasPriceOracle, + from: deployer, + args: [gasOracleOwner, initialGasPrice], + log: true, + }); +} + +deployFn.tags = ['OVM_GasPriceOracle'] + +export default deployFn diff --git a/packages/contracts/deployments/README.md b/packages/contracts/deployments/README.md index 133f6ba27b34..a6d5b7156e9b 100644 --- a/packages/contracts/deployments/README.md +++ b/packages/contracts/deployments/README.md @@ -1,6 +1,15 @@ # Optimism Regenesis Deployments ## LAYER 2 +## OPTIMISTIC-KOVAN + +Network : __optimistic-kovan (chain id: 69)__ + +|Contract|Address| +|--|--| +|OVM_GasPriceOracle|[0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76](https://kovan-optimistic.etherscan.io/address/0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76)| +--- + ### Chain IDs: - Mainnet: 10 - Kovan: 69 diff --git a/packages/contracts/deployments/optimistic-kovan/.chainId b/packages/contracts/deployments/optimistic-kovan/.chainId new file mode 100644 index 000000000000..8c0474e3239f --- /dev/null +++ b/packages/contracts/deployments/optimistic-kovan/.chainId @@ -0,0 +1 @@ +69 \ No newline at end of file diff --git a/packages/contracts/deployments/optimistic-kovan/OVM_GasPriceOracle.json b/packages/contracts/deployments/optimistic-kovan/OVM_GasPriceOracle.json new file mode 100644 index 000000000000..3ad5351c0abc --- /dev/null +++ b/packages/contracts/deployments/optimistic-kovan/OVM_GasPriceOracle.json @@ -0,0 +1,164 @@ +{ + "address": "0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_initialGasPrice", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "gasPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_gasPrice", + "type": "uint256" + } + ], + "name": "setGasPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "transactionHash": "0xed5fd0757566bc0bc1f3f7d701e31199835d6fe7b1e74353ad502983f2f5e744", + "receipt": { + "to": null, + "from": "0x18394B52d3Cb931dfA76F63251919D051953413d", + "contractAddress": "0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76", + "transactionIndex": 0, + "gasUsed": "1732518", + "logsBloom": "0x00000000000000000000000000000000000000000000000000840000000000000000000000000000000000100000000000000000000000140000000000000000000000000100000000000008000000000001000010000000000000000000000400000000020000000000000000008800000000000000000000400010000000400000000000000000000000000000000000000000002000000000000000000000000000000000000000010000000000000000000000000000000000000000000000008002000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x310208064b53df696581d48cee7439d3e94acac8c31a519e5d07e7cc542c920c", + "transactionHash": "0xed5fd0757566bc0bc1f3f7d701e31199835d6fe7b1e74353ad502983f2f5e744", + "logs": [ + { + "transactionIndex": 0, + "blockNumber": 336546, + "transactionHash": "0xed5fd0757566bc0bc1f3f7d701e31199835d6fe7b1e74353ad502983f2f5e744", + "address": "0x4200000000000000000000000000000000000006", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x00000000000000000000000018394b52d3cb931dfa76f63251919d051953413d", + "0x0000000000000000000000004200000000000000000000000000000000000005" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000", + "logIndex": 0, + "blockHash": "0x310208064b53df696581d48cee7439d3e94acac8c31a519e5d07e7cc542c920c" + }, + { + "transactionIndex": 0, + "blockNumber": 336546, + "transactionHash": "0xed5fd0757566bc0bc1f3f7d701e31199835d6fe7b1e74353ad502983f2f5e744", + "address": "0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000018394b52d3cb931dfa76f63251919d051953413d" + ], + "data": "0x", + "logIndex": 1, + "blockHash": "0x310208064b53df696581d48cee7439d3e94acac8c31a519e5d07e7cc542c920c" + }, + { + "transactionIndex": 0, + "blockNumber": 336546, + "transactionHash": "0xed5fd0757566bc0bc1f3f7d701e31199835d6fe7b1e74353ad502983f2f5e744", + "address": "0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x00000000000000000000000018394b52d3cb931dfa76f63251919d051953413d", + "0x00000000000000000000000018394b52d3cb931dfa76f63251919d051953413d" + ], + "data": "0x", + "logIndex": 2, + "blockHash": "0x310208064b53df696581d48cee7439d3e94acac8c31a519e5d07e7cc542c920c" + } + ], + "blockNumber": 336546, + "cumulativeGasUsed": "1732518", + "status": 1, + "byzantium": true + }, + "args": [ + "0x18394B52d3Cb931dfA76F63251919D051953413d", + 1000000 + ], + "bytecode": "0x60806040523480156200001c5760008062000019620002ea565b50505b5060405162000a7338038062000a73833981810160405260408110156200004d576000806200004a620002ea565b50505b81019080805192919060200180519250600091506200006d9050620000ec565b90508060006001816200007f62000357565b816001600160a01b0302191690836001600160a01b0316021790620000a3620003b9565b5050506001600160a01b038116600060008051602062000a5383398151915260405160405180910390a350620000d981620000fe565b620000e48262000184565b50506200044f565b60005a620000f962000408565b905090565b62000108620000ec565b6001600160a01b03166200011b620002c7565b6001600160a01b031614620001705760405162461bcd60e51b8152602060048201819052602482015260008051602062000a338339815191526044820152606401604051809103906200016d620002ea565b50505b808060016200017e620003b9565b50505050565b6200018e620000ec565b6001600160a01b0316620001a1620002c7565b6001600160a01b031614620001f65760405162461bcd60e51b8152602060048201819052602482015260008051602062000a33833981519152604482015260640160405180910390620001f3620002ea565b50505b6001600160a01b038116620002485760405162461bcd60e51b815260040180806020018281038252602681526020018062000a0d602691396040019150506040518091039062000245620002ea565b50505b806001600160a01b03166000806200025f62000357565b906101000a90046001600160a01b03166001600160a01b031660008051602062000a5383398151915260405160405180910390a3806000600181620002a362000357565b816001600160a01b0302191690836001600160a01b03160217906200017e620003b9565b60008080620002d562000357565b906101000a90046001600160a01b0316905090565b632a2a7adb598160e01b8152600481016020815285602082015260005b868110156200032457808601518282016040015260200162000307565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b6040811015620003b4576000828201526020016200039b565b505050565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b6000815260206200039b565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b805160008252935060206200039b565b6105ae806200045f6000396000f3fe60806040523480156100195760008061001661042d565b50505b50600436106100605760003560e01c8063715018a61461006e5780638da5cb5b14610078578063bf1fe4201461009c578063f2fde38b146100c2578063fe173b97146100f1575b60008061006b61042d565b50505b61007661010b565b005b61008061020d565b6040516001600160a01b03909116815260200160405180910390f35b610076600480360360208110156100bb576000806100b861042d565b50505b503561022e565b610076600480360360208110156100e1576000806100de61042d565b50505b50356001600160a01b03166102bc565b6100f9610410565b60405190815260200160405180910390f35b61011361041d565b6001600160a01b031661012461020d565b6001600160a01b0316146101875760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016040518091039061018461042d565b50505b60008080610193610498565b906101000a90046001600160a01b03166001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a36000806001816101e6610498565b816001600160a01b0302191690836001600160a01b03160217906102086104f3565b505050565b60008080610219610498565b906101000a90046001600160a01b0316905090565b61023661041d565b6001600160a01b031661024761020d565b6001600160a01b0316146102aa5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401604051809103906102a761042d565b50505b808060016102b66104f3565b50505050565b6102c461041d565b6001600160a01b03166102d561020d565b6001600160a01b0316146103385760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016040518091039061033561042d565b50505b6001600160a01b0381166103865760405162461bcd60e51b8152600401808060200182810382526026815260200180610588602691396040019150506040518091039061038361042d565b50505b806001600160a01b031660008061039b610498565b906101000a90046001600160a01b03166001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a38060006001816103ee610498565b816001600160a01b0302191690836001600160a01b03160217906102b66104f3565b600161041a610498565b81565b60005a610428610541565b905090565b632a2a7adb598160e01b8152600481016020815285602082015260005b8681101561046557808601518282016040015260200161044a565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b6040811015610208576000828201526020016104dc565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b6000815260206104dc565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b805160008252935060206104dc56fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573734f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573734f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65728be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "deployedBytecode": "0x60806040523480156100195760008061001661042d565b50505b50600436106100605760003560e01c8063715018a61461006e5780638da5cb5b14610078578063bf1fe4201461009c578063f2fde38b146100c2578063fe173b97146100f1575b60008061006b61042d565b50505b61007661010b565b005b61008061020d565b6040516001600160a01b03909116815260200160405180910390f35b610076600480360360208110156100bb576000806100b861042d565b50505b503561022e565b610076600480360360208110156100e1576000806100de61042d565b50505b50356001600160a01b03166102bc565b6100f9610410565b60405190815260200160405180910390f35b61011361041d565b6001600160a01b031661012461020d565b6001600160a01b0316146101875760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016040518091039061018461042d565b50505b60008080610193610498565b906101000a90046001600160a01b03166001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a36000806001816101e6610498565b816001600160a01b0302191690836001600160a01b03160217906102086104f3565b505050565b60008080610219610498565b906101000a90046001600160a01b0316905090565b61023661041d565b6001600160a01b031661024761020d565b6001600160a01b0316146102aa5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401604051809103906102a761042d565b50505b808060016102b66104f3565b50505050565b6102c461041d565b6001600160a01b03166102d561020d565b6001600160a01b0316146103385760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016040518091039061033561042d565b50505b6001600160a01b0381166103865760405162461bcd60e51b8152600401808060200182810382526026815260200180610588602691396040019150506040518091039061038361042d565b50505b806001600160a01b031660008061039b610498565b906101000a90046001600160a01b03166001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a38060006001816103ee610498565b816001600160a01b0302191690836001600160a01b03160217906102b66104f3565b600161041a610498565b81565b60005a610428610541565b905090565b632a2a7adb598160e01b8152600481016020815285602082015260005b8681101561046557808601518282016040015260200161044a565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b6040811015610208576000828201526020016104dc565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b6000815260206104dc565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b805160008252935060206104dc56fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373" +} \ No newline at end of file diff --git a/packages/contracts/tasks/deploy.ts b/packages/contracts/tasks/deploy.ts index 35d2e041b692..8c3a793b062b 100644 --- a/packages/contracts/tasks/deploy.ts +++ b/packages/contracts/tasks/deploy.ts @@ -99,6 +99,12 @@ task('deploy') undefined, types.string ) + .addOptionalParam( + 'initialGasPriceOracleGasPrice', + 'The initial execution price for the gas price oracle.', + undefined, + types.int + ) .setAction(async (args, hre: any, runSuper) => { // Necessary because hardhat doesn't let us attach non-optional parameters to existing tasks. const validateAddressArg = (argName: string) => { diff --git a/packages/contracts/test/contracts/OVM/precompiles/OVM_GasPriceOracle.spec.ts b/packages/contracts/test/contracts/OVM/precompiles/OVM_GasPriceOracle.spec.ts new file mode 100644 index 000000000000..ead0baebf899 --- /dev/null +++ b/packages/contracts/test/contracts/OVM/precompiles/OVM_GasPriceOracle.spec.ts @@ -0,0 +1,80 @@ +import { expect } from '../../../setup' + +/* External Imports */ +import { ethers } from 'hardhat' +import { ContractFactory, Contract, Signer } from 'ethers' + +describe('OVM_GasPriceOracle', () => { + const initialGasPrice = 0 + let signer1: Signer + let signer2: Signer + before(async () => { + ;[signer1, signer2] = await ethers.getSigners() + }) + + let Factory__OVM_GasPriceOracle: ContractFactory + before(async () => { + Factory__OVM_GasPriceOracle = await ethers.getContractFactory( + 'OVM_GasPriceOracle' + ) + }) + + let OVM_GasPriceOracle: Contract + beforeEach(async () => { + OVM_GasPriceOracle = await Factory__OVM_GasPriceOracle.deploy( + await signer1.getAddress(), + initialGasPrice + ) + }) + + describe('owner', () => { + it('should have an owner', async () => { + expect(await OVM_GasPriceOracle.owner()).to.equal( + await signer1.getAddress() + ) + }) + }) + + describe('setGasPrice', () => { + it('should revert if called by someone other than the owner', async () => { + await expect(OVM_GasPriceOracle.connect(signer2).setGasPrice(1234)).to.be + .reverted + }) + + it('should succeed if called by the owner and is equal to `0`', async () => { + await expect(OVM_GasPriceOracle.connect(signer1).setGasPrice(0)).to.not.be + .reverted + }) + }) + + describe('get gasPrice', () => { + it('should return zero at first', async () => { + expect(await OVM_GasPriceOracle.gasPrice()).to.equal(initialGasPrice) + }) + + it('should change when setGasPrice is called', async () => { + const gasPrice = 1234 + + await OVM_GasPriceOracle.connect(signer1).setGasPrice(gasPrice) + + expect(await OVM_GasPriceOracle.gasPrice()).to.equal(gasPrice) + }) + + it('is the 1st storage slot', async () => { + const gasPrice = 1234 + const slot = 1 + + // set the price + await OVM_GasPriceOracle.connect(signer1).setGasPrice(gasPrice) + + // get the storage slot value + const priceAtSlot = await signer1.provider.getStorageAt( + OVM_GasPriceOracle.address, + slot + ) + expect(await OVM_GasPriceOracle.gasPrice()).to.equal( + ethers.BigNumber.from(priceAtSlot) + ) + }) + }) +}) From b355be0b23ab80d9cdcb0b1f481eaeec02412b20 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Wed, 2 Jun 2021 14:30:56 -0700 Subject: [PATCH 18/46] ops: expose debug namespace (#1007) --- ops/envs/geth.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ops/envs/geth.env b/ops/envs/geth.env index bf73226cf815..59519125b17b 100644 --- a/ops/envs/geth.env +++ b/ops/envs/geth.env @@ -12,7 +12,7 @@ ROLLUP_ENABLE_L2_GAS_POLLING=true RPC_ENABLE=true RPC_ADDR=0.0.0.0 RPC_PORT=8545 -RPC_API=eth,net,rollup,web3 +RPC_API=eth,net,rollup,web3,debug RPC_CORS_DOMAIN=* RPC_VHOSTS=* From 5e4eaea1cc748d803a89b13160b92d61a5201c0d Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Thu, 3 Jun 2021 18:21:10 +0300 Subject: [PATCH 19/46] fix(sync-service): prevent underflows (#1015) * fix(sync-service): prevent underflows * chore: add changeset * chore: remove dead confirmation depth * chore: remove eth1conf depth from rollup config --- .changeset/kind-houses-rush.md | 5 +++++ l2geth/rollup/config.go | 2 -- l2geth/rollup/sync_service.go | 13 ++++++++----- 3 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 .changeset/kind-houses-rush.md diff --git a/.changeset/kind-houses-rush.md b/.changeset/kind-houses-rush.md new file mode 100644 index 000000000000..1819a8f8c93e --- /dev/null +++ b/.changeset/kind-houses-rush.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/l2geth': patch +--- + +fix potential underflow when launching the chain when the last verified index is 0 diff --git a/l2geth/rollup/config.go b/l2geth/rollup/config.go index 22d9b3f35ce5..2ce695201645 100644 --- a/l2geth/rollup/config.go +++ b/l2geth/rollup/config.go @@ -10,8 +10,6 @@ import ( type Config struct { // Maximum calldata size for a Queue Origin Sequencer Tx MaxCallDataSize int - // Number of confs before applying a L1 to L2 tx - Eth1ConfirmationDepth uint64 // Verifier mode IsVerifier bool // Enable the sync service diff --git a/l2geth/rollup/sync_service.go b/l2geth/rollup/sync_service.go index 98460aa7387a..473acff61afe 100644 --- a/l2geth/rollup/sync_service.go +++ b/l2geth/rollup/sync_service.go @@ -53,7 +53,6 @@ type SyncService struct { syncing atomic.Value chainHeadSub event.Subscription OVMContext OVMContext - confirmationDepth uint64 pollInterval time.Duration timestampRefreshThreshold time.Duration chainHeadCh chan core.ChainHeadEvent @@ -103,7 +102,6 @@ func NewSyncService(ctx context.Context, cfg Config, txpool *core.TxPool, bc *co cancel: cancel, verifier: cfg.IsVerifier, enable: cfg.Eth1SyncServiceEnable, - confirmationDepth: cfg.Eth1ConfirmationDepth, syncing: atomic.Value{}, bc: bc, txpool: txpool, @@ -244,8 +242,12 @@ func (s *SyncService) initializeLatestL1(ctcDeployHeight *big.Int) error { s.SetLatestL1Timestamp(context.Timestamp) s.SetLatestL1BlockNumber(context.BlockNumber) } else { + // Prevent underflows + if *index != 0 { + *index = *index - 1 + } log.Info("Found latest index", "index", *index) - block := s.bc.GetBlockByNumber(*index - 1) + block := s.bc.GetBlockByNumber(*index) if block == nil { block = s.bc.CurrentBlock() idx := block.Number().Uint64() @@ -254,11 +256,12 @@ func (s *SyncService) initializeLatestL1(ctcDeployHeight *big.Int) error { return fmt.Errorf("Current block height greater than index") } s.SetLatestIndex(&idx) - log.Info("Block not found, resetting index", "new", idx, "old", *index-1) + log.Info("Block not found, resetting index", "new", idx, "old", *index) } txs := block.Transactions() if len(txs) != 1 { - log.Error("Unexpected number of transactions in block: %d", len(txs)) + log.Error("Unexpected number of transactions in block", "count", len(txs)) + panic("Cannot recover OVM Context") } tx := txs[0] s.SetLatestL1Timestamp(tx.L1Timestamp()) From d2ad599755f0cc6b166d86ac21af81b91dd3c773 Mon Sep 17 00:00:00 2001 From: Liam Horne Date: Thu, 3 Jun 2021 11:21:37 -0400 Subject: [PATCH 20/46] test: remove duplicate value in array (#1014) --- packages/contracts/test/helpers/test-runner/test.types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/contracts/test/helpers/test-runner/test.types.ts b/packages/contracts/test/helpers/test-runner/test.types.ts index fa799dd6b4e1..b31b03d17318 100644 --- a/packages/contracts/test/helpers/test-runner/test.types.ts +++ b/packages/contracts/test/helpers/test-runner/test.types.ts @@ -213,7 +213,6 @@ export const isTestStep_Context = ( 'ovmCALLER', 'ovmNUMBER', 'ovmADDRESS', - 'ovmNUMBER', 'ovmL1TXORIGIN', 'ovmTIMESTAMP', 'ovmGASLIMIT', From 1c1e405cc53c0ef5c0f3e6db471a69a6f1f076fa Mon Sep 17 00:00:00 2001 From: Liam Horne Date: Thu, 3 Jun 2021 13:52:11 -0400 Subject: [PATCH 21/46] ci: tag docker image for canary with abbreviated GITHUB_SHA (#1006) * ci: tag docker image for canary with abbreviated GITHUB_SHA * ci: update from 6 bytes to 8 bytes of abbreviation --- .github/workflows/publish-canary.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish-canary.yml b/.github/workflows/publish-canary.yml index 67e53a6b54c9..5669f25dabd7 100644 --- a/.github/workflows/publish-canary.yml +++ b/.github/workflows/publish-canary.yml @@ -155,7 +155,7 @@ jobs: context: . file: ./ops/docker/Dockerfile.message-relayer push: true - tags: ethereumoptimism/message-relayer:${{ needs.builder.outputs.message-relayer }} + tags: ethereumoptimism/message-relayer:${{ GITHUB_SHA::8 }} batch-submitter: name: Publish Batch Submitter Version ${{ needs.builder.outputs.batch-submitter }} @@ -181,7 +181,7 @@ jobs: context: . file: ./ops/docker/Dockerfile.batch-submitter push: true - tags: ethereumoptimism/batch-submitter:${{ needs.builder.outputs.batch-submitter }} + tags: ethereumoptimism/batch-submitter:${{ GITHUB_SHA::8 }} data-transport-layer: name: Publish Data Transport Layer Version ${{ needs.builder.outputs.data-transport-layer }} @@ -207,7 +207,7 @@ jobs: context: . file: ./ops/docker/Dockerfile.data-transport-layer push: true - tags: ethereumoptimism/data-transport-layer:${{ needs.builder.outputs.data-transport-layer }} + tags: ethereumoptimism/data-transport-layer:${{ GITHUB_SHA::8 }} contracts: name: Publish Deployer Version ${{ needs.builder.outputs.contracts }} @@ -233,7 +233,7 @@ jobs: context: . file: ./ops/docker/Dockerfile.deployer push: true - tags: ethereumoptimism/deployer:${{ needs.builder.outputs.contracts }} + tags: ethereumoptimism/deployer:${{ GITHUB_SHA::8 }} integration_tests: name: Publish Integration tests ${{ needs.builder.outputs.integration-tests }} @@ -259,4 +259,4 @@ jobs: context: . file: ./ops/docker/Dockerfile.integration-tests push: true - tags: ethereumoptimism/integration-tests:${{ needs.builder.outputs.integration-tests }} + tags: ethereumoptimism/integration-tests:${{ GITHUB_SHA::8 }} From 55ee461e3f0a051f23267b2b14f8e2fb9ffdc1c4 Mon Sep 17 00:00:00 2001 From: Liam Horne Date: Thu, 3 Jun 2021 17:32:54 -0400 Subject: [PATCH 22/46] refactor: improve logging for transactions being submitted to chain with gasPrice (#1016) * refactor: improve logging for transactions being submitted to chain with gasPrice * lint: apply lint autofixes --- .../src/batch-submitter/state-batch-submitter.ts | 7 +++++-- .../src/batch-submitter/tx-batch-submitter.ts | 14 ++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/batch-submitter/src/batch-submitter/state-batch-submitter.ts b/packages/batch-submitter/src/batch-submitter/state-batch-submitter.ts index 2ccb68b8a8b9..319dae882081 100644 --- a/packages/batch-submitter/src/batch-submitter/state-batch-submitter.ts +++ b/packages/batch-submitter/src/batch-submitter/state-batch-submitter.ts @@ -178,15 +178,18 @@ export class StateBatchSubmitter extends BatchSubmitter { const nonce = await this.signer.getTransactionCount() const contractFunction = async (gasPrice): Promise => { + this.logger.info('Submitting appendStateBatch transaction', { + gasPrice, + nonce, + contractAddr: this.chainContract.address, + }) const contractTx = await this.chainContract.appendStateBatch( batch, offsetStartsAtIndex, { nonce, gasPrice } ) this.logger.info('Submitted appendStateBatch transaction', { - nonce, txHash: contractTx.hash, - contractAddr: this.chainContract.address, from: contractTx.from, }) this.logger.debug('appendStateBatch transaction data', { diff --git a/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts b/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts index b52be781aa52..bdcc59db48ed 100644 --- a/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts +++ b/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts @@ -143,14 +143,17 @@ export class TransactionBatchSubmitter extends BatchSubmitter { const contractFunction = async ( gasPrice ): Promise => { + this.logger.info('Submitting appendQueueBatch transaction', { + gasPrice, + nonce, + contractAddr: this.chainContract.address, + }) const tx = await this.chainContract.appendQueueBatch(99999999, { nonce, gasPrice, }) this.logger.info('Submitted appendQueueBatch transaction', { - nonce, txHash: tx.hash, - contractAddr: this.chainContract.address, from: tx.from, }) this.logger.debug('appendQueueBatch transaction data', { @@ -250,14 +253,17 @@ export class TransactionBatchSubmitter extends BatchSubmitter { const nonce = await this.signer.getTransactionCount() const contractFunction = async (gasPrice): Promise => { + this.logger.info('Submitting appendSequencerBatch transaction', { + gasPrice, + nonce, + contractAddr: this.chainContract.address, + }) const tx = await this.chainContract.appendSequencerBatch(batchParams, { nonce, gasPrice, }) this.logger.info('Submitted appendSequencerBatch transaction', { - nonce, txHash: tx.hash, - contractAddr: this.chainContract.address, from: tx.from, }) this.logger.debug('appendSequencerBatch transaction data', { From ed9fc79d2a9a4677ae78041b5ffc29e41c696359 Mon Sep 17 00:00:00 2001 From: Liam Horne Date: Fri, 4 Jun 2021 05:47:07 -0400 Subject: [PATCH 23/46] ci: upload logs for failed integration tests (#1020) --- .github/workflows/integration.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index f9ba9f19c20b..2e4697184a05 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -93,3 +93,21 @@ jobs: yarn compile yarn compile:ovm yarn test:integration:ovm + + - name: Collect docker logs on failure + if: failure() + uses: jwalton/gh-docker-logs@v1 + with: + images: 'ethereumoptimism/builder,ethereumoptimism/hardhat,ethereumoptimism/deployer,ethereumoptimism/data-transport-layer,ethereumoptimism/l2geth,ethereumoptimism/message-relayer,ethereumoptimism/batch-submitter,ethereumoptimism/l2geth,ethereumoptimism/integration-tests' + dest: './logs' + + - name: Tar logs + if: failure() + run: tar cvzf ./logs.tgz ./logs + + - name: Upload logs to GitHub + if: failure() + uses: actions/upload-artifact@master + with: + name: logs.tgz + path: ./logs.tgz \ No newline at end of file From 8ac4c74c943c61444b6e414ccb972e2e34b0db6b Mon Sep 17 00:00:00 2001 From: Tim Myers Date: Fri, 4 Jun 2021 03:48:02 -0600 Subject: [PATCH 24/46] fix(dtl): improve slow blocking JSON parsing that occurs during l2 sync (#1019) The use of eth_getBlockRange returns a large response which is very slow to parse in ethersjs, and can block the event loop for upwards of multiple seconds. When this happens, incoming http requests will likely timeout and fail. Instead, we will parse the incoming http stream directly with the bfj package, which yields the event loop periodically so that we don't fail to serve requests. --- .changeset/wet-falcons-talk.md | 5 +++ packages/data-transport-layer/package.json | 2 ++ .../src/services/l2-ingestion/service.ts | 28 ++++++++++++--- yarn.lock | 34 ++++++++++++++++++- 4 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 .changeset/wet-falcons-talk.md diff --git a/.changeset/wet-falcons-talk.md b/.changeset/wet-falcons-talk.md new file mode 100644 index 000000000000..7698788db0e9 --- /dev/null +++ b/.changeset/wet-falcons-talk.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/data-transport-layer': patch +--- + +improve slow blocking JSON parsing that occurs during l2 sync diff --git a/packages/data-transport-layer/package.json b/packages/data-transport-layer/package.json index c959e434e2c1..da0ca5267abf 100644 --- a/packages/data-transport-layer/package.json +++ b/packages/data-transport-layer/package.json @@ -30,6 +30,7 @@ "@sentry/tracing": "^6.3.1", "@types/express": "^4.17.11", "bcfg": "^0.1.6", + "bfj": "^7.0.2", "browser-or-node": "^1.3.0", "cors": "^2.8.5", "dotenv": "^8.2.0", @@ -49,6 +50,7 @@ "@types/levelup": "^4.3.0", "@types/mocha": "^8.2.2", "@types/node-fetch": "^2.5.8", + "@types/workerpool": "^6.0.0", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", "hardhat": "^2.2.1", diff --git a/packages/data-transport-layer/src/services/l2-ingestion/service.ts b/packages/data-transport-layer/src/services/l2-ingestion/service.ts index 120bb35ad734..6a32bc301a58 100644 --- a/packages/data-transport-layer/src/services/l2-ingestion/service.ts +++ b/packages/data-transport-layer/src/services/l2-ingestion/service.ts @@ -3,6 +3,8 @@ import { BaseService } from '@eth-optimism/common-ts' import { JsonRpcProvider } from '@ethersproject/providers' import { BigNumber } from 'ethers' import { LevelUp } from 'levelup' +import axios from 'axios'; +import bfj from 'bfj'; /* Imports: Internal */ import { TransportDB } from '../../db/transport-db' @@ -168,11 +170,27 @@ export class L2IngestionService extends BaseService { ) }) } else { - blocks = await this.state.l2RpcProvider.send('eth_getBlockRange', [ - toRpcHexString(startBlockNumber), - toRpcHexString(endBlockNumber), - true, - ]) + // This request returns a large response. Parsing it into JSON inside the ethers library is + // quite slow, and can block the event loop for upwards of multiple seconds. When this happens, + // incoming http requests will likely timeout and fail. + // Instead, we will parse the incoming http stream directly with the bfj package, which yields + // the event loop periodically so that we don't fail to serve requests. + const req = { + jsonrpc: '2.0', + method: 'eth_getBlockRange', + params: [ + toRpcHexString(startBlockNumber), + toRpcHexString(endBlockNumber), + true, + ], + id: '1', + }; + + const resp = await axios.post(this.state.l2RpcProvider.connection.url, req, {responseType: 'stream'}); + const respJson = await bfj.parse(resp.data, { + yieldRate: 4096 // this yields abit more often than the default of 16384 + }); + const blocks = respJson.data; } for (const block of blocks) { diff --git a/yarn.lock b/yarn.lock index 678b6dfabc8f..1be52e5f1a8e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2601,6 +2601,13 @@ "@types/bn.js" "*" "@types/underscore" "*" +"@types/workerpool@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@types/workerpool/-/workerpool-6.0.0.tgz#068c31191f7df9b3d49ebe348b1eeb601e75e2d3" + integrity sha512-BjbKVHFBWblQ3vZ5yFq29kbM2TsaUaTOwYgVxqnNjMrT6CktVF8AvMxOJZgHGgNbAzP4z8DK+EshyZcYpdvAhQ== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "20.2.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" @@ -3657,6 +3664,16 @@ better-path-resolve@1.0.0: dependencies: is-windows "^1.0.0" +bfj@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/bfj/-/bfj-7.0.2.tgz#1988ce76f3add9ac2913fd8ba47aad9e651bfbb2" + integrity sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw== + dependencies: + bluebird "^3.5.5" + check-types "^11.1.1" + hoopy "^0.1.4" + tryer "^1.0.1" + bignumber.js@^9.0.0, bignumber.js@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" @@ -3711,7 +3728,7 @@ blakejs@^1.1.0: resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5" integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U= -bluebird@^3.5.0, bluebird@^3.5.2, bluebird@^3.5.3, bluebird@^3.7.2: +bluebird@^3.5.0, bluebird@^3.5.2, bluebird@^3.5.3, bluebird@^3.5.5, bluebird@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -4190,6 +4207,11 @@ check-error@^1.0.2: resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= +check-types@^11.1.1: + version "11.1.2" + resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.1.2.tgz#86a7c12bf5539f6324eb0e70ca8896c0e38f3e2f" + integrity sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ== + checkpoint-store@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/checkpoint-store/-/checkpoint-store-1.1.0.tgz#04e4cb516b91433893581e6d4601a78e9552ea06" @@ -7304,6 +7326,11 @@ home-or-tmp@^2.0.0: os-homedir "^1.0.0" os-tmpdir "^1.0.1" +hoopy@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" + integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== + hosted-git-info@^2.1.4, hosted-git-info@^2.6.0: version "2.8.8" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" @@ -12743,6 +12770,11 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q== +tryer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" + integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== + ts-essentials@^1.0.0, ts-essentials@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-1.0.4.tgz#ce3b5dade5f5d97cf69889c11bf7d2da8555b15a" From e3dc90cb08fde9b842643efdc5908e54d6065c2c Mon Sep 17 00:00:00 2001 From: Karl Floersch Date: Sun, 6 Jun 2021 05:48:24 -0400 Subject: [PATCH 25/46] fix: lint errors in dtl (#1025) --- packages/data-transport-layer/package.json | 1 + .../src/services/l2-ingestion/service.ts | 20 +++++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/data-transport-layer/package.json b/packages/data-transport-layer/package.json index da0ca5267abf..ac98ed5bdad3 100644 --- a/packages/data-transport-layer/package.json +++ b/packages/data-transport-layer/package.json @@ -29,6 +29,7 @@ "@sentry/node": "^6.3.1", "@sentry/tracing": "^6.3.1", "@types/express": "^4.17.11", + "axios": "^0.21.1", "bcfg": "^0.1.6", "bfj": "^7.0.2", "browser-or-node": "^1.3.0", diff --git a/packages/data-transport-layer/src/services/l2-ingestion/service.ts b/packages/data-transport-layer/src/services/l2-ingestion/service.ts index 6a32bc301a58..fb6434f433c3 100644 --- a/packages/data-transport-layer/src/services/l2-ingestion/service.ts +++ b/packages/data-transport-layer/src/services/l2-ingestion/service.ts @@ -3,8 +3,8 @@ import { BaseService } from '@eth-optimism/common-ts' import { JsonRpcProvider } from '@ethersproject/providers' import { BigNumber } from 'ethers' import { LevelUp } from 'levelup' -import axios from 'axios'; -import bfj from 'bfj'; +import axios from 'axios' +import bfj from 'bfj' /* Imports: Internal */ import { TransportDB } from '../../db/transport-db' @@ -184,13 +184,17 @@ export class L2IngestionService extends BaseService { true, ], id: '1', - }; + } - const resp = await axios.post(this.state.l2RpcProvider.connection.url, req, {responseType: 'stream'}); - const respJson = await bfj.parse(resp.data, { - yieldRate: 4096 // this yields abit more often than the default of 16384 - }); - const blocks = respJson.data; + const resp = await axios.post( + this.state.l2RpcProvider.connection.url, + req, + { responseType: 'stream' } + ) + const respJson = await bfj.parse(resp.data, { + yieldRate: 4096, // this yields abit more often than the default of 16384 + }) + blocks = respJson.data } for (const block of blocks) { From a75f05b7904df73c8e4f04b85614e5629d3da943 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Mon, 7 Jun 2021 14:29:48 -0400 Subject: [PATCH 26/46] fix[dtl]: fix dtl bug breaking verifiers (#1011) * fix[dtl]: fix dtl bug breaking verifiers * tweaks so tests pass * chore: add changeset --- .changeset/quick-pandas-laugh.md | 5 +++++ .../handlers/sequencer-batch-appended.ts | 11 ++++++---- .../src/services/l1-ingestion/service.ts | 13 ++++++++++-- .../l2-ingestion/handlers/transaction.ts | 3 ++- .../src/types/event-handler-types.ts | 3 ++- .../data-transport-layer/src/utils/eth-tx.ts | 9 +++++++++ .../data-transport-layer/src/utils/index.ts | 1 + .../handlers/sequencer-batch-appended.spec.ts | 7 ++++--- .../handlers/state-batch-appended.spec.ts | 11 ++++++++-- .../handlers/transaction-enqueued.spec.ts | 20 +++++++++++++------ .../l2-ingestion/handlers/transaction.spec.ts | 2 ++ 11 files changed, 66 insertions(+), 19 deletions(-) create mode 100644 .changeset/quick-pandas-laugh.md create mode 100644 packages/data-transport-layer/src/utils/eth-tx.ts diff --git a/.changeset/quick-pandas-laugh.md b/.changeset/quick-pandas-laugh.md new file mode 100644 index 000000000000..0faa252664d5 --- /dev/null +++ b/.changeset/quick-pandas-laugh.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/data-transport-layer': patch +--- + +Fixes a bug that prevented verifiers from syncing properly with the DTL diff --git a/packages/data-transport-layer/src/services/l1-ingestion/handlers/sequencer-batch-appended.ts b/packages/data-transport-layer/src/services/l1-ingestion/handlers/sequencer-batch-appended.ts index 0c90fa286277..114e04f3c5c1 100644 --- a/packages/data-transport-layer/src/services/l1-ingestion/handlers/sequencer-batch-appended.ts +++ b/packages/data-transport-layer/src/services/l1-ingestion/handlers/sequencer-batch-appended.ts @@ -20,6 +20,7 @@ import { import { SEQUENCER_ENTRYPOINT_ADDRESS, SEQUENCER_GAS_LIMIT, + parseSignatureVParam, } from '../../../utils' export const handleEventsSequencerBatchAppended: EventHandlerSet< @@ -76,7 +77,7 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet< batchExtraData: batchSubmissionEvent.args._extraData, } }, - parseEvent: (event, extraData) => { + parseEvent: (event, extraData, l2ChainId) => { const transactionEntries: TransactionEntry[] = [] // It's easier to deal with this data if it's a Buffer. @@ -103,7 +104,8 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet< ) const decoded = maybeDecodeSequencerBatchTransaction( - sequencerTransaction + sequencerTransaction, + l2ChainId ) transactionEntries.push({ @@ -234,7 +236,8 @@ const parseSequencerBatchTransaction = ( } const maybeDecodeSequencerBatchTransaction = ( - transaction: Buffer + transaction: Buffer, + l2ChainId: number ): DecodedSequencerBatchTransaction | null => { try { const decodedTx = ethers.utils.parseTransaction(transaction) @@ -247,7 +250,7 @@ const maybeDecodeSequencerBatchTransaction = ( target: toHexString(decodedTx.to), // Maybe null this out for creations? data: toHexString(decodedTx.data), sig: { - v: BigNumber.from(decodedTx.v).toNumber(), + v: parseSignatureVParam(decodedTx.v, l2ChainId), r: toHexString(decodedTx.r), s: toHexString(decodedTx.s), }, diff --git a/packages/data-transport-layer/src/services/l1-ingestion/service.ts b/packages/data-transport-layer/src/services/l1-ingestion/service.ts index 3bc862cf4feb..d682fa5436b8 100644 --- a/packages/data-transport-layer/src/services/l1-ingestion/service.ts +++ b/packages/data-transport-layer/src/services/l1-ingestion/service.ts @@ -3,6 +3,7 @@ import { fromHexString, EventArgsAddressSet } from '@eth-optimism/core-utils' import { BaseService } from '@eth-optimism/common-ts' import { JsonRpcProvider } from '@ethersproject/providers' import { LevelUp } from 'levelup' +import { ethers, constants } from 'ethers' /* Imports: Internal */ import { TransportDB } from '../../db/transport-db' @@ -18,7 +19,6 @@ import { handleEventsTransactionEnqueued } from './handlers/transaction-enqueued import { handleEventsSequencerBatchAppended } from './handlers/sequencer-batch-appended' import { handleEventsStateBatchAppended } from './handlers/state-batch-appended' import { L1DataTransportServiceOptions } from '../main/service' -import { constants } from 'ethers' export interface L1IngestionServiceOptions extends L1DataTransportServiceOptions { @@ -65,6 +65,7 @@ export class L1IngestionService extends BaseService { contracts: OptimismContracts l1RpcProvider: JsonRpcProvider startingL1BlockNumber: number + l2ChainId: number } = {} as any protected async _init(): Promise { @@ -114,6 +115,10 @@ export class L1IngestionService extends BaseService { this.options.addressManager ) + this.state.l2ChainId = ethers.BigNumber.from( + await this.state.contracts.OVM_ExecutionManager.ovmCHAINID() + ).toNumber() + const startingL1BlockNumber = await this.state.db.getStartingL1Block() if (startingL1BlockNumber) { this.state.startingL1BlockNumber = startingL1BlockNumber @@ -295,7 +300,11 @@ export class L1IngestionService extends BaseService { event, this.state.l1RpcProvider ) - const parsedEvent = await handlers.parseEvent(event, extraData) + const parsedEvent = await handlers.parseEvent( + event, + extraData, + this.state.l2ChainId + ) await handlers.storeEvent(parsedEvent, this.state.db) } diff --git a/packages/data-transport-layer/src/services/l2-ingestion/handlers/transaction.ts b/packages/data-transport-layer/src/services/l2-ingestion/handlers/transaction.ts index be127cc2d0dd..0c2bb410b1cd 100644 --- a/packages/data-transport-layer/src/services/l2-ingestion/handlers/transaction.ts +++ b/packages/data-transport-layer/src/services/l2-ingestion/handlers/transaction.ts @@ -13,6 +13,7 @@ import { padHexString, SEQUENCER_ENTRYPOINT_ADDRESS, SEQUENCER_GAS_LIMIT, + parseSignatureVParam, } from '../../../utils' export const handleSequencerBlock = { @@ -43,7 +44,7 @@ export const handleSequencerBlock = { if (transaction.queueOrigin === 'sequencer') { const decodedTransaction: DecodedSequencerBatchTransaction = { sig: { - v: BigNumber.from(transaction.v).toNumber() - 2 * chainId - 35, + v: parseSignatureVParam(transaction.v, chainId), r: padHexString(transaction.r, 32), s: padHexString(transaction.s, 32), }, diff --git a/packages/data-transport-layer/src/types/event-handler-types.ts b/packages/data-transport-layer/src/types/event-handler-types.ts index 82bf5bf7f198..1839796b527e 100644 --- a/packages/data-transport-layer/src/types/event-handler-types.ts +++ b/packages/data-transport-layer/src/types/event-handler-types.ts @@ -20,7 +20,8 @@ export type GetExtraDataHandler = ( export type ParseEventHandler = ( event: TypedEthersEvent, - extraData: TExtraData + extraData: TExtraData, + l2ChainId: number ) => TParsedEvent export type StoreEventHandler = ( diff --git a/packages/data-transport-layer/src/utils/eth-tx.ts b/packages/data-transport-layer/src/utils/eth-tx.ts new file mode 100644 index 000000000000..cce210d26909 --- /dev/null +++ b/packages/data-transport-layer/src/utils/eth-tx.ts @@ -0,0 +1,9 @@ +/* Imports: External */ +import { ethers } from 'ethers' + +export const parseSignatureVParam = ( + v: number | ethers.BigNumber, + chainId: number +): number => { + return ethers.BigNumber.from(v).toNumber() - 2 * chainId - 35 +} diff --git a/packages/data-transport-layer/src/utils/index.ts b/packages/data-transport-layer/src/utils/index.ts index 482c5161307c..411d8cdb6d80 100644 --- a/packages/data-transport-layer/src/utils/index.ts +++ b/packages/data-transport-layer/src/utils/index.ts @@ -2,3 +2,4 @@ export * from './common' export * from './constants' export * from './contracts' export * from './validation' +export * from './eth-tx' diff --git a/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/sequencer-batch-appended.spec.ts b/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/sequencer-batch-appended.spec.ts index 813f3c65c63a..f4ad79c89730 100644 --- a/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/sequencer-batch-appended.spec.ts +++ b/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/sequencer-batch-appended.spec.ts @@ -1,9 +1,9 @@ import { BigNumber, ethers } from 'ethers' + +/* Imports: Internal */ import { expect } from '../../../../setup' import { handleEventsSequencerBatchAppended } from '../../../../../src/services/l1-ingestion/handlers/sequencer-batch-appended' import { SequencerBatchAppendedExtraData } from '../../../../../src/types' -import { l1TransactionData } from '../../../examples/l1-data' -import { blocksOnL2 } from '../../../examples/l2-data' describe('Event Handlers: OVM_CanonicalTransactionChain.SequencerBatchAppended', () => { describe('handleEventsSequencerBatchAppended.parseEvent', () => { @@ -28,7 +28,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.SequencerBatchAppended', } it('should error on malformed transaction data', async () => { - const input1: [any, SequencerBatchAppendedExtraData] = [ + const input1: [any, SequencerBatchAppendedExtraData, number] = [ { args: { _startingQueueIndex: ethers.constants.Zero, @@ -40,6 +40,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.SequencerBatchAppended', l1TransactionData: '0x00000', ...exampleExtraData, }, + 0, ] expect(() => { diff --git a/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/state-batch-appended.spec.ts b/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/state-batch-appended.spec.ts index fb16de430cbe..8090a05cb4f4 100644 --- a/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/state-batch-appended.spec.ts +++ b/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/state-batch-appended.spec.ts @@ -1,7 +1,10 @@ +import { expect } from '../../../../setup' + +/* Imports: External */ import { BigNumber } from 'ethers' import { Block } from '@ethersproject/abstract-provider' -import { expect } from '../../../../setup' +/* Imports: Internal */ import { handleEventsStateBatchAppended } from '../../../../../src/services/l1-ingestion/handlers/state-batch-appended' import { StateBatchAppendedExtraData } from '../../../../../src/types' import { l1StateBatchData } from '../../../examples/l1-data' @@ -73,7 +76,11 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.StateBatchAppended', () l1TransactionHash: '0x4ca72484e93cdb50fe1089984db152258c2bbffc2534dcafbfe032b596bd5b49', } - const input1: [any, StateBatchAppendedExtraData] = [event, extraData] + const input1: [any, StateBatchAppendedExtraData, number] = [ + event, + extraData, + 0, + ] const output1 = handleEventsStateBatchAppended.parseEvent(...input1) diff --git a/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/transaction-enqueued.spec.ts b/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/transaction-enqueued.spec.ts index b14d6ba505b9..0e3ef5e25541 100644 --- a/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/transaction-enqueued.spec.ts +++ b/packages/data-transport-layer/test/unit-tests/services/l1-ingestion/handlers/transaction-enqueued.spec.ts @@ -1,6 +1,9 @@ +import { expect } from '../../../../setup' + +/* Imports: External */ import { ethers, BigNumber } from 'ethers' -import { expect } from '../../../../setup' +/* Imports: Internal */ import { handleEventsTransactionEnqueued } from '../../../../../src/services/l1-ingestion/handlers/transaction-enqueued' const MAX_ITERATIONS = 128 @@ -22,7 +25,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', () // but it's probably better to get wider test coverage first. it('should have a ctcIndex equal to null', () => { - const input1: [any, any] = [ + const input1: [any, any, number] = [ { blockNumber: 0, args: { @@ -32,6 +35,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', () }, }, null, + 0, ] const output1 = handleEventsTransactionEnqueued.parseEvent(...input1) @@ -47,7 +51,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', () i < Number.MAX_SAFE_INTEGER; i += Math.floor(Number.MAX_SAFE_INTEGER / MAX_ITERATIONS) ) { - const input1: [any, any] = [ + const input1: [any, any, number] = [ { blockNumber: i, args: { @@ -57,6 +61,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', () }, }, null, + 0, ] const output1 = handleEventsTransactionEnqueued.parseEvent(...input1) @@ -73,7 +78,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', () i < Number.MAX_SAFE_INTEGER; i += Math.floor(Number.MAX_SAFE_INTEGER / MAX_ITERATIONS) ) { - const input1: [any, any] = [ + const input1: [any, any, number] = [ { blockNumber: 0, args: { @@ -83,6 +88,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', () }, }, null, + 0, ] const output1 = handleEventsTransactionEnqueued.parseEvent(...input1) @@ -99,7 +105,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', () i < Number.MAX_SAFE_INTEGER; i += Math.floor(Number.MAX_SAFE_INTEGER / MAX_ITERATIONS) ) { - const input1: [any, any] = [ + const input1: [any, any, number] = [ { blockNumber: 0, args: { @@ -109,6 +115,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', () }, }, null, + 0, ] const output1 = handleEventsTransactionEnqueued.parseEvent(...input1) @@ -125,7 +132,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', () i < Number.MAX_SAFE_INTEGER; i += Math.floor(Number.MAX_SAFE_INTEGER / MAX_ITERATIONS) ) { - const input1: [any, any] = [ + const input1: [any, any, number] = [ { blockNumber: 0, args: { @@ -135,6 +142,7 @@ describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', () }, }, null, + 0, ] const output1 = handleEventsTransactionEnqueued.parseEvent(...input1) diff --git a/packages/data-transport-layer/test/unit-tests/services/l2-ingestion/handlers/transaction.spec.ts b/packages/data-transport-layer/test/unit-tests/services/l2-ingestion/handlers/transaction.spec.ts index dac1dcc3aa45..0f7a71323623 100644 --- a/packages/data-transport-layer/test/unit-tests/services/l2-ingestion/handlers/transaction.spec.ts +++ b/packages/data-transport-layer/test/unit-tests/services/l2-ingestion/handlers/transaction.spec.ts @@ -1,4 +1,6 @@ import { expect } from '../../../../setup' + +/* Imports: Internal */ import { l2Block } from '../../../examples/l2-data' import { handleSequencerBlock } from '../../../../../src/services/l2-ingestion/handlers/transaction' From cb4a928bf02e7609d49cf827a1f7faed8480deb3 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Mon, 7 Jun 2021 21:07:01 -0700 Subject: [PATCH 27/46] fix: deterministic blockhashes (#1032) * config: set etherbase * l2geth: add deterministic clique key * l2geth: default value * chore: add changeset * test: add sync test for deterministic blockhash Co-authored-by: Kevin Ho --- .changeset/chilled-apricots-tie.md | 5 +++++ .../sync-tests/sync-verifier.spec.ts | 10 +++++++++- l2geth/accounts/keystore/keystore.go | 18 ++++++++++++++++++ l2geth/cmd/utils/flags.go | 5 +++-- l2geth/consensus/clique/clique.go | 6 +++--- ops/envs/geth.env | 2 ++ 6 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 .changeset/chilled-apricots-tie.md diff --git a/.changeset/chilled-apricots-tie.md b/.changeset/chilled-apricots-tie.md new file mode 100644 index 000000000000..c01d75ebe149 --- /dev/null +++ b/.changeset/chilled-apricots-tie.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/l2geth': patch +--- + +Make block hashes deterministic by using the same clique signer key diff --git a/integration-tests/sync-tests/sync-verifier.spec.ts b/integration-tests/sync-tests/sync-verifier.spec.ts index c1fe1e7e40f1..2c931eb87c4f 100644 --- a/integration-tests/sync-tests/sync-verifier.spec.ts +++ b/integration-tests/sync-tests/sync-verifier.spec.ts @@ -60,7 +60,7 @@ describe('Syncing a verifier', () => { }) describe('Basic transactions', () => { - afterEach(async () => { + after(async () => { await verifier.stop('verifier') await verifier.rm() }) @@ -97,5 +97,13 @@ describe('Syncing a verifier', () => { latestSequencerBlock.stateRoot ) }) + + it('should have matching block data', async () => { + const sequencerTip = await sequencerProvider.getBlock('latest') + const verifierTip = await provider.getBlock('latest') + + expect(sequencerTip.number).to.deep.eq(verifierTip.number) + expect(sequencerTip.hash).to.deep.eq(verifierTip.hash) + }) }) }) diff --git a/l2geth/accounts/keystore/keystore.go b/l2geth/accounts/keystore/keystore.go index 5b55175b1f3e..1ddd98a4622f 100644 --- a/l2geth/accounts/keystore/keystore.go +++ b/l2geth/accounts/keystore/keystore.go @@ -21,6 +21,7 @@ package keystore import ( + "bytes" "crypto/ecdsa" crand "crypto/rand" "errors" @@ -36,8 +37,10 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" ) var ( @@ -80,6 +83,21 @@ func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore { keydir, _ = filepath.Abs(keydir) ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP, false}} ks.init(keydir) + if vm.UsingOVM { + // Add a deterministic key to the key store so that + // all clique blocks are signed with the same key + input := make([]byte, 65) + rng := bytes.NewReader(input) + key, err := newKey(rng) + log.Info("Adding key to keyring", "address", key.Address.Hex()) + if err != nil { + panic(fmt.Sprintf("cannot create key: %s", err)) + } + _, err = ks.importKey(key, "") + if err != nil { + panic(fmt.Sprintf("cannot import key: %s", err)) + } + } return ks } diff --git a/l2geth/cmd/utils/flags.go b/l2geth/cmd/utils/flags.go index 6c5c4447401b..c28526ae252a 100644 --- a/l2geth/cmd/utils/flags.go +++ b/l2geth/cmd/utils/flags.go @@ -471,12 +471,13 @@ var ( MinerEtherbaseFlag = cli.StringFlag{ Name: "miner.etherbase", Usage: "Public address for block mining rewards (default = first account)", - Value: "0", + + Value: "0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf", + EnvVar: "ETHERBASE", } MinerLegacyEtherbaseFlag = cli.StringFlag{ Name: "etherbase", Usage: "Public address for block mining rewards (default = first account, deprecated, use --miner.etherbase)", - Value: "0", } MinerExtraDataFlag = cli.StringFlag{ Name: "miner.extradata", diff --git a/l2geth/consensus/clique/clique.go b/l2geth/consensus/clique/clique.go index 29201e2ba0d1..62bc27c1fe0f 100644 --- a/l2geth/consensus/clique/clique.go +++ b/l2geth/consensus/clique/clique.go @@ -23,7 +23,6 @@ import ( "io" "math/big" "math/rand" - "os" "sync" "time" @@ -34,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" @@ -249,7 +249,7 @@ func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header, } number := header.Number.Uint64() - if os.Getenv("USING_OVM") != "true" { + if vm.UsingOVM { // Don't waste time checking blocks from the future if header.Time > uint64(time.Now().Unix()) { return consensus.ErrFutureBlock @@ -631,7 +631,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, results c log.Trace("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle)) } - if os.Getenv("USING_OVM") == "true" { + if vm.UsingOVM { delay = 0 } // Sign all the things! diff --git a/ops/envs/geth.env b/ops/envs/geth.env index 59519125b17b..358a41b0d366 100644 --- a/ops/envs/geth.env +++ b/ops/envs/geth.env @@ -9,6 +9,8 @@ ROLLUP_POLL_INTERVAL_FLAG=500ms ROLLUP_ENABLE_L2_GAS_POLLING=true # ROLLUP_ENFORCE_FEES= +ETHERBASE=0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf + RPC_ENABLE=true RPC_ADDR=0.0.0.0 RPC_PORT=8545 From 0cbaa9a46e23624e72676bb561049b44dd1d6d2d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 10:28:21 -0400 Subject: [PATCH 28/46] Version Packages (#978) Co-authored-by: github-actions[bot] --- .changeset/chilled-apricots-tie.md | 5 ----- .changeset/chilled-books-grab.md | 5 ----- .changeset/cold-cows-cross.md | 5 ----- .changeset/cool-baboons-guess.md | 5 ----- .changeset/dull-fishes-type.md | 5 ----- .changeset/fair-maps-pretend.md | 6 ------ .changeset/fuzzy-dogs-share.md | 6 ------ .changeset/great-shrimps-rule.md | 5 ----- .changeset/kind-houses-rush.md | 5 ----- .changeset/late-countries-guess.md | 5 ----- .changeset/nasty-dots-grow.md | 6 ------ .changeset/neat-melons-lie.md | 5 ----- .changeset/nervous-bobcats-grow.md | 5 ----- .changeset/quick-pandas-laugh.md | 5 ----- .changeset/seven-carpets-tell.md | 5 ----- .changeset/sharp-files-knock.md | 5 ----- .changeset/silent-masks-hunt.md | 5 ----- .changeset/slimy-rivers-promise.md | 5 ----- .changeset/smooth-ears-pay.md | 7 ------- .changeset/ten-spiders-boil.md | 5 ----- .changeset/thin-waves-bathe.md | 5 ----- .changeset/unlucky-shoes-bake.md | 5 ----- .changeset/wet-falcons-talk.md | 5 ----- integration-tests/CHANGELOG.md | 8 ++++++++ integration-tests/package.json | 6 +++--- l2geth/CHANGELOG.md | 12 ++++++++++++ l2geth/package.json | 2 +- packages/batch-submitter/CHANGELOG.md | 14 ++++++++++++++ packages/batch-submitter/package.json | 8 ++++---- packages/contracts/CHANGELOG.md | 12 ++++++++++++ packages/contracts/package.json | 6 +++--- packages/core-utils/CHANGELOG.md | 8 ++++++++ packages/core-utils/package.json | 2 +- packages/data-transport-layer/CHANGELOG.md | 19 +++++++++++++++++++ packages/data-transport-layer/package.json | 6 +++--- packages/message-relayer/CHANGELOG.md | 16 ++++++++++++++++ packages/message-relayer/package.json | 8 ++++---- packages/smock/CHANGELOG.md | 11 +++++++++++ packages/smock/package.json | 4 ++-- 39 files changed, 121 insertions(+), 141 deletions(-) delete mode 100644 .changeset/chilled-apricots-tie.md delete mode 100644 .changeset/chilled-books-grab.md delete mode 100644 .changeset/cold-cows-cross.md delete mode 100644 .changeset/cool-baboons-guess.md delete mode 100644 .changeset/dull-fishes-type.md delete mode 100644 .changeset/fair-maps-pretend.md delete mode 100644 .changeset/fuzzy-dogs-share.md delete mode 100644 .changeset/great-shrimps-rule.md delete mode 100644 .changeset/kind-houses-rush.md delete mode 100644 .changeset/late-countries-guess.md delete mode 100644 .changeset/nasty-dots-grow.md delete mode 100644 .changeset/neat-melons-lie.md delete mode 100644 .changeset/nervous-bobcats-grow.md delete mode 100644 .changeset/quick-pandas-laugh.md delete mode 100644 .changeset/seven-carpets-tell.md delete mode 100644 .changeset/sharp-files-knock.md delete mode 100644 .changeset/silent-masks-hunt.md delete mode 100644 .changeset/slimy-rivers-promise.md delete mode 100644 .changeset/smooth-ears-pay.md delete mode 100644 .changeset/ten-spiders-boil.md delete mode 100644 .changeset/thin-waves-bathe.md delete mode 100644 .changeset/unlucky-shoes-bake.md delete mode 100644 .changeset/wet-falcons-talk.md diff --git a/.changeset/chilled-apricots-tie.md b/.changeset/chilled-apricots-tie.md deleted file mode 100644 index c01d75ebe149..000000000000 --- a/.changeset/chilled-apricots-tie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/l2geth': patch ---- - -Make block hashes deterministic by using the same clique signer key diff --git a/.changeset/chilled-books-grab.md b/.changeset/chilled-books-grab.md deleted file mode 100644 index 10efc8f70022..000000000000 --- a/.changeset/chilled-books-grab.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/l2geth': patch ---- - -Fixes incorrect type parsing in the RollupClient. The gasLimit became greater than the largest safe JS number so it needs to be represented as a string diff --git a/.changeset/cold-cows-cross.md b/.changeset/cold-cows-cross.md deleted file mode 100644 index cc67cf4d950d..000000000000 --- a/.changeset/cold-cows-cross.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/integration-tests': patch ---- - -Reduce test timeout from 100 to 20 seconds diff --git a/.changeset/cool-baboons-guess.md b/.changeset/cool-baboons-guess.md deleted file mode 100644 index aa0ea6cea3e9..000000000000 --- a/.changeset/cool-baboons-guess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/smock': patch ---- - -Fixes a bug that would break call assertions for overloaded smocked functions diff --git a/.changeset/dull-fishes-type.md b/.changeset/dull-fishes-type.md deleted file mode 100644 index 3a2e75a32a95..000000000000 --- a/.changeset/dull-fishes-type.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/data-transport-layer': patch ---- - -Represent gaslimit as a string to avoid an overflow diff --git a/.changeset/fair-maps-pretend.md b/.changeset/fair-maps-pretend.md deleted file mode 100644 index 2feeebb11b22..000000000000 --- a/.changeset/fair-maps-pretend.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@eth-optimism/batch-submitter': patch -'@eth-optimism/data-transport-layer': patch ---- - -Remove dead imports from core-utils diff --git a/.changeset/fuzzy-dogs-share.md b/.changeset/fuzzy-dogs-share.md deleted file mode 100644 index 3b890e4e9e8d..000000000000 --- a/.changeset/fuzzy-dogs-share.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@eth-optimism/l2geth': patch -'@eth-optimism/core-utils': patch ---- - -Implement the next fee spec in both geth and in core-utils diff --git a/.changeset/great-shrimps-rule.md b/.changeset/great-shrimps-rule.md deleted file mode 100644 index 388ad60eeaa7..000000000000 --- a/.changeset/great-shrimps-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/message-relayer': patch ---- - -Adds a README and cleans up the interface for generating messages and proofs diff --git a/.changeset/kind-houses-rush.md b/.changeset/kind-houses-rush.md deleted file mode 100644 index 1819a8f8c93e..000000000000 --- a/.changeset/kind-houses-rush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/l2geth': patch ---- - -fix potential underflow when launching the chain when the last verified index is 0 diff --git a/.changeset/late-countries-guess.md b/.changeset/late-countries-guess.md deleted file mode 100644 index 2edddd99dad3..000000000000 --- a/.changeset/late-countries-guess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/contracts': patch ---- - -Update contracts README to add deploy instructions. diff --git a/.changeset/nasty-dots-grow.md b/.changeset/nasty-dots-grow.md deleted file mode 100644 index 417ca35c1d1f..000000000000 --- a/.changeset/nasty-dots-grow.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@eth-optimism/l2geth': patch -'@eth-optimism/data-transport-layer': patch ---- - -Fix gasLimit overflow diff --git a/.changeset/neat-melons-lie.md b/.changeset/neat-melons-lie.md deleted file mode 100644 index 833a9b34d248..000000000000 --- a/.changeset/neat-melons-lie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/smock': patch ---- - -Fix a bug where overloaded functions would not be handled correctly diff --git a/.changeset/nervous-bobcats-grow.md b/.changeset/nervous-bobcats-grow.md deleted file mode 100644 index fc963b069701..000000000000 --- a/.changeset/nervous-bobcats-grow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@eth-optimism/l2geth": patch ---- - -Refactor the SyncService to more closely implement the specification. This includes using query params to select the backend from the DTL, trailing syncing of batches for the sequencer, syncing by batches as the verifier as well as unified code paths for transaction ingestion to prevent double ingestion or missed ingestion diff --git a/.changeset/quick-pandas-laugh.md b/.changeset/quick-pandas-laugh.md deleted file mode 100644 index 0faa252664d5..000000000000 --- a/.changeset/quick-pandas-laugh.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/data-transport-layer': patch ---- - -Fixes a bug that prevented verifiers from syncing properly with the DTL diff --git a/.changeset/seven-carpets-tell.md b/.changeset/seven-carpets-tell.md deleted file mode 100644 index 9be51c0a6974..000000000000 --- a/.changeset/seven-carpets-tell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/contracts': patch ---- - -Introduces the congestion price oracle contract diff --git a/.changeset/sharp-files-knock.md b/.changeset/sharp-files-knock.md deleted file mode 100644 index c74d2c14e4c3..000000000000 --- a/.changeset/sharp-files-knock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/message-relayer': patch ---- - -Adds a new set of tools for generating messages to be relayed and their proofs diff --git a/.changeset/silent-masks-hunt.md b/.changeset/silent-masks-hunt.md deleted file mode 100644 index 46f0382c5a79..000000000000 --- a/.changeset/silent-masks-hunt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/core-utils': patch ---- - -Delete dead transaction coders. These are no longer used now that RLP encoded transactions are used diff --git a/.changeset/slimy-rivers-promise.md b/.changeset/slimy-rivers-promise.md deleted file mode 100644 index 38b393bac00e..000000000000 --- a/.changeset/slimy-rivers-promise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/data-transport-layer': patch ---- - -Logs the error stacktrace for a failed HTTP request diff --git a/.changeset/smooth-ears-pay.md b/.changeset/smooth-ears-pay.md deleted file mode 100644 index 9df7b7cc9289..000000000000 --- a/.changeset/smooth-ears-pay.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@eth-optimism/integration-tests': patch -'@eth-optimism/l2geth': patch -'@eth-optimism/core-utils': patch ---- - -Implement the latest fee spec such that the L2 gas limit is scaled and the tx.gasPrice/tx.gasLimit show correctly in metamask diff --git a/.changeset/ten-spiders-boil.md b/.changeset/ten-spiders-boil.md deleted file mode 100644 index 757827799789..000000000000 --- a/.changeset/ten-spiders-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/message-relayer': patch ---- - -Removes spreadsheet mode from the message relayer diff --git a/.changeset/thin-waves-bathe.md b/.changeset/thin-waves-bathe.md deleted file mode 100644 index 605e410ac5df..000000000000 --- a/.changeset/thin-waves-bathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/contracts': patch ---- - -Minor change to how deploy.ts is invoked diff --git a/.changeset/unlucky-shoes-bake.md b/.changeset/unlucky-shoes-bake.md deleted file mode 100644 index 7b24244798b9..000000000000 --- a/.changeset/unlucky-shoes-bake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/integration-tests': patch ---- - -Add verifier sync test and extra docker-compose functions diff --git a/.changeset/wet-falcons-talk.md b/.changeset/wet-falcons-talk.md deleted file mode 100644 index 7698788db0e9..000000000000 --- a/.changeset/wet-falcons-talk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/data-transport-layer': patch ---- - -improve slow blocking JSON parsing that occurs during l2 sync diff --git a/integration-tests/CHANGELOG.md b/integration-tests/CHANGELOG.md index 1d370a90264e..ea2181a934dd 100644 --- a/integration-tests/CHANGELOG.md +++ b/integration-tests/CHANGELOG.md @@ -1,5 +1,13 @@ # @eth-optimism/integration-tests +## 0.0.7 + +### Patch Changes + +- d1680052: Reduce test timeout from 100 to 20 seconds +- c2b6e14b: Implement the latest fee spec such that the L2 gas limit is scaled and the tx.gasPrice/tx.gasLimit show correctly in metamask +- 77108d37: Add verifier sync test and extra docker-compose functions + ## 0.0.6 ### Patch Changes diff --git a/integration-tests/package.json b/integration-tests/package.json index 03629b169883..ca67edcc4fcf 100644 --- a/integration-tests/package.json +++ b/integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/integration-tests", - "version": "0.0.6", + "version": "0.0.7", "description": "[Optimism] Integration Tests", "private": true, "author": "Optimism PBC", @@ -17,8 +17,8 @@ "clean": "rimraf cache artifacts artifacts-ovm cache-ovm" }, "devDependencies": { - "@eth-optimism/contracts": "^0.3.4", - "@eth-optimism/core-utils": "^0.4.4", + "@eth-optimism/contracts": "^0.3.5", + "@eth-optimism/core-utils": "^0.4.5", "@eth-optimism/hardhat-ovm": "^0.2.2", "@ethersproject/providers": "^5.0.24", "@nomiclabs/hardhat-ethers": "^2.0.2", diff --git a/l2geth/CHANGELOG.md b/l2geth/CHANGELOG.md index f50227477351..d1962b54be8f 100644 --- a/l2geth/CHANGELOG.md +++ b/l2geth/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 0.3.7 + +### Patch Changes + +- cb4a928b: Make block hashes deterministic by using the same clique signer key +- f1b27318: Fixes incorrect type parsing in the RollupClient. The gasLimit became greater than the largest safe JS number so it needs to be represented as a string +- a64f8161: Implement the next fee spec in both geth and in core-utils +- 5e4eaea1: fix potential underflow when launching the chain when the last verified index is 0 +- 1293825c: Fix gasLimit overflow +- a25acbbd: Refactor the SyncService to more closely implement the specification. This includes using query params to select the backend from the DTL, trailing syncing of batches for the sequencer, syncing by batches as the verifier as well as unified code paths for transaction ingestion to prevent double ingestion or missed ingestion +- c2b6e14b: Implement the latest fee spec such that the L2 gas limit is scaled and the tx.gasPrice/tx.gasLimit show correctly in metamask + ## 0.3.6 ### Patch Changes diff --git a/l2geth/package.json b/l2geth/package.json index 494c2e3fef89..cf0f0a6d2fff 100644 --- a/l2geth/package.json +++ b/l2geth/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/l2geth", - "version": "0.3.6", + "version": "0.3.7", "private": true, "devDependencies": {} } diff --git a/packages/batch-submitter/CHANGELOG.md b/packages/batch-submitter/CHANGELOG.md index be440911b141..02cac6b87960 100644 --- a/packages/batch-submitter/CHANGELOG.md +++ b/packages/batch-submitter/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.3.3 + +### Patch Changes + +- 750a5021: Remove dead imports from core-utils +- Updated dependencies [a64f8161] +- Updated dependencies [4e03f8a9] +- Updated dependencies [8e2bfd07] +- Updated dependencies [750a5021] +- Updated dependencies [c2b6e14b] +- Updated dependencies [245136f1] + - @eth-optimism/core-utils@0.4.5 + - @eth-optimism/contracts@0.3.5 + ## 0.3.2 ### Patch Changes diff --git a/packages/batch-submitter/package.json b/packages/batch-submitter/package.json index a2b2ed31d71c..a1cdaeeaa09d 100644 --- a/packages/batch-submitter/package.json +++ b/packages/batch-submitter/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/batch-submitter", - "version": "0.3.2", + "version": "0.3.3", "private": true, "description": "[Optimism] Batch submission for sequencer & aggregators", "main": "dist/index", @@ -32,8 +32,8 @@ }, "dependencies": { "@eth-optimism/common-ts": "^0.1.2", - "@eth-optimism/contracts": "^0.3.1", - "@eth-optimism/core-utils": "^0.4.3", + "@eth-optimism/contracts": "^0.3.5", + "@eth-optimism/core-utils": "^0.4.5", "@eth-optimism/ynatm": "^0.2.2", "@ethersproject/abstract-provider": "^5.0.5", "@ethersproject/providers": "^5.0.14", @@ -45,7 +45,7 @@ "prom-client": "^13.1.0" }, "devDependencies": { - "@eth-optimism/smock": "^1.1.4", + "@eth-optimism/smock": "^1.1.5", "@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-waffle": "^2.0.1", "@types/bluebird": "^3.5.34", diff --git a/packages/contracts/CHANGELOG.md b/packages/contracts/CHANGELOG.md index ae5579530c4f..857f869507d8 100644 --- a/packages/contracts/CHANGELOG.md +++ b/packages/contracts/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 0.3.5 + +### Patch Changes + +- 4e03f8a9: Update contracts README to add deploy instructions. +- 8e2bfd07: Introduces the congestion price oracle contract +- 245136f1: Minor change to how deploy.ts is invoked +- Updated dependencies [a64f8161] +- Updated dependencies [750a5021] +- Updated dependencies [c2b6e14b] + - @eth-optimism/core-utils@0.4.5 + ## 0.3.4 ### Patch Changes diff --git a/packages/contracts/package.json b/packages/contracts/package.json index ec5771a978c3..eb4e94dddf50 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/contracts", - "version": "0.3.4", + "version": "0.3.5", "main": "dist/index", "files": [ "dist/**/*.js", @@ -50,7 +50,7 @@ "generate-markdown": "node \"./scripts/generate-markdown.js\"" }, "dependencies": { - "@eth-optimism/core-utils": "^0.4.4", + "@eth-optimism/core-utils": "^0.4.5", "@ethersproject/abstract-provider": "^5.0.8", "@ethersproject/abstract-signer": "^5.1.0", "@ethersproject/contracts": "^5.0.5", @@ -59,7 +59,7 @@ "devDependencies": { "@codechecks/client": "0.1.10-beta", "@eth-optimism/hardhat-ovm": "^0.2.2", - "@eth-optimism/smock": "^1.1.3", + "@eth-optimism/smock": "^1.1.5", "@nomiclabs/hardhat-ethers": "^2.0.1", "@nomiclabs/hardhat-waffle": "^2.0.1", "@openzeppelin/contracts": "^3.3.0", diff --git a/packages/core-utils/CHANGELOG.md b/packages/core-utils/CHANGELOG.md index a5d6db922774..69d1bbdd6ec8 100644 --- a/packages/core-utils/CHANGELOG.md +++ b/packages/core-utils/CHANGELOG.md @@ -1,5 +1,13 @@ # @eth-optimism/core-utils +## 0.4.5 + +### Patch Changes + +- a64f8161: Implement the next fee spec in both geth and in core-utils +- 750a5021: Delete dead transaction coders. These are no longer used now that RLP encoded transactions are used +- c2b6e14b: Implement the latest fee spec such that the L2 gas limit is scaled and the tx.gasPrice/tx.gasLimit show correctly in metamask + ## 0.4.4 ### Patch Changes diff --git a/packages/core-utils/package.json b/packages/core-utils/package.json index dea8684a4ccd..74166fb352bd 100644 --- a/packages/core-utils/package.json +++ b/packages/core-utils/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/core-utils", - "version": "0.4.4", + "version": "0.4.5", "main": "dist/index", "files": [ "dist/*" diff --git a/packages/data-transport-layer/CHANGELOG.md b/packages/data-transport-layer/CHANGELOG.md index 61c4944d5ac0..3f498d8914fe 100644 --- a/packages/data-transport-layer/CHANGELOG.md +++ b/packages/data-transport-layer/CHANGELOG.md @@ -1,5 +1,24 @@ # data transport layer +## 0.3.4 + +### Patch Changes + +- f1b27318: Represent gaslimit as a string to avoid an overflow +- 750a5021: Remove dead imports from core-utils +- 1293825c: Fix gasLimit overflow +- a75f05b7: Fixes a bug that prevented verifiers from syncing properly with the DTL +- e52ccd98: Logs the error stacktrace for a failed HTTP request +- 8ac4c74c: improve slow blocking JSON parsing that occurs during l2 sync +- Updated dependencies [a64f8161] +- Updated dependencies [4e03f8a9] +- Updated dependencies [8e2bfd07] +- Updated dependencies [750a5021] +- Updated dependencies [c2b6e14b] +- Updated dependencies [245136f1] + - @eth-optimism/core-utils@0.4.5 + - @eth-optimism/contracts@0.3.5 + ## 0.3.3 ### Patch Changes diff --git a/packages/data-transport-layer/package.json b/packages/data-transport-layer/package.json index ac98ed5bdad3..1dab5b320334 100644 --- a/packages/data-transport-layer/package.json +++ b/packages/data-transport-layer/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/data-transport-layer", - "version": "0.3.3", + "version": "0.3.4", "private": true, "main": "dist/index", "files": [ @@ -22,8 +22,8 @@ }, "dependencies": { "@eth-optimism/common-ts": "^0.1.2", - "@eth-optimism/contracts": "^0.3.3", - "@eth-optimism/core-utils": "^0.4.3", + "@eth-optimism/contracts": "^0.3.5", + "@eth-optimism/core-utils": "^0.4.5", "@ethersproject/providers": "^5.0.21", "@ethersproject/transactions": "^5.0.21", "@sentry/node": "^6.3.1", diff --git a/packages/message-relayer/CHANGELOG.md b/packages/message-relayer/CHANGELOG.md index 01e27330bcfe..587238e85699 100644 --- a/packages/message-relayer/CHANGELOG.md +++ b/packages/message-relayer/CHANGELOG.md @@ -1,5 +1,21 @@ # @eth-optimism/message-relayer +## 0.1.4 + +### Patch Changes + +- 9d39121b: Adds a README and cleans up the interface for generating messages and proofs +- 86708bb5: Adds a new set of tools for generating messages to be relayed and their proofs +- 064c03af: Removes spreadsheet mode from the message relayer +- Updated dependencies [a64f8161] +- Updated dependencies [4e03f8a9] +- Updated dependencies [8e2bfd07] +- Updated dependencies [750a5021] +- Updated dependencies [c2b6e14b] +- Updated dependencies [245136f1] + - @eth-optimism/core-utils@0.4.5 + - @eth-optimism/contracts@0.3.5 + ## 0.1.3 ### Patch Changes diff --git a/packages/message-relayer/package.json b/packages/message-relayer/package.json index 7cd1a0444edf..dbbfb14fd30b 100644 --- a/packages/message-relayer/package.json +++ b/packages/message-relayer/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/message-relayer", - "version": "0.1.3", + "version": "0.1.4", "description": "[Optimism] Cross Domain Message Relayer service", "main": "dist/index", "types": "dist/index", @@ -30,8 +30,8 @@ }, "dependencies": { "@eth-optimism/common-ts": "^0.1.2", - "@eth-optimism/contracts": "^0.3.3", - "@eth-optimism/core-utils": "^0.4.3", + "@eth-optimism/contracts": "^0.3.5", + "@eth-optimism/core-utils": "^0.4.5", "bcfg": "^0.1.6", "dotenv": "^8.2.0", "ethers": "^5.1.0", @@ -39,7 +39,7 @@ "rlp": "^2.2.6" }, "devDependencies": { - "@eth-optimism/smock": "^1.1.4", + "@eth-optimism/smock": "^1.1.5", "@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-waffle": "^2.0.1", "@types/chai": "^4.2.18", diff --git a/packages/smock/CHANGELOG.md b/packages/smock/CHANGELOG.md index 9d108ba56d19..1140eb913484 100644 --- a/packages/smock/CHANGELOG.md +++ b/packages/smock/CHANGELOG.md @@ -1,5 +1,16 @@ # @eth-optimism/smock +## 1.1.5 + +### Patch Changes + +- 5e3c5d1c: Fixes a bug that would break call assertions for overloaded smocked functions +- e6e87ae1: Fix a bug where overloaded functions would not be handled correctly +- Updated dependencies [a64f8161] +- Updated dependencies [750a5021] +- Updated dependencies [c2b6e14b] + - @eth-optimism/core-utils@0.4.5 + ## 1.1.4 ### Patch Changes diff --git a/packages/smock/package.json b/packages/smock/package.json index b21d1551f8ed..4a41c1d58444 100644 --- a/packages/smock/package.json +++ b/packages/smock/package.json @@ -3,7 +3,7 @@ "files": [ "dist/src/*" ], - "version": "1.1.4", + "version": "1.1.5", "main": "dist/src/index", "types": "dist/src/index", "author": "Optimism PBC", @@ -26,7 +26,7 @@ "hardhat": "^2.2.1" }, "dependencies": { - "@eth-optimism/core-utils": "^0.4.1", + "@eth-optimism/core-utils": "^0.4.5", "bn.js": "^5.2.0" }, "devDependencies": { From 958477be4f8e778864ddc383c1c62a898b55672a Mon Sep 17 00:00:00 2001 From: Annie Ke Date: Tue, 8 Jun 2021 09:17:56 -0700 Subject: [PATCH 29/46] ci: add sync test's own workflow (#1031) --- .github/workflows/sync-tests.yml | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/sync-tests.yml diff --git a/.github/workflows/sync-tests.yml b/.github/workflows/sync-tests.yml new file mode 100644 index 000000000000..b70e12a9bdab --- /dev/null +++ b/.github/workflows/sync-tests.yml @@ -0,0 +1,44 @@ +name: sync-tests + +on: + push: + branches: + - 'master' + - 'develop' + - 'regenesis/*' + pull_request: + workflow_dispatch: + +jobs: + integration-sync-test: + runs-on: ubuntu-latest + env: + DOCKER_BUILDKIT: 1 + COMPOSE_DOCKER_CLI_BUILD: 1 + steps: + - uses: actions/checkout@v2 + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: Build the services + working-directory: ./ops + run: ./scripts/build-ci.sh + + - name: Bring the stack up + working-directory: ./ops + run: docker-compose up -d && ./scripts/wait-for-sequencer.sh + + - name: Run the sync tests + working-directory: ./integration-tests + run: | + yarn + yarn test:sync From 1b692415464a44d18a85255c7c7e43dc0a1619b6 Mon Sep 17 00:00:00 2001 From: Tim Myers Date: Tue, 8 Jun 2021 14:09:21 -0600 Subject: [PATCH 30/46] fix(dtl): incorrect parsing of eth_getBlockRange result (#1037) --- .changeset/spotty-drinks-bathe.md | 5 +++++ .../src/services/l2-ingestion/service.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/spotty-drinks-bathe.md diff --git a/.changeset/spotty-drinks-bathe.md b/.changeset/spotty-drinks-bathe.md new file mode 100644 index 000000000000..9eb5d6357d5d --- /dev/null +++ b/.changeset/spotty-drinks-bathe.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/data-transport-layer': patch +--- + +incorrect parsing of eth_getBlockRange result diff --git a/packages/data-transport-layer/src/services/l2-ingestion/service.ts b/packages/data-transport-layer/src/services/l2-ingestion/service.ts index fb6434f433c3..a9f59f7cb261 100644 --- a/packages/data-transport-layer/src/services/l2-ingestion/service.ts +++ b/packages/data-transport-layer/src/services/l2-ingestion/service.ts @@ -194,7 +194,7 @@ export class L2IngestionService extends BaseService { const respJson = await bfj.parse(resp.data, { yieldRate: 4096, // this yields abit more often than the default of 16384 }) - blocks = respJson.data + blocks = respJson.result } for (const block of blocks) { From 0c18e1903f8a33a607f782c0081a8fb5071b71ef Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 18:57:34 -0400 Subject: [PATCH 31/46] Version Packages (#1045) Co-authored-by: github-actions[bot] --- .changeset/spotty-drinks-bathe.md | 5 ----- packages/data-transport-layer/CHANGELOG.md | 6 ++++++ packages/data-transport-layer/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/spotty-drinks-bathe.md diff --git a/.changeset/spotty-drinks-bathe.md b/.changeset/spotty-drinks-bathe.md deleted file mode 100644 index 9eb5d6357d5d..000000000000 --- a/.changeset/spotty-drinks-bathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/data-transport-layer': patch ---- - -incorrect parsing of eth_getBlockRange result diff --git a/packages/data-transport-layer/CHANGELOG.md b/packages/data-transport-layer/CHANGELOG.md index 3f498d8914fe..9b2978947a34 100644 --- a/packages/data-transport-layer/CHANGELOG.md +++ b/packages/data-transport-layer/CHANGELOG.md @@ -1,5 +1,11 @@ # data transport layer +## 0.3.5 + +### Patch Changes + +- 1b692415: incorrect parsing of eth_getBlockRange result + ## 0.3.4 ### Patch Changes diff --git a/packages/data-transport-layer/package.json b/packages/data-transport-layer/package.json index 1dab5b320334..47e0a8158f2e 100644 --- a/packages/data-transport-layer/package.json +++ b/packages/data-transport-layer/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/data-transport-layer", - "version": "0.3.4", + "version": "0.3.5", "private": true, "main": "dist/index", "files": [ From 859437f9eb94665606d826b6eb1f66be13b4a545 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 12:06:05 -0400 Subject: [PATCH 32/46] build(deps): bump glob-parent from 5.1.1 to 5.1.2 (#1036) Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.1 to 5.1.2. - [Release notes](https://github.com/gulpjs/glob-parent/releases) - [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md) - [Commits](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2) --- updated-dependencies: - dependency-name: glob-parent dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: smartcontracts Co-authored-by: Liam Horne --- yarn.lock | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1be52e5f1a8e..84e59fa04878 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6860,20 +6860,13 @@ github-from-package@0.0.0: resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= -glob-parent@^5.1.0, glob-parent@^5.1.1: +glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@~5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob-parent@~5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" - integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== - dependencies: - is-glob "^4.0.1" - glob@7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" From 989a3027ab52b63859bfb40006f92fef11ba9d09 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Wed, 9 Jun 2021 13:08:22 -0700 Subject: [PATCH 33/46] l2geth: optimize loops (#1027) * l2geth: optimize loops * l2geth: stop ticker when done * l2geth: don't wait for first tick * chore: add changeset --- .changeset/angry-numbers-swim.md | 5 +++++ l2geth/rollup/sync_service.go | 13 +++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 .changeset/angry-numbers-swim.md diff --git a/.changeset/angry-numbers-swim.md b/.changeset/angry-numbers-swim.md new file mode 100644 index 000000000000..59883f090bb0 --- /dev/null +++ b/.changeset/angry-numbers-swim.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/l2geth': patch +--- + +Optimize main polling loops diff --git a/l2geth/rollup/sync_service.go b/l2geth/rollup/sync_service.go index 473acff61afe..b3bcfa952853 100644 --- a/l2geth/rollup/sync_service.go +++ b/l2geth/rollup/sync_service.go @@ -138,17 +138,18 @@ func NewSyncService(ctx context.Context, cfg Config, txpool *core.TxPool, bc *co } // Wait until the remote service is done syncing - for { + t := time.NewTicker(10 * time.Second) + for ; true; <-t.C { status, err := service.client.SyncStatus(service.backend) if err != nil { log.Error("Cannot get sync status") continue } if !status.Syncing { + t.Stop() break } log.Info("Still syncing", "index", status.CurrentTransactionIndex, "tip", status.HighestKnownTransactionIndex) - time.Sleep(10 * time.Second) } // Initialize the latest L1 data here to make sure that @@ -320,7 +321,8 @@ func (s *SyncService) Stop() error { // VerifierLoop is the main loop for Verifier mode func (s *SyncService) VerifierLoop() { log.Info("Starting Verifier Loop", "poll-interval", s.pollInterval, "timestamp-refresh-threshold", s.timestampRefreshThreshold) - for { + t := time.NewTicker(s.pollInterval) + for ; true; <-t.C { if err := s.updateL1GasPrice(); err != nil { log.Error("Cannot update L1 gas price", "msg", err) } @@ -330,7 +332,6 @@ func (s *SyncService) VerifierLoop() { if err := s.updateL2GasPrice(nil); err != nil { log.Error("Cannot update L2 gas price", "msg", err) } - time.Sleep(s.pollInterval) } } @@ -354,7 +355,8 @@ func (s *SyncService) verify() error { // transactions and then updates the EthContext. func (s *SyncService) SequencerLoop() { log.Info("Starting Sequencer Loop", "poll-interval", s.pollInterval, "timestamp-refresh-threshold", s.timestampRefreshThreshold) - for { + t := time.NewTicker(s.pollInterval) + for ; true; <-t.C { if err := s.updateL1GasPrice(); err != nil { log.Error("Cannot update L1 gas price", "msg", err) } @@ -370,7 +372,6 @@ func (s *SyncService) SequencerLoop() { if err := s.updateContext(); err != nil { log.Error("Could not update execution context", "error", err) } - time.Sleep(s.pollInterval) } } From c451060f1ec909820f4d2128e1c9211885f7068d Mon Sep 17 00:00:00 2001 From: Liam Horne Date: Wed, 9 Jun 2021 17:06:20 -0400 Subject: [PATCH 34/46] Enable custom tagging of release docker image (#1048) * ci: enable custom tagging of release docker image * ci: refactor to add prerelease prefix for secuirty * doc: typo --- .github/workflows/publish-canary.yml | 30 ++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish-canary.yml b/.github/workflows/publish-canary.yml index 5669f25dabd7..4bb0ff10cbc6 100644 --- a/.github/workflows/publish-canary.yml +++ b/.github/workflows/publish-canary.yml @@ -2,7 +2,12 @@ name: Publish Packages (canary) on: # enable users to manually trigger with workflow_dispatch - workflow_dispatch: {} + workflow_dispatch: + inputs: + customImageName: + description: 'Custom Docker Image Tag (keep empty for git hash)' + required: false + default: '0.0.0-rc-0' jobs: canary-publish: @@ -66,6 +71,19 @@ jobs: run: | node ops/scripts/ci-versions.js ${{ toJSON(steps.changesets.outputs.publishedPackages) }} + - name: Docker Image Name + id: docker_image_name + run: | + if [ $CUSTOM_IMAGE_NAME == '' ] + then + echo "::set-output name=canary-docker-tag::$GITHUB_SHA_PREFIX" + else + echo "::set-output name=canary-docker-tag::prerelease-$CUSTOM_IMAGE_NAME" + fi + env: + GITHUB_SHA_PREFIX: ${{ GITHUB_SHA::8 }} + CUSTOM_IMAGE_NAME: ${{ github.event.inputs.customImageName }} + # The below code is duplicated, would be ideal if we could use a matrix with a # key/value being dynamically generated from the `publishedPackages` output @@ -155,7 +173,7 @@ jobs: context: . file: ./ops/docker/Dockerfile.message-relayer push: true - tags: ethereumoptimism/message-relayer:${{ GITHUB_SHA::8 }} + tags: ethereumoptimism/message-relayer:${{ steps.docker_image_name.outputs.canary-docker-tag }} batch-submitter: name: Publish Batch Submitter Version ${{ needs.builder.outputs.batch-submitter }} @@ -181,7 +199,7 @@ jobs: context: . file: ./ops/docker/Dockerfile.batch-submitter push: true - tags: ethereumoptimism/batch-submitter:${{ GITHUB_SHA::8 }} + tags: ethereumoptimism/batch-submitter:${{ steps.docker_image_name.outputs.canary-docker-tag }} data-transport-layer: name: Publish Data Transport Layer Version ${{ needs.builder.outputs.data-transport-layer }} @@ -207,7 +225,7 @@ jobs: context: . file: ./ops/docker/Dockerfile.data-transport-layer push: true - tags: ethereumoptimism/data-transport-layer:${{ GITHUB_SHA::8 }} + tags: ethereumoptimism/data-transport-layer:${{ steps.docker_image_name.outputs.canary-docker-tag }} contracts: name: Publish Deployer Version ${{ needs.builder.outputs.contracts }} @@ -233,7 +251,7 @@ jobs: context: . file: ./ops/docker/Dockerfile.deployer push: true - tags: ethereumoptimism/deployer:${{ GITHUB_SHA::8 }} + tags: ethereumoptimism/deployer:${{ steps.docker_image_name.outputs.canary-docker-tag }} integration_tests: name: Publish Integration tests ${{ needs.builder.outputs.integration-tests }} @@ -259,4 +277,4 @@ jobs: context: . file: ./ops/docker/Dockerfile.integration-tests push: true - tags: ethereumoptimism/integration-tests:${{ GITHUB_SHA::8 }} + tags: ethereumoptimism/integration-tests:${{ steps.docker_image_name.outputs.canary-docker-tag }} From f03bb3e5cecc2e1d232e8263b0ba804ab3d761cf Mon Sep 17 00:00:00 2001 From: Liam Horne Date: Wed, 9 Jun 2021 17:09:46 -0400 Subject: [PATCH 35/46] ci: fix indentation issue of canary workflow --- .github/workflows/publish-canary.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish-canary.yml b/.github/workflows/publish-canary.yml index 4bb0ff10cbc6..93176ce4360d 100644 --- a/.github/workflows/publish-canary.yml +++ b/.github/workflows/publish-canary.yml @@ -4,10 +4,10 @@ on: # enable users to manually trigger with workflow_dispatch workflow_dispatch: inputs: - customImageName: - description: 'Custom Docker Image Tag (keep empty for git hash)' - required: false - default: '0.0.0-rc-0' + customImageName: + description: 'Custom Docker Image Tag (keep empty for git hash)' + required: false + default: '0.0.0-rc-0' jobs: canary-publish: From 85d5ce136e31b934a3655712bf2cb00c990727cc Mon Sep 17 00:00:00 2001 From: Liam Horne Date: Wed, 9 Jun 2021 17:13:16 -0400 Subject: [PATCH 36/46] ci: fix typo with GITHUB_SHA --- .github/workflows/publish-canary.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/publish-canary.yml b/.github/workflows/publish-canary.yml index 93176ce4360d..d5cb35ddb93f 100644 --- a/.github/workflows/publish-canary.yml +++ b/.github/workflows/publish-canary.yml @@ -76,12 +76,11 @@ jobs: run: | if [ $CUSTOM_IMAGE_NAME == '' ] then - echo "::set-output name=canary-docker-tag::$GITHUB_SHA_PREFIX" + echo "::set-output name=canary-docker-tag::${GITHUB_SHA::8}" else echo "::set-output name=canary-docker-tag::prerelease-$CUSTOM_IMAGE_NAME" fi env: - GITHUB_SHA_PREFIX: ${{ GITHUB_SHA::8 }} CUSTOM_IMAGE_NAME: ${{ github.event.inputs.customImageName }} From cc742715ecbca98248367d67f51a5f03038f5ba2 Mon Sep 17 00:00:00 2001 From: Karl Floersch Date: Wed, 9 Jun 2021 17:26:30 -0400 Subject: [PATCH 37/46] fix: typo in USE_HARDHAT config (#1023) --- .changeset/popular-feet-suffer.md | 5 +++++ packages/batch-submitter/src/exec/run-batch-submitter.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/popular-feet-suffer.md diff --git a/.changeset/popular-feet-suffer.md b/.changeset/popular-feet-suffer.md new file mode 100644 index 000000000000..afb71d29cdb1 --- /dev/null +++ b/.changeset/popular-feet-suffer.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/batch-submitter': patch +--- + +Fix typo in USE_HARDHAT config diff --git a/packages/batch-submitter/src/exec/run-batch-submitter.ts b/packages/batch-submitter/src/exec/run-batch-submitter.ts index d1439d64520f..9174cfab3b44 100644 --- a/packages/batch-submitter/src/exec/run-batch-submitter.ts +++ b/packages/batch-submitter/src/exec/run-batch-submitter.ts @@ -116,7 +116,7 @@ export const run = async () => { logger = new Logger({ name }) } - const useHardhat = config.bool('use-hardhat', !!env.USE_HARDAT) + const useHardhat = config.bool('use-hardhat', !!env.USE_HARDHAT) const DEBUG_IMPERSONATE_SEQUENCER_ADDRESS = config.str( 'debug-impersonate-sequencer-address', env.DEBUG_IMPERSONATE_SEQUENCER_ADDRESS From cc6c7f0794831e9ed4760b5b3a178914269f723c Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Thu, 10 Jun 2021 15:51:08 -0700 Subject: [PATCH 38/46] l2geth: bump to go 1.15 (#1058) * l2geth: bump to go 1.15 * chore: add changeset --- .changeset/grumpy-spiders-yell.md | 5 +++++ l2geth/go.mod | 2 +- ops/docker/Dockerfile.geth | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/grumpy-spiders-yell.md diff --git a/.changeset/grumpy-spiders-yell.md b/.changeset/grumpy-spiders-yell.md new file mode 100644 index 000000000000..c231b337cf97 --- /dev/null +++ b/.changeset/grumpy-spiders-yell.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/l2geth': patch +--- + +Bump golang version to 1.15 diff --git a/l2geth/go.mod b/l2geth/go.mod index 47277f950dba..d8239f1d0c52 100644 --- a/l2geth/go.mod +++ b/l2geth/go.mod @@ -1,6 +1,6 @@ module github.com/ethereum/go-ethereum -go 1.13 +go 1.15 require ( github.com/Azure/azure-pipeline-go v0.2.2 // indirect diff --git a/ops/docker/Dockerfile.geth b/ops/docker/Dockerfile.geth index 03f4a8b25bff..9b52339f9dd6 100644 --- a/ops/docker/Dockerfile.geth +++ b/ops/docker/Dockerfile.geth @@ -1,5 +1,5 @@ # Build Geth in a stock Go builder container -FROM golang:1.14-alpine as builder +FROM golang:1.15-alpine as builder RUN apk add --no-cache make gcc musl-dev linux-headers git From 98b7839f6d49cc9780277f8b5c9ebb93d34a0a62 Mon Sep 17 00:00:00 2001 From: Liam Horne Date: Thu, 10 Jun 2021 18:56:52 -0400 Subject: [PATCH 39/46] Change monotonicity band-aid code to log warnings not errors (#1060) * refactor: change monotonicity band-aid code to log warnings not errors * build: add changeset --- .changeset/shaggy-dogs-attend.md | 5 +++++ .../src/batch-submitter/tx-batch-submitter.ts | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 .changeset/shaggy-dogs-attend.md diff --git a/.changeset/shaggy-dogs-attend.md b/.changeset/shaggy-dogs-attend.md new file mode 100644 index 000000000000..66480b391ab0 --- /dev/null +++ b/.changeset/shaggy-dogs-attend.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/batch-submitter': patch +--- + +Change monotonicity band-aid code to log warnings not errors diff --git a/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts b/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts index bdcc59db48ed..7515b1028b62 100644 --- a/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts +++ b/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts @@ -469,7 +469,7 @@ export class TransactionBatchSubmitter extends BatchSubmitter { ] = await this.chainContract.getQueueElement(nextQueueIndex) if (timestamp < ele.timestamp || blockNumber < ele.blockNumber) { - this.logger.error('Fixing skipped deposit', { + this.logger.warn('Fixing skipped deposit', { badTimestamp: ele.timestamp, skippedQueueTimestamp: timestamp, badBlockNumber: ele.blockNumber, @@ -563,7 +563,7 @@ export class TransactionBatchSubmitter extends BatchSubmitter { ele.timestamp < earliestTimestamp || ele.blockNumber < earliestBlockNumber ) { - this.logger.error('Fixing timestamp/blockNumber too small', { + this.logger.warn('Fixing timestamp/blockNumber too small', { oldTimestamp: ele.timestamp, newTimestamp: earliestTimestamp, oldBlockNumber: ele.blockNumber, @@ -581,7 +581,7 @@ export class TransactionBatchSubmitter extends BatchSubmitter { ele.timestamp > latestTimestamp || ele.blockNumber > latestBlockNumber ) { - this.logger.error('Fixing timestamp/blockNumber too large.', { + this.logger.warn('Fixing timestamp/blockNumber too large.', { oldTimestamp: ele.timestamp, newTimestamp: latestTimestamp, oldBlockNumber: ele.blockNumber, From baa3b761cd5693dd57b1acf7b4bd42500a83b34f Mon Sep 17 00:00:00 2001 From: Liam Horne Date: Fri, 11 Jun 2021 14:29:09 -0400 Subject: [PATCH 40/46] Ensure Sentry is correctly set up for DTL and MR (#1054) * refactor: add logger and metrics to options for BaseService * refactor: thread sentryOptions through from message-relayer into BaseService * refactor: ensure DTL Logger is using Sentry for errors * style: lint base-service.ts * refactor: init Sentry on batch-submitter too * refactor: init Sentry on message-relayer too * refactor: pass in basic logger to MessageRelayerService * build: provide changeset * fix: correct usage of use-sentry boolean config * refactor: appropriately type loggingOptions * build: add @sentry/node * build: add @sentry/node to message-relayer and fix linting issue --- .changeset/modern-chicken-listen.md | 8 ++ packages/batch-submitter/package.json | 1 + .../src/exec/run-batch-submitter.ts | 15 ++- packages/common-ts/src/base-service.ts | 16 ++- .../src/services/main/service.ts | 2 +- .../src/services/server/service.ts | 11 +- packages/message-relayer/package.json | 1 + packages/message-relayer/src/exec/run.ts | 24 ++++ packages/message-relayer/src/service.ts | 8 +- yarn.lock | 124 +++++++++--------- 10 files changed, 136 insertions(+), 74 deletions(-) create mode 100644 .changeset/modern-chicken-listen.md diff --git a/.changeset/modern-chicken-listen.md b/.changeset/modern-chicken-listen.md new file mode 100644 index 000000000000..22cda8e2674a --- /dev/null +++ b/.changeset/modern-chicken-listen.md @@ -0,0 +1,8 @@ +--- +'@eth-optimism/batch-submitter': patch +'@eth-optimism/common-ts': patch +'@eth-optimism/data-transport-layer': patch +'@eth-optimism/message-relayer': patch +--- + +Improve Sentry support, initializing as needed and ensuring ERROR logs route to Sentry diff --git a/packages/batch-submitter/package.json b/packages/batch-submitter/package.json index a1cdaeeaa09d..099278ef041b 100644 --- a/packages/batch-submitter/package.json +++ b/packages/batch-submitter/package.json @@ -37,6 +37,7 @@ "@eth-optimism/ynatm": "^0.2.2", "@ethersproject/abstract-provider": "^5.0.5", "@ethersproject/providers": "^5.0.14", + "@sentry/node": "^6.2.5", "bcfg": "^0.1.6", "bluebird": "^3.7.2", "dotenv": "^8.2.0", diff --git a/packages/batch-submitter/src/exec/run-batch-submitter.ts b/packages/batch-submitter/src/exec/run-batch-submitter.ts index 9174cfab3b44..e3f21a3139cd 100644 --- a/packages/batch-submitter/src/exec/run-batch-submitter.ts +++ b/packages/batch-submitter/src/exec/run-batch-submitter.ts @@ -1,5 +1,6 @@ /* External Imports */ import { injectL2Context, Bcfg } from '@eth-optimism/core-utils' +import * as Sentry from '@sentry/node' import { Logger, Metrics, createMetricsServer } from '@eth-optimism/common-ts' import { exit } from 'process' import { Signer, Wallet } from 'ethers' @@ -101,15 +102,17 @@ export const run = async () => { let logger if (config.bool('use-sentry', env.USE_SENTRY === 'true')) { + const sentryOptions = { + release, + dsn: sentryDsn, + tracesSampleRate: sentryTraceRate, + environment: network, + } + Sentry.init(sentryOptions) // Initialize Sentry for Batch Submitter deployed to a network logger = new Logger({ name, - sentryOptions: { - release, - dsn: sentryDsn, - tracesSampleRate: sentryTraceRate, - environment: network, - }, + sentryOptions, }) } else { // Skip initializing Sentry diff --git a/packages/common-ts/src/base-service.ts b/packages/common-ts/src/base-service.ts index df21cf200601..1f1fee6bfd7b 100644 --- a/packages/common-ts/src/base-service.ts +++ b/packages/common-ts/src/base-service.ts @@ -9,6 +9,11 @@ type OptionSettings = { } } +type BaseServiceOptions = T & { + logger?: Logger + metrics?: Metrics +} + /** * Base for other "Service" objects. Handles your standard initialization process, can dynamically * start and stop. @@ -21,11 +26,18 @@ export class BaseService { protected initialized: boolean = false protected running: boolean = false - constructor(name: string, options: T, optionSettings: OptionSettings) { + constructor( + name: string, + options: BaseServiceOptions, + optionSettings: OptionSettings + ) { validateOptions(options, optionSettings) this.name = name this.options = mergeDefaultOptions(options, optionSettings) - this.logger = new Logger({ name }) + this.logger = options.logger || new Logger({ name }) + if (options.metrics) { + this.metrics = options.metrics + } } /** diff --git a/packages/data-transport-layer/src/services/main/service.ts b/packages/data-transport-layer/src/services/main/service.ts index d4a16923e2f6..b4d0839a360e 100644 --- a/packages/data-transport-layer/src/services/main/service.ts +++ b/packages/data-transport-layer/src/services/main/service.ts @@ -1,5 +1,5 @@ /* Imports: External */ -import { BaseService } from '@eth-optimism/common-ts' +import { BaseService, Logger } from '@eth-optimism/common-ts' import { LevelUp } from 'levelup' import level from 'level' diff --git a/packages/data-transport-layer/src/services/server/service.ts b/packages/data-transport-layer/src/services/server/service.ts index 4e781114d92d..7accc0bd8dec 100644 --- a/packages/data-transport-layer/src/services/server/service.ts +++ b/packages/data-transport-layer/src/services/server/service.ts @@ -1,5 +1,5 @@ /* Imports: External */ -import { BaseService, Metrics } from '@eth-optimism/common-ts' +import { BaseService, Logger, Metrics } from '@eth-optimism/common-ts' import express, { Request, Response } from 'express' import promBundle from 'express-prom-bundle' import cors from 'cors' @@ -125,10 +125,17 @@ export class L1TransportServer extends BaseService { * Initialize Sentry and related middleware */ private _initSentry() { - Sentry.init({ + const sentryOptions = { dsn: this.options.sentryDsn, release: this.options.release, environment: this.options.ethNetworkName, + } + this.logger = new Logger({ + name: this.name, + sentryOptions, + }) + Sentry.init({ + ...sentryOptions, integrations: [ new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ diff --git a/packages/message-relayer/package.json b/packages/message-relayer/package.json index dbbfb14fd30b..08026c85efe6 100644 --- a/packages/message-relayer/package.json +++ b/packages/message-relayer/package.json @@ -32,6 +32,7 @@ "@eth-optimism/common-ts": "^0.1.2", "@eth-optimism/contracts": "^0.3.5", "@eth-optimism/core-utils": "^0.4.5", + "@sentry/node": "6.2.5", "bcfg": "^0.1.6", "dotenv": "^8.2.0", "ethers": "^5.1.0", diff --git a/packages/message-relayer/src/exec/run.ts b/packages/message-relayer/src/exec/run.ts index 0ee3a0abeefc..1cca64005d0a 100644 --- a/packages/message-relayer/src/exec/run.ts +++ b/packages/message-relayer/src/exec/run.ts @@ -1,6 +1,8 @@ import { Wallet, providers } from 'ethers' import { MessageRelayerService } from '../service' import { Bcfg } from '@eth-optimism/core-utils' +import { Logger, LoggerOptions } from '@eth-optimism/common-ts' +import * as Sentry from '@sentry/node' import * as dotenv from 'dotenv' import Config from 'bcfg' @@ -14,6 +16,27 @@ const main = async () => { }) const env = process.env + + const SENTRY_DSN = config.str('sentry-dsn', env.SENTRY_DSN) + const USE_SENTRY = config.bool('use-sentry', env.USE_SENTRY === 'true') + const ETH_NETWORK_NAME = config.str('eth-network-name', env.ETH_NETWORK_NAME) + + const loggerOptions: LoggerOptions = { + name: 'Message_Relayer', + } + + if (USE_SENTRY) { + const sentryOptions = { + release: `message-relayer@${process.env.npm_package_version}`, + dsn: SENTRY_DSN, + environment: ETH_NETWORK_NAME, + } + loggerOptions.sentryOptions = sentryOptions + Sentry.init(sentryOptions) + } + + const logger = new Logger(loggerOptions) + const L2_NODE_WEB3_URL = config.str('l2-node-web3-url', env.L2_NODE_WEB3_URL) const L1_NODE_WEB3_URL = config.str('l1-node-web3-url', env.L1_NODE_WEB3_URL) const ADDRESS_MANAGER_ADDRESS = config.str( @@ -82,6 +105,7 @@ const main = async () => { l2BlockOffset: L2_BLOCK_OFFSET, l1StartOffset: L1_START_OFFSET, getLogsInterval: GET_LOGS_INTERVAL, + logger, }) await service.start() diff --git a/packages/message-relayer/src/service.ts b/packages/message-relayer/src/service.ts index 1851dc7f0005..c69305fd262c 100644 --- a/packages/message-relayer/src/service.ts +++ b/packages/message-relayer/src/service.ts @@ -5,7 +5,7 @@ import { MerkleTree } from 'merkletreejs' /* Imports: Internal */ import { fromHexString, sleep } from '@eth-optimism/core-utils' -import { BaseService } from '@eth-optimism/common-ts' +import { Logger, BaseService, Metrics } from '@eth-optimism/common-ts' import { loadContract, loadContractFromManager } from '@eth-optimism/contracts' import { StateRootBatchHeader, SentMessage, SentMessageProof } from './types' @@ -40,6 +40,12 @@ interface MessageRelayerOptions { // Number of blocks within each getLogs query - max is 2000 getLogsInterval?: number + + // A custom logger to transport logs via; default STDOUT + logger?: Logger + + // A custom metrics tracker to manage metrics; default undefined + metrics?: Metrics } const optionSettings = { diff --git a/yarn.lock b/yarn.lock index 84e59fa04878..cc075769cbea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1875,15 +1875,15 @@ "@sentry/utils" "6.2.5" tslib "^1.9.3" -"@sentry/core@6.3.5": - version "6.3.5" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.3.5.tgz#6b73de736eb9d0040be94cdbb06a744cd6b9172e" - integrity sha512-VR2ibDy33mryD0mT6d9fGhKjdNzS2FSwwZPe9GvmNOjkyjly/oV91BKVoYJneCqOeq8fyj2lvkJGKuupdJNDqg== - dependencies: - "@sentry/hub" "6.3.5" - "@sentry/minimal" "6.3.5" - "@sentry/types" "6.3.5" - "@sentry/utils" "6.3.5" +"@sentry/core@6.6.0": + version "6.6.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.6.0.tgz#51661d2dd5023d6cd07467422de1854282ced7e5" + integrity sha512-EjdeT6paAdxAZgfsVCB8wneahQF3nAUt9GxOJxaOBUv8BSc3HQ/svcTU3RU7k8YsP26PseEOIsedaxsEVZ+7og== + dependencies: + "@sentry/hub" "6.6.0" + "@sentry/minimal" "6.6.0" + "@sentry/types" "6.6.0" + "@sentry/utils" "6.6.0" tslib "^1.9.3" "@sentry/hub@5.30.0": @@ -1913,13 +1913,13 @@ "@sentry/utils" "6.3.1" tslib "^1.9.3" -"@sentry/hub@6.3.5": - version "6.3.5" - resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.3.5.tgz#c5bc6760f7e4e53e87149703b106804299060389" - integrity sha512-ZYFo7VYKwdPVjuV9BDFiYn+MpANn6eZMz5QDBfZ2dugIvIVbuOyOOLx8PSa3ZXJoVTZZ7s2wD2fi/ZxKjNjZOQ== +"@sentry/hub@6.6.0": + version "6.6.0" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.6.0.tgz#1b9fa22ee104b7d6afd2dc4c40a1459fda259366" + integrity sha512-1Yw0kbxcvO7njZUDGvCKB6DxU5jQio7Be3Kx5qxwcx8ojpT9lo9p+IYZajgl6zQqkjjbVm/4SoYqU24ozu5vxw== dependencies: - "@sentry/types" "6.3.5" - "@sentry/utils" "6.3.5" + "@sentry/types" "6.6.0" + "@sentry/utils" "6.6.0" tslib "^1.9.3" "@sentry/minimal@5.30.0": @@ -1949,13 +1949,28 @@ "@sentry/types" "6.3.1" tslib "^1.9.3" -"@sentry/minimal@6.3.5": - version "6.3.5" - resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.3.5.tgz#ef4894771243d01d81e91819400d2ecdcb34b411" - integrity sha512-4RqIGAU0+8iI/1sw0GYPTr4SUA88/i2+JPjFJ+qloh5ANVaNwhFPRChw+Ys9xpre8LV9JZrEsEf8AvQr4fkNbA== +"@sentry/minimal@6.6.0": + version "6.6.0" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.6.0.tgz#48684734e3c380e5e63a9357d05f0c18bae84419" + integrity sha512-xVBlZIDxSvHvNdvD5KmjTf8Xgi78vLpT4xqJaDUkW7B+DqWMVJZe5aUdQmcp7X/zWxctBwyMKsdHO7oiHkpS+Q== dependencies: - "@sentry/hub" "6.3.5" - "@sentry/types" "6.3.5" + "@sentry/hub" "6.6.0" + "@sentry/types" "6.6.0" + tslib "^1.9.3" + +"@sentry/node@6.2.5", "@sentry/node@^6.2.5": + version "6.2.5" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.2.5.tgz#6e6694c0c3ce6ca231710f40da0cac7fd5c645ef" + integrity sha512-/iM3khzGnUH713VFhZBAEYJhb/saEQSVz7Udogml+O7mFQ4rutnwJhgoGcB9YYrwMv2m7qOSszkdZbemDV6k2g== + dependencies: + "@sentry/core" "6.2.5" + "@sentry/hub" "6.2.5" + "@sentry/tracing" "6.2.5" + "@sentry/types" "6.2.5" + "@sentry/utils" "6.2.5" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" tslib "^1.9.3" "@sentry/node@^5.18.1": @@ -1973,31 +1988,16 @@ lru_map "^0.3.3" tslib "^1.9.3" -"@sentry/node@^6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.2.5.tgz#6e6694c0c3ce6ca231710f40da0cac7fd5c645ef" - integrity sha512-/iM3khzGnUH713VFhZBAEYJhb/saEQSVz7Udogml+O7mFQ4rutnwJhgoGcB9YYrwMv2m7qOSszkdZbemDV6k2g== - dependencies: - "@sentry/core" "6.2.5" - "@sentry/hub" "6.2.5" - "@sentry/tracing" "6.2.5" - "@sentry/types" "6.2.5" - "@sentry/utils" "6.2.5" - cookie "^0.4.1" - https-proxy-agent "^5.0.0" - lru_map "^0.3.3" - tslib "^1.9.3" - "@sentry/node@^6.3.1": - version "6.3.5" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.3.5.tgz#d5cbf941d0a4caf7b8e644d71cc6b463eeda214e" - integrity sha512-scPB+DoAEPaqkYuyb8d/gVWbFmX5PhaYSNHybeHncaP/P4itLdq/AoAWGNxl0Hj4EQokfT4OZWxaaJi7SCYnaw== - dependencies: - "@sentry/core" "6.3.5" - "@sentry/hub" "6.3.5" - "@sentry/tracing" "6.3.5" - "@sentry/types" "6.3.5" - "@sentry/utils" "6.3.5" + version "6.6.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.6.0.tgz#e535e1e679cf894752810529ffdee93cbfd078f0" + integrity sha512-heKie/AOanYq3mCsKR1igPn1sUIxBmGibBp79Xc0iSAgliPKnnLkqUjvAIKu6mcevL9UOUhpMDLzhilkaG+bAA== + dependencies: + "@sentry/core" "6.6.0" + "@sentry/hub" "6.6.0" + "@sentry/tracing" "6.6.0" + "@sentry/types" "6.6.0" + "@sentry/utils" "6.6.0" cookie "^0.4.1" https-proxy-agent "^5.0.0" lru_map "^0.3.3" @@ -2025,15 +2025,15 @@ "@sentry/utils" "6.2.5" tslib "^1.9.3" -"@sentry/tracing@6.3.5": - version "6.3.5" - resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.3.5.tgz#f76c362159141f860081ec7df80aa9f85b545860" - integrity sha512-TNKAST1ge2g24BlTfVxNp4gP5t3drbi0OVCh8h8ah+J7UjHSfdiqhd9W2h5qv1GO61gGlpWeN/TyioyQmOxu0Q== +"@sentry/tracing@6.6.0": + version "6.6.0" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.6.0.tgz#ce62fcb951faa6447cf47889f91efe3617b9eed2" + integrity sha512-tjXrmAOFfVBfx+ZmgE5bkpDPs/euNj0xrUg8MowCWGfCRn01W679tTb+dyNeP6faxQTo2RcaD68xD8oLroJwwA== dependencies: - "@sentry/hub" "6.3.5" - "@sentry/minimal" "6.3.5" - "@sentry/types" "6.3.5" - "@sentry/utils" "6.3.5" + "@sentry/hub" "6.6.0" + "@sentry/minimal" "6.6.0" + "@sentry/types" "6.6.0" + "@sentry/utils" "6.6.0" tslib "^1.9.3" "@sentry/tracing@^6.3.1": @@ -2062,10 +2062,10 @@ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.3.1.tgz#af3b54728b29f633f38fbe51b8c10e3834fbc158" integrity sha512-BEBn8JX1yaooCAuonbaMci9z0RjwwMbQ3Eny/eyDdd+rjXprZCZaStZnCvSThbNBqAJ8YaUqY2YBMnEwJxarAw== -"@sentry/types@6.3.5": - version "6.3.5" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.3.5.tgz#d5eca7e76c250882ab78c01a8df894a9a9ca537d" - integrity sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ== +"@sentry/types@6.6.0": + version "6.6.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.6.0.tgz#55cbca23859bad87411f0f32135a968e6e40a639" + integrity sha512-lZ1uFN0lSNftAohi0lciEoSL58Gk/Ib1lLKaj0FSOvB1PAUmvo5dPtLdd0qjtNdtoaM8zqhrAbwCTQ8XZCDRsg== "@sentry/utils@5.30.0": version "5.30.0" @@ -2091,12 +2091,12 @@ "@sentry/types" "6.3.1" tslib "^1.9.3" -"@sentry/utils@6.3.5": - version "6.3.5" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.3.5.tgz#a4805448cb0314d3d119688162aa695598a10bbb" - integrity sha512-kHUcZ37QYlNzz7c9LVdApITXHaNmQK7+sw/If3M/qpff1fd5XoecA8laLfcYuz+Cw5mRhVmdhPcCRM3Xi1IGXg== +"@sentry/utils@6.6.0": + version "6.6.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.6.0.tgz#b34d342d05eefc25b7ddd3f27f41c050f1e7e1ef" + integrity sha512-FK9yqz2x+ef50B54tueeJ6mfb7Pf3lN75omx/YQBDL5cicyOV4j4kJDqn8/VKYhcSuX+ZaCZ/8bvOf0lxe0aHg== dependencies: - "@sentry/types" "6.3.5" + "@sentry/types" "6.6.0" tslib "^1.9.3" "@sindresorhus/is@^0.14.0": From 85362d44fc13966f00bee26172dd04e6a442a6c6 Mon Sep 17 00:00:00 2001 From: Liam Horne Date: Fri, 11 Jun 2021 16:28:12 -0400 Subject: [PATCH 41/46] Add more logging information to monotonicity violation logs (#1066) * refactor: log idx of monotonicity violation from batch * build: add changeset --- .changeset/weak-terms-worry.md | 5 +++++ .../src/batch-submitter/tx-batch-submitter.ts | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 .changeset/weak-terms-worry.md diff --git a/.changeset/weak-terms-worry.md b/.changeset/weak-terms-worry.md new file mode 100644 index 000000000000..dd9653d4c69a --- /dev/null +++ b/.changeset/weak-terms-worry.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/batch-submitter': patch +--- + +Log additional data in monotonicity violation diff --git a/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts b/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts index 7515b1028b62..ff7449b730c3 100644 --- a/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts +++ b/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts @@ -356,13 +356,17 @@ export class TransactionBatchSubmitter extends BatchSubmitter { // Verify all of the batch elements are monotonic let lastTimestamp: number let lastBlockNumber: number - for (const ele of batch) { + for (const [idx, ele] of batch.entries()) { if (ele.timestamp < lastTimestamp) { - this.logger.error('Timestamp monotonicity violated! Element', { ele }) + this.logger.error('Timestamp monotonicity violated! Element', { + idx, + ele, + }) return false } if (ele.blockNumber < lastBlockNumber) { this.logger.error('Block Number monotonicity violated! Element', { + idx, ele, }) return false From c520100d04df5be8de6a55c83f516ce370de442c Mon Sep 17 00:00:00 2001 From: Karl Floersch Date: Fri, 11 Jun 2021 18:15:13 -0400 Subject: [PATCH 42/46] fix: monotonicity auto healer (#1070) * fix: monotonicity auto healer * add: changeset --- .changeset/slimy-snails-roll.md | 5 +++++ .../src/batch-submitter/tx-batch-submitter.ts | 19 ++++++------------- 2 files changed, 11 insertions(+), 13 deletions(-) create mode 100644 .changeset/slimy-snails-roll.md diff --git a/.changeset/slimy-snails-roll.md b/.changeset/slimy-snails-roll.md new file mode 100644 index 000000000000..742d78e6d9f3 --- /dev/null +++ b/.changeset/slimy-snails-roll.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/batch-submitter': patch +--- + +Fix a bug in fixMonotonicity auto healer diff --git a/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts b/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts index ff7449b730c3..713c2a560aa8 100644 --- a/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts +++ b/packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts @@ -573,12 +573,8 @@ export class TransactionBatchSubmitter extends BatchSubmitter { oldBlockNumber: ele.blockNumber, newBlockNumber: earliestBlockNumber, }) - fixedBatch.push({ - ...ele, - timestamp: earliestTimestamp, - blockNumber: earliestBlockNumber, - }) - continue + ele.timestamp = earliestTimestamp + ele.blockNumber = earliestBlockNumber } // Fix the element if its timestammp/blockNumber is too large if ( @@ -591,14 +587,11 @@ export class TransactionBatchSubmitter extends BatchSubmitter { oldBlockNumber: ele.blockNumber, newBlockNumber: latestBlockNumber, }) - fixedBatch.push({ - ...ele, - timestamp: latestTimestamp, - blockNumber: latestBlockNumber, - }) - continue + ele.timestamp = latestTimestamp + ele.blockNumber = latestBlockNumber } - // No fixes needed! + earliestTimestamp = ele.timestamp + earliestBlockNumber = ele.blockNumber fixedBatch.push(ele) } return fixedBatch From e389ba105fa167d195444d047cdeae29182d1e45 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jun 2021 18:18:36 -0400 Subject: [PATCH 43/46] Version Packages (#1053) Co-authored-by: github-actions[bot] --- .changeset/angry-numbers-swim.md | 5 ----- .changeset/grumpy-spiders-yell.md | 5 ----- .changeset/modern-chicken-listen.md | 8 -------- .changeset/popular-feet-suffer.md | 5 ----- .changeset/shaggy-dogs-attend.md | 5 ----- .changeset/slimy-snails-roll.md | 5 ----- .changeset/weak-terms-worry.md | 5 ----- l2geth/CHANGELOG.md | 7 +++++++ l2geth/package.json | 2 +- packages/batch-submitter/CHANGELOG.md | 12 ++++++++++++ packages/batch-submitter/package.json | 4 ++-- packages/common-ts/CHANGELOG.md | 6 ++++++ packages/common-ts/package.json | 2 +- packages/data-transport-layer/CHANGELOG.md | 8 ++++++++ packages/data-transport-layer/package.json | 4 ++-- packages/message-relayer/CHANGELOG.md | 8 ++++++++ packages/message-relayer/package.json | 4 ++-- 17 files changed, 49 insertions(+), 46 deletions(-) delete mode 100644 .changeset/angry-numbers-swim.md delete mode 100644 .changeset/grumpy-spiders-yell.md delete mode 100644 .changeset/modern-chicken-listen.md delete mode 100644 .changeset/popular-feet-suffer.md delete mode 100644 .changeset/shaggy-dogs-attend.md delete mode 100644 .changeset/slimy-snails-roll.md delete mode 100644 .changeset/weak-terms-worry.md diff --git a/.changeset/angry-numbers-swim.md b/.changeset/angry-numbers-swim.md deleted file mode 100644 index 59883f090bb0..000000000000 --- a/.changeset/angry-numbers-swim.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/l2geth': patch ---- - -Optimize main polling loops diff --git a/.changeset/grumpy-spiders-yell.md b/.changeset/grumpy-spiders-yell.md deleted file mode 100644 index c231b337cf97..000000000000 --- a/.changeset/grumpy-spiders-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/l2geth': patch ---- - -Bump golang version to 1.15 diff --git a/.changeset/modern-chicken-listen.md b/.changeset/modern-chicken-listen.md deleted file mode 100644 index 22cda8e2674a..000000000000 --- a/.changeset/modern-chicken-listen.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@eth-optimism/batch-submitter': patch -'@eth-optimism/common-ts': patch -'@eth-optimism/data-transport-layer': patch -'@eth-optimism/message-relayer': patch ---- - -Improve Sentry support, initializing as needed and ensuring ERROR logs route to Sentry diff --git a/.changeset/popular-feet-suffer.md b/.changeset/popular-feet-suffer.md deleted file mode 100644 index afb71d29cdb1..000000000000 --- a/.changeset/popular-feet-suffer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/batch-submitter': patch ---- - -Fix typo in USE_HARDHAT config diff --git a/.changeset/shaggy-dogs-attend.md b/.changeset/shaggy-dogs-attend.md deleted file mode 100644 index 66480b391ab0..000000000000 --- a/.changeset/shaggy-dogs-attend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/batch-submitter': patch ---- - -Change monotonicity band-aid code to log warnings not errors diff --git a/.changeset/slimy-snails-roll.md b/.changeset/slimy-snails-roll.md deleted file mode 100644 index 742d78e6d9f3..000000000000 --- a/.changeset/slimy-snails-roll.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/batch-submitter': patch ---- - -Fix a bug in fixMonotonicity auto healer diff --git a/.changeset/weak-terms-worry.md b/.changeset/weak-terms-worry.md deleted file mode 100644 index dd9653d4c69a..000000000000 --- a/.changeset/weak-terms-worry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/batch-submitter': patch ---- - -Log additional data in monotonicity violation diff --git a/l2geth/CHANGELOG.md b/l2geth/CHANGELOG.md index d1962b54be8f..d0a0f3a73c23 100644 --- a/l2geth/CHANGELOG.md +++ b/l2geth/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.3.8 + +### Patch Changes + +- 989a3027: Optimize main polling loops +- cc6c7f07: Bump golang version to 1.15 + ## 0.3.7 ### Patch Changes diff --git a/l2geth/package.json b/l2geth/package.json index cf0f0a6d2fff..56a9a6a93700 100644 --- a/l2geth/package.json +++ b/l2geth/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/l2geth", - "version": "0.3.7", + "version": "0.3.8", "private": true, "devDependencies": {} } diff --git a/packages/batch-submitter/CHANGELOG.md b/packages/batch-submitter/CHANGELOG.md index 02cac6b87960..6b0e1457b7fb 100644 --- a/packages/batch-submitter/CHANGELOG.md +++ b/packages/batch-submitter/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 0.3.4 + +### Patch Changes + +- baa3b761: Improve Sentry support, initializing as needed and ensuring ERROR logs route to Sentry +- cc742715: Fix typo in USE_HARDHAT config +- 98b7839f: Change monotonicity band-aid code to log warnings not errors +- c520100d: Fix a bug in fixMonotonicity auto healer +- 85362d44: Log additional data in monotonicity violation +- Updated dependencies [baa3b761] + - @eth-optimism/common-ts@0.1.3 + ## 0.3.3 ### Patch Changes diff --git a/packages/batch-submitter/package.json b/packages/batch-submitter/package.json index 099278ef041b..102b8c8f6773 100644 --- a/packages/batch-submitter/package.json +++ b/packages/batch-submitter/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/batch-submitter", - "version": "0.3.3", + "version": "0.3.4", "private": true, "description": "[Optimism] Batch submission for sequencer & aggregators", "main": "dist/index", @@ -31,7 +31,7 @@ "url": "https://github.com/ethereum-optimism/optimism-monorepo.git" }, "dependencies": { - "@eth-optimism/common-ts": "^0.1.2", + "@eth-optimism/common-ts": "^0.1.3", "@eth-optimism/contracts": "^0.3.5", "@eth-optimism/core-utils": "^0.4.5", "@eth-optimism/ynatm": "^0.2.2", diff --git a/packages/common-ts/CHANGELOG.md b/packages/common-ts/CHANGELOG.md index 85386b02aaad..60781b255f87 100644 --- a/packages/common-ts/CHANGELOG.md +++ b/packages/common-ts/CHANGELOG.md @@ -1,5 +1,11 @@ # @eth-optimism/common-ts +## 0.1.3 + +### Patch Changes + +- baa3b761: Improve Sentry support, initializing as needed and ensuring ERROR logs route to Sentry + ## 0.1.2 ### Patch Changes diff --git a/packages/common-ts/package.json b/packages/common-ts/package.json index 8bf3d9b75052..ac3f92b2d573 100644 --- a/packages/common-ts/package.json +++ b/packages/common-ts/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/common-ts", - "version": "0.1.2", + "version": "0.1.3", "main": "dist/index", "files": [ "dist/*" diff --git a/packages/data-transport-layer/CHANGELOG.md b/packages/data-transport-layer/CHANGELOG.md index 9b2978947a34..cd696595dba6 100644 --- a/packages/data-transport-layer/CHANGELOG.md +++ b/packages/data-transport-layer/CHANGELOG.md @@ -1,5 +1,13 @@ # data transport layer +## 0.3.6 + +### Patch Changes + +- baa3b761: Improve Sentry support, initializing as needed and ensuring ERROR logs route to Sentry +- Updated dependencies [baa3b761] + - @eth-optimism/common-ts@0.1.3 + ## 0.3.5 ### Patch Changes diff --git a/packages/data-transport-layer/package.json b/packages/data-transport-layer/package.json index 47e0a8158f2e..940bb46393f8 100644 --- a/packages/data-transport-layer/package.json +++ b/packages/data-transport-layer/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/data-transport-layer", - "version": "0.3.5", + "version": "0.3.6", "private": true, "main": "dist/index", "files": [ @@ -21,7 +21,7 @@ "build": "tsc -p tsconfig.build.json" }, "dependencies": { - "@eth-optimism/common-ts": "^0.1.2", + "@eth-optimism/common-ts": "^0.1.3", "@eth-optimism/contracts": "^0.3.5", "@eth-optimism/core-utils": "^0.4.5", "@ethersproject/providers": "^5.0.21", diff --git a/packages/message-relayer/CHANGELOG.md b/packages/message-relayer/CHANGELOG.md index 587238e85699..0a8a41e3a2bb 100644 --- a/packages/message-relayer/CHANGELOG.md +++ b/packages/message-relayer/CHANGELOG.md @@ -1,5 +1,13 @@ # @eth-optimism/message-relayer +## 0.1.5 + +### Patch Changes + +- baa3b761: Improve Sentry support, initializing as needed and ensuring ERROR logs route to Sentry +- Updated dependencies [baa3b761] + - @eth-optimism/common-ts@0.1.3 + ## 0.1.4 ### Patch Changes diff --git a/packages/message-relayer/package.json b/packages/message-relayer/package.json index 08026c85efe6..2847bc3af931 100644 --- a/packages/message-relayer/package.json +++ b/packages/message-relayer/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/message-relayer", - "version": "0.1.4", + "version": "0.1.5", "description": "[Optimism] Cross Domain Message Relayer service", "main": "dist/index", "types": "dist/index", @@ -29,7 +29,7 @@ "url": "https://github.com/ethereum-optimism/optimism.git" }, "dependencies": { - "@eth-optimism/common-ts": "^0.1.2", + "@eth-optimism/common-ts": "^0.1.3", "@eth-optimism/contracts": "^0.3.5", "@eth-optimism/core-utils": "^0.4.5", "@sentry/node": "6.2.5", From 6d7b12b8693117c379be76223448ef36707304d2 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Sun, 13 Jun 2021 10:03:09 -0400 Subject: [PATCH 44/46] fix: comment out codeowners (#1073) --- .github/CODEOWNERS | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index feb1c407fe80..924c601c1677 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,14 +1,18 @@ -l2geth/ @smartcontracts @tynes @karlfloersch -packages/specs/l2geth/ @smartcontracts @tynes @karlfloersch -packages/contracts/ @smartcontracts @ben-chain @maurelian @elenadimitrova -packages/specs/protocol/ @smartcontracts @ben-chain @maurelian -ops/ @tynes @karlfloersch -packages/hardhat-ovm/ @smartcontracts -packages/smock/ @smartcontracts @maurelian -packages/core-utils/ @smartcontracts @annieke @ben-chain -packages/common-ts/ @annieke -packages/core-utils/src/watcher.ts @K-Ho -packages/message-relayer/ @K-Ho -packages/batch-submitter/ @annieke @karlfloersch -packages/data-transport-layer/ @annieke -integration-tests/ @tynes +# CODEOWNERS can be disruptive because it automatically requests review from individuals across the +# board. We still like to use this file to track who's working on what, but all lines are commented +# out so that GitHub won't trigger review requests. + +# l2geth/ @smartcontracts @tynes @karlfloersch +# packages/specs/l2geth/ @smartcontracts @tynes @karlfloersch +# packages/contracts/ @smartcontracts @ben-chain @maurelian @elenadimitrova +# packages/specs/protocol/ @smartcontracts @ben-chain @maurelian +# ops/ @tynes @karlfloersch +# packages/hardhat-ovm/ @smartcontracts +# packages/smock/ @smartcontracts @maurelian +# packages/core-utils/ @smartcontracts @annieke @ben-chain +# packages/common-ts/ @annieke +# packages/core-utils/src/watcher.ts @K-Ho +# packages/message-relayer/ @K-Ho +# packages/batch-submitter/ @annieke @karlfloersch +# packages/data-transport-layer/ @annieke +# integration-tests/ @tynes From f409ce75eed76639bf5fd90060570cf0af0bef51 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Mon, 14 Jun 2021 17:09:35 -0400 Subject: [PATCH 45/46] fix[l2geth]: off-by-one sometimes breaking replica sync (#1082) * fix[l2geth]: off-by-one sometimes breaking replica sync * chore: add changeset --- .changeset/lazy-toes-teach.md | 5 +++++ l2geth/miner/worker.go | 2 +- l2geth/rollup/sync_service.go | 19 ++++++++++--------- 3 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 .changeset/lazy-toes-teach.md diff --git a/.changeset/lazy-toes-teach.md b/.changeset/lazy-toes-teach.md new file mode 100644 index 000000000000..3c482f92504d --- /dev/null +++ b/.changeset/lazy-toes-teach.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/l2geth': patch +--- + +Fixes an off-by-one error that would sometimes break replica syncing when stopping and restarting geth. diff --git a/l2geth/miner/worker.go b/l2geth/miner/worker.go index 2c9a0a177997..e8da4757e148 100644 --- a/l2geth/miner/worker.go +++ b/l2geth/miner/worker.go @@ -196,7 +196,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth), pendingTasks: make(map[common.Hash]*task), txsCh: make(chan core.NewTxsEvent, txChanSize), - rollupCh: make(chan core.NewTxsEvent, txChanSize), + rollupCh: make(chan core.NewTxsEvent, 1), chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), newWorkCh: make(chan *newWorkReq), diff --git a/l2geth/rollup/sync_service.go b/l2geth/rollup/sync_service.go index b3bcfa952853..0b71d20193ee 100644 --- a/l2geth/rollup/sync_service.go +++ b/l2geth/rollup/sync_service.go @@ -243,21 +243,22 @@ func (s *SyncService) initializeLatestL1(ctcDeployHeight *big.Int) error { s.SetLatestL1Timestamp(context.Timestamp) s.SetLatestL1BlockNumber(context.BlockNumber) } else { - // Prevent underflows - if *index != 0 { - *index = *index - 1 - } log.Info("Found latest index", "index", *index) - block := s.bc.GetBlockByNumber(*index) + block := s.bc.GetBlockByNumber(*index + 1) if block == nil { block = s.bc.CurrentBlock() - idx := block.Number().Uint64() - if idx > *index { + blockNum := block.Number().Uint64() + if blockNum > *index { // This is recoverable with a reorg but should never happen return fmt.Errorf("Current block height greater than index") } - s.SetLatestIndex(&idx) - log.Info("Block not found, resetting index", "new", idx, "old", *index) + var idx *uint64 + if blockNum > 0 { + num := blockNum - 1 + idx = &num + } + s.SetLatestIndex(idx) + log.Info("Block not found, resetting index", "new", stringify(idx), "old", *index) } txs := block.Transactions() if len(txs) != 1 { From d9fd67d2502a590e116ffdb6c1c53003a045e318 Mon Sep 17 00:00:00 2001 From: Maurelian Date: Mon, 14 Jun 2021 17:54:31 -0400 Subject: [PATCH 46/46] fix(l2geth): Log 'end of OVM execution' correctly (#1080) --- .changeset/ten-pumas-perform.md | 5 +++++ l2geth/core/vm/evm.go | 7 +++---- 2 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 .changeset/ten-pumas-perform.md diff --git a/.changeset/ten-pumas-perform.md b/.changeset/ten-pumas-perform.md new file mode 100644 index 000000000000..e40a6484c396 --- /dev/null +++ b/.changeset/ten-pumas-perform.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/l2geth': patch +--- + +Correctly log 'end of OVM execution' message. diff --git a/l2geth/core/vm/evm.go b/l2geth/core/vm/evm.go index b54b9bd5d777..20fdc2dfcb60 100644 --- a/l2geth/core/vm/evm.go +++ b/l2geth/core/vm/evm.go @@ -464,10 +464,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas ret = []byte{} } } - } - - if evm.Context.EthCallSender == nil { - log.Debug("Reached the end of an OVM execution", "ID", evm.Id, "Return Data", hexutil.Encode(ret), "Error", err) + if evm.Context.EthCallSender == nil { + log.Debug("Reached the end of an OVM execution", "ID", evm.Id, "Return Data", hexutil.Encode(ret), "Error", err) + } } }