diff --git a/.changeset/big-pants-remain.md b/.changeset/big-pants-remain.md new file mode 100644 index 0000000000000..a84d52df68058 --- /dev/null +++ b/.changeset/big-pants-remain.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/l2geth': patch +--- + +Add support to fully unmarshal Receipts with Optimism fields diff --git a/.changeset/dull-ladybugs-learn.md b/.changeset/dull-ladybugs-learn.md new file mode 100644 index 0000000000000..abe655483dc4f --- /dev/null +++ b/.changeset/dull-ladybugs-learn.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/l2geth': patch +--- + +Add changeset for https://github.com/ethereum-optimism/optimism/pull/2011 - replicas forward write requests to the sequencer via a configured parameter `--sequencer.clienthttp` or `SEQUENCER_CLIENT_HTTP` diff --git a/.changeset/fluffy-teachers-greet.md b/.changeset/fluffy-teachers-greet.md new file mode 100644 index 0000000000000..1fa18f332cb4a --- /dev/null +++ b/.changeset/fluffy-teachers-greet.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/l2geth': patch +--- + +Correctly parse fee enforcement via config to allow turning off L2 fees for development diff --git a/.github/workflows/ts-packages.yml b/.github/workflows/ts-packages.yml index ec38fa9a19f96..8968fcb73999f 100644 --- a/.github/workflows/ts-packages.yml +++ b/.github/workflows/ts-packages.yml @@ -138,6 +138,66 @@ jobs: verbose: true flags: sdk + depcheck: + name: Check for unused dependencies + 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: '16.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: Check packages/batch-submitter + working-directory: ./packages/batch-submitter + run: npx depcheck + + - name: Check packages/contracts + working-directory: ./packages/contracts + run: npx depcheck + + - name: Check packages/core-utils + working-directory: ./packages/core-utils + run: npx depcheck + + - name: Check packages/data-transport-layer + working-directory: ./packages/data-transport-layer + run: npx depcheck + + - name: Check packages/message-relayer + working-directory: ./packages/message-relayer + run: npx depcheck + + - name: Check packages/sdk + working-directory: ./packages/sdk + run: npx depcheck + + - name: Check integration-tests + working-directory: ./integration-tests + run: npx depcheck + lint: name: Linting runs-on: ubuntu-latest diff --git a/integration-tests/.depcheckrc b/integration-tests/.depcheckrc new file mode 100644 index 0000000000000..3401fedfc718c --- /dev/null +++ b/integration-tests/.depcheckrc @@ -0,0 +1,8 @@ +ignores: [ + "@openzeppelin/contracts", + "@types/mocha", + "@types/rimraf", + "@uniswap/v3-core", + "mocha", + "typescript", +] \ No newline at end of file diff --git a/integration-tests/test/replica.spec.ts b/integration-tests/test/replica.spec.ts index 165461d90fe8c..6f157ae2feb18 100644 --- a/integration-tests/test/replica.spec.ts +++ b/integration-tests/test/replica.spec.ts @@ -71,5 +71,32 @@ describe('Replica Tests', () => { expect(sequencerBlock.stateRoot).to.deep.eq(replicaBlock.stateRoot) expect(sequencerBlock.hash).to.deep.eq(replicaBlock.hash) }) + + it('should forward tx to sequencer', async () => { + const tx = { + ...defaultTransactionFactory(), + nonce: await env.l2Wallet.getTransactionCount(), + gasPrice: await gasPriceForL2(env), + } + const signed = await env.l2Wallet.signTransaction(tx) + const result = await env.replicaProvider.sendTransaction(signed) + + let receipt: TransactionReceipt + while (!receipt) { + receipt = await env.replicaProvider.getTransactionReceipt(result.hash) + await sleep(200) + } + + const sequencerBlock = (await env.l2Provider.getBlock( + result.blockNumber + )) as any + + const replicaBlock = (await env.replicaProvider.getBlock( + result.blockNumber + )) as any + + expect(sequencerBlock.stateRoot).to.deep.eq(replicaBlock.stateRoot) + expect(sequencerBlock.hash).to.deep.eq(replicaBlock.hash) + }) }) }) diff --git a/l2geth/cmd/geth/main.go b/l2geth/cmd/geth/main.go index 6ca66ab7021d5..c38c993670b06 100644 --- a/l2geth/cmd/geth/main.go +++ b/l2geth/cmd/geth/main.go @@ -163,6 +163,7 @@ var ( utils.RollupEnforceFeesFlag, utils.RollupFeeThresholdDownFlag, utils.RollupFeeThresholdUpFlag, + utils.SequencerClientHttpFlag, } rpcFlags = []cli.Flag{ diff --git a/l2geth/cmd/geth/usage.go b/l2geth/cmd/geth/usage.go index 87d8ac6c4b74b..9a9321addbd43 100644 --- a/l2geth/cmd/geth/usage.go +++ b/l2geth/cmd/geth/usage.go @@ -77,6 +77,7 @@ var AppHelpFlagGroups = []flagGroup{ utils.RollupEnforceFeesFlag, utils.RollupFeeThresholdDownFlag, utils.RollupFeeThresholdUpFlag, + utils.SequencerClientHttpFlag, }, }, { diff --git a/l2geth/cmd/utils/flags.go b/l2geth/cmd/utils/flags.go index 3195889dbac68..f071f3f768a8c 100644 --- a/l2geth/cmd/utils/flags.go +++ b/l2geth/cmd/utils/flags.go @@ -861,6 +861,11 @@ var ( Usage: "Allow txs with fees above the current fee up to this amount, must be > 1", EnvVar: "ROLLUP_FEE_THRESHOLD_UP", } + SequencerClientHttpFlag = cli.StringFlag{ + Name: "sequencer.clienthttp", + Usage: "HTTP endpoint for the sequencer client", + EnvVar: "SEQUENCER_CLIENT_HTTP", + } ) // MakeDataDir retrieves the currently requested data directory, terminating @@ -1127,7 +1132,7 @@ func setRollup(ctx *cli.Context, cfg *rollup.Config) { cfg.Backend = backend } if ctx.GlobalIsSet(RollupEnforceFeesFlag.Name) { - cfg.EnforceFees = true + cfg.EnforceFees = ctx.GlobalBool(RollupEnforceFeesFlag.Name) } if ctx.GlobalIsSet(RollupFeeThresholdDownFlag.Name) { val := ctx.GlobalFloat64(RollupFeeThresholdDownFlag.Name) @@ -1137,6 +1142,9 @@ func setRollup(ctx *cli.Context, cfg *rollup.Config) { val := ctx.GlobalFloat64(RollupFeeThresholdUpFlag.Name) cfg.FeeThresholdUp = new(big.Float).SetFloat64(val) } + if ctx.GlobalIsSet(SequencerClientHttpFlag.Name) { + cfg.SequencerClientHttp = ctx.GlobalString(SequencerClientHttpFlag.Name) + } } // setLes configures the les server and ultra light client settings from the command line flags. diff --git a/l2geth/core/types/gen_receipt_json.go b/l2geth/core/types/gen_receipt_json.go index af89de6a39b8f..875fe11810d4a 100644 --- a/l2geth/core/types/gen_receipt_json.go +++ b/l2geth/core/types/gen_receipt_json.go @@ -27,9 +27,9 @@ func (r Receipt) MarshalJSON() ([]byte, error) { BlockHash common.Hash `json:"blockHash,omitempty"` BlockNumber *hexutil.Big `json:"blockNumber,omitempty"` TransactionIndex hexutil.Uint `json:"transactionIndex"` - L1GasPrice *big.Int `json:"l1GasPrice" gencodec:"required"` - L1GasUsed *big.Int `json:"l1GasUsed" gencodec:"required"` - L1Fee *big.Int `json:"l1Fee" gencodec:"required"` + L1GasPrice *hexutil.Big `json:"l1GasPrice" gencodec:"required"` + L1GasUsed *hexutil.Big `json:"l1GasUsed" gencodec:"required"` + L1Fee *hexutil.Big `json:"l1Fee" gencodec:"required"` FeeScalar *big.Float `json:"l1FeeScalar" gencodec:"required"` } var enc Receipt @@ -44,9 +44,9 @@ func (r Receipt) MarshalJSON() ([]byte, error) { enc.BlockHash = r.BlockHash enc.BlockNumber = (*hexutil.Big)(r.BlockNumber) enc.TransactionIndex = hexutil.Uint(r.TransactionIndex) - enc.L1GasPrice = r.L1GasPrice - enc.L1GasUsed = r.L1GasUsed - enc.L1Fee = r.L1Fee + enc.L1GasPrice = (*hexutil.Big)(r.L1GasPrice) + enc.L1GasUsed = (*hexutil.Big)(r.L1GasUsed) + enc.L1Fee = (*hexutil.Big)(r.L1Fee) enc.FeeScalar = r.FeeScalar return json.Marshal(&enc) } @@ -65,9 +65,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { BlockHash *common.Hash `json:"blockHash,omitempty"` BlockNumber *hexutil.Big `json:"blockNumber,omitempty"` TransactionIndex *hexutil.Uint `json:"transactionIndex"` - L1GasPrice *big.Int `json:"l1GasPrice" gencodec:"required"` - L1GasUsed *big.Int `json:"l1GasUsed" gencodec:"required"` - L1Fee *big.Int `json:"l1Fee" gencodec:"required"` + L1GasPrice *hexutil.Big `json:"l1GasPrice" gencodec:"required"` + L1GasUsed *hexutil.Big `json:"l1GasUsed" gencodec:"required"` + L1Fee *hexutil.Big `json:"l1Fee" gencodec:"required"` FeeScalar *big.Float `json:"l1FeeScalar" gencodec:"required"` } var dec Receipt @@ -115,15 +115,15 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { if dec.L1GasPrice == nil { return errors.New("missing required field 'l1GasPrice' for Receipt") } - r.L1GasPrice = dec.L1GasPrice + r.L1GasPrice = (*big.Int)(dec.L1GasPrice) if dec.L1GasUsed == nil { return errors.New("missing required field 'l1GasUsed' for Receipt") } - r.L1GasUsed = dec.L1GasUsed + r.L1GasUsed = (*big.Int)(dec.L1GasUsed) if dec.L1Fee == nil { return errors.New("missing required field 'l1Fee' for Receipt") } - r.L1Fee = dec.L1Fee + r.L1Fee = (*big.Int)(dec.L1Fee) if dec.FeeScalar == nil { return errors.New("missing required field 'l1FeeScalar' for Receipt") } diff --git a/l2geth/core/types/receipt.go b/l2geth/core/types/receipt.go index b08ca724cf196..994fc4e0d36e3 100644 --- a/l2geth/core/types/receipt.go +++ b/l2geth/core/types/receipt.go @@ -81,6 +81,9 @@ type receiptMarshaling struct { GasUsed hexutil.Uint64 BlockNumber *hexutil.Big TransactionIndex hexutil.Uint + L1GasPrice *hexutil.Big + L1GasUsed *hexutil.Big + L1Fee *hexutil.Big } // receiptRLP is the consensus encoding of a receipt. diff --git a/l2geth/eth/api_backend.go b/l2geth/eth/api_backend.go index 0f4ee88c1e72b..342163c48f59c 100644 --- a/l2geth/eth/api_backend.go +++ b/l2geth/eth/api_backend.go @@ -136,6 +136,10 @@ func (b *EthAPIBackend) IngestTransactions(txs []*types.Transaction) error { return nil } +func (b *EthAPIBackend) SequencerClientHttp() string { + return b.eth.config.Rollup.SequencerClientHttp +} + func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { // Pending block is only known by the miner if number == rpc.PendingBlockNumber { diff --git a/l2geth/internal/ethapi/api.go b/l2geth/internal/ethapi/api.go index bbb33ea1a6b47..8240b3b1a762d 100644 --- a/l2geth/internal/ethapi/api.go +++ b/l2geth/internal/ethapi/api.go @@ -40,6 +40,7 @@ import ( "github.com/ethereum-optimism/optimism/l2geth/core/types" "github.com/ethereum-optimism/optimism/l2geth/core/vm" "github.com/ethereum-optimism/optimism/l2geth/crypto" + "github.com/ethereum-optimism/optimism/l2geth/ethclient" "github.com/ethereum-optimism/optimism/l2geth/log" "github.com/ethereum-optimism/optimism/l2geth/p2p" "github.com/ethereum-optimism/optimism/l2geth/params" @@ -51,6 +52,12 @@ import ( var errOVMUnsupported = errors.New("OVM: Unsupported RPC Method") +const ( + // defaultDialTimeout is default duration the service will wait on + // startup to make a connection to either the L1 or L2 backends. + defaultDialTimeout = 5 * time.Second +) + // PublicEthereumAPI provides an API to access Ethereum related information. // It offers only methods that operate on public data that is freely available to anyone. type PublicEthereumAPI struct { @@ -1289,6 +1296,18 @@ func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransa return nil } +// dialSequencerClientWithTimeout attempts to dial the Sequencer using the +// provided URL. If the dial doesn't complete within defaultDialTimeout +// seconds, this method will return an error. +func dialSequencerClientWithTimeout(ctx context.Context, url string) ( + *ethclient.Client, error) { + + ctxt, cancel := context.WithTimeout(ctx, defaultDialTimeout) + defer cancel() + + return ethclient.DialContext(ctxt, url) +} + // PublicTransactionPoolAPI exposes methods for the RPC interface type PublicTransactionPoolAPI struct { b Backend @@ -1649,18 +1668,27 @@ func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args Sen // SendRawTransaction will add the signed transaction to the transaction pool. // The sender is responsible for signing the transaction and using the correct nonce. func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) { - if s.b.IsVerifier() { - return common.Hash{}, errors.New("Cannot send raw transaction in verifier mode") + tx := new(types.Transaction) + if err := rlp.DecodeBytes(encodedTx, tx); err != nil { + return common.Hash{}, err } if s.b.IsSyncing() { return common.Hash{}, errors.New("Cannot send raw transaction while syncing") } - tx := new(types.Transaction) - if err := rlp.DecodeBytes(encodedTx, tx); err != nil { - return common.Hash{}, err + if s.b.IsVerifier() { + client, err := dialSequencerClientWithTimeout(ctx, s.b.SequencerClientHttp()) + if err != nil { + return common.Hash{}, err + } + err = client.SendTransaction(context.Background(), tx) + if err != nil { + return common.Hash{}, err + } + return tx.Hash(), nil } + // L1Timestamp and L1BlockNumber will be set right before execution txMeta := types.NewTransactionMeta(nil, 0, nil, types.QueueOriginSequencer, nil, nil, encodedTx) tx.SetTransactionMeta(txMeta) diff --git a/l2geth/internal/ethapi/backend.go b/l2geth/internal/ethapi/backend.go index df8aef030ace4..a6a21f43339a8 100644 --- a/l2geth/internal/ethapi/backend.go +++ b/l2geth/internal/ethapi/backend.go @@ -96,6 +96,7 @@ type Backend interface { SuggestL2GasPrice(context.Context) (*big.Int, error) SetL2GasPrice(context.Context, *big.Int) error IngestTransactions([]*types.Transaction) error + SequencerClientHttp() string } func GetAPIs(apiBackend Backend) []rpc.API { diff --git a/l2geth/les/api_backend.go b/l2geth/les/api_backend.go index c95fd87ab98f8..24623f6eb5ccc 100644 --- a/l2geth/les/api_backend.go +++ b/l2geth/les/api_backend.go @@ -86,6 +86,10 @@ func (b *LesApiBackend) IngestTransactions([]*types.Transaction) error { panic("not implemented") } +func (b *LesApiBackend) SequencerClientHttp() string { + return b.eth.config.Rollup.SequencerClientHttp +} + func (b *LesApiBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { if number == rpc.LatestBlockNumber || number == rpc.PendingBlockNumber { return b.eth.blockchain.CurrentHeader(), nil diff --git a/l2geth/rollup/config.go b/l2geth/rollup/config.go index 6d28466549d3e..c13e1394c7921 100644 --- a/l2geth/rollup/config.go +++ b/l2geth/rollup/config.go @@ -37,4 +37,6 @@ type Config struct { // quoted and the transaction being executed FeeThresholdDown *big.Float FeeThresholdUp *big.Float + // HTTP endpoint of the sequencer + SequencerClientHttp string } diff --git a/ops/README.md b/ops/README.md index 49319933fda53..8062187de97f2 100644 --- a/ops/README.md +++ b/ops/README.md @@ -58,6 +58,15 @@ A Makefile has been provided for convience. The following targets are available. - make up-metrics - make down-metrics +## Turning off L2 Fee Enforcement + +Fees can be turned off at runtime by setting the environment variable +`ROLLUP_ENFORCE_FEES` to `false`. + +```bash +ROLLUP_ENFORCE_FEES=false docker-compose up +``` + ## Using the Go Batch Submitter The existing Typescript batch submitter is in the process of being reimplemented diff --git a/ops/docker-compose.yml b/ops/docker-compose.yml index 1c428bbebfda9..4a81acfa25a50 100644 --- a/ops/docker-compose.yml +++ b/ops/docker-compose.yml @@ -101,6 +101,10 @@ services: # no need to keep this secret, only used internally to sign blocks BLOCK_SIGNER_KEY: "6587ae678cf4fc9a33000cdbf9f35226b71dcc6a4684a31203241f9bcfd55d27" BLOCK_SIGNER_ADDRESS: "0x00000398232E2064F896018496b4b44b3D62751F" + + ROLLUP_ENFORCE_FEES: ${ROLLUP_ENFORCE_FEES:-true} + ROLLUP_FEE_THRESHOLD_DOWN: 0.9 + ROLLUP_FEE_THRESHOLD_UP: 1.1 ports: - ${L2GETH_HTTP_PORT:-8545}:8545 - ${L2GETH_WS_PORT:-8546}:8546 @@ -155,6 +159,7 @@ services: replica: depends_on: - dtl + - l2geth deploy: replicas: 1 build: @@ -165,6 +170,7 @@ services: - ./envs/geth.env environment: ETH1_HTTP: http://l1_chain:8545 + SEQUENCER_CLIENT_HTTP: http://l2geth:8545 ROLLUP_STATE_DUMP_PATH: http://deployer:8081/state-dump.latest.json ROLLUP_CLIENT_HTTP: http://dtl:7878 ROLLUP_BACKEND: 'l2' diff --git a/ops/envs/geth.env b/ops/envs/geth.env index d3863718f1cf7..36b2a00990bb3 100644 --- a/ops/envs/geth.env +++ b/ops/envs/geth.env @@ -6,7 +6,6 @@ ETH1_CONFIRMATION_DEPTH=0 ROLLUP_CLIENT_HTTP= ROLLUP_POLL_INTERVAL_FLAG=500ms ROLLUP_ENABLE_L2_GAS_POLLING=true -ROLLUP_ENFORCE_FEES=true RPC_ENABLE=true RPC_ADDR=0.0.0.0 @@ -36,6 +35,3 @@ BLOCK_SIGNER_KEY=6587ae678cf4fc9a33000cdbf9f35226b71dcc6a4684a31203241f9bcfd55d2 BLOCK_SIGNER_ADDRESS=0x00000398232E2064F896018496b4b44b3D62751F L2_BLOCK_GAS_LIMIT=15000000 - -ROLLUP_FEE_THRESHOLD_DOWN=0.9 -ROLLUP_FEE_THRESHOLD_UP=1.1 diff --git a/packages/contracts/.depcheckrc b/packages/contracts/.depcheckrc new file mode 100644 index 0000000000000..df14a6c252919 --- /dev/null +++ b/packages/contracts/.depcheckrc @@ -0,0 +1,11 @@ +ignores: [ + "@codechecks/client", + "@ethersproject/transactions", + "@openzeppelin/contracts", + "@openzeppelin/contracts-upgradeable", + "@typechain/ethers-v5", + "prettier-plugin-solidity", + "solhint-plugin-prettier", + "ts-generator", + "yargs", +] \ No newline at end of file diff --git a/packages/sdk/.depcheckrc b/packages/sdk/.depcheckrc new file mode 100644 index 0000000000000..aed57c26f51ef --- /dev/null +++ b/packages/sdk/.depcheckrc @@ -0,0 +1,4 @@ +ignores: [ + "@eth-optimism/core-utils", + "ts-mocha", +] \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index d494a7e9264ed..8c3ce2fad23c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7928,9 +7928,9 @@ fmix@^0.1.0: imul "^1.0.0" follow-redirects@^1.12.1, follow-redirects@^1.14.0: - version "1.14.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.3.tgz#6ada78118d8d24caee595595accdc0ac6abd022e" - integrity sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw== + version "1.14.7" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" + integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== for-each@^0.3.3, for-each@~0.3.3: version "0.3.3" @@ -13964,9 +13964,9 @@ shebang-regex@^3.0.0: integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shelljs@^0.8.3, shelljs@^0.8.4: - version "0.8.4" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" - integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== + version "0.8.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== dependencies: glob "^7.0.0" interpret "^1.0.0"