diff --git a/.changeset/smart-readers-remember.md b/.changeset/smart-readers-remember.md new file mode 100644 index 0000000000000..bd19d949e5391 --- /dev/null +++ b/.changeset/smart-readers-remember.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/sdk': patch +--- + +Fixes a bug in the SDK which would cause the SDK to throw if no tx nonce is provided diff --git a/.changeset/sweet-trains-hunt.md b/.changeset/sweet-trains-hunt.md new file mode 100644 index 0000000000000..e05288a51eb15 --- /dev/null +++ b/.changeset/sweet-trains-hunt.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/batch-submitter-service': patch +--- + +Add MAX_PLAINTEXT_BATCH_SIZE parameter to max out compression diff --git a/batch-submitter/batch_submitter.go b/batch-submitter/batch_submitter.go index 168c3e49906d6..53a1d118c5fa8 100644 --- a/batch-submitter/batch_submitter.go +++ b/batch-submitter/batch_submitter.go @@ -121,16 +121,17 @@ func Main(gitVersion string) func(ctx *cli.Context) error { var services []*bsscore.Service if cfg.RunTxBatchSubmitter { batchTxDriver, err := sequencer.NewDriver(sequencer.Config{ - Name: "Sequencer", - L1Client: l1Client, - L2Client: l2Client, - BlockOffset: cfg.BlockOffset, - MinTxSize: cfg.MinL1TxSize, - MaxTxSize: cfg.MaxL1TxSize, - CTCAddr: ctcAddress, - ChainID: chainID, - PrivKey: sequencerPrivKey, - BatchType: sequencer.BatchTypeFromString(cfg.SequencerBatchType), + Name: "Sequencer", + L1Client: l1Client, + L2Client: l2Client, + BlockOffset: cfg.BlockOffset, + MinTxSize: cfg.MinL1TxSize, + MaxTxSize: cfg.MaxL1TxSize, + MaxPlaintextBatchSize: cfg.MaxPlaintextBatchSize, + CTCAddr: ctcAddress, + ChainID: chainID, + PrivKey: sequencerPrivKey, + BatchType: sequencer.BatchTypeFromString(cfg.SequencerBatchType), }) if err != nil { return err diff --git a/batch-submitter/config.go b/batch-submitter/config.go index 6f87e1cbc3ae1..724e489497ba7 100644 --- a/batch-submitter/config.go +++ b/batch-submitter/config.go @@ -74,6 +74,10 @@ type Config struct { // by the batch submitter. MaxL1TxSize uint64 + // MaxPlaintextL1TxSize is the maximum size in bytes of the plaintext tx + // data encoded in batches. + MaxPlaintextBatchSize uint64 + // MinStateRootElements is the minimum number of state root elements that // can be submitted in single proposer batch. MinStateRootElements uint64 @@ -203,6 +207,7 @@ func NewConfig(ctx *cli.Context) (Config, error) { SCCAddress: ctx.GlobalString(flags.SCCAddressFlag.Name), MinL1TxSize: ctx.GlobalUint64(flags.MinL1TxSizeFlag.Name), MaxL1TxSize: ctx.GlobalUint64(flags.MaxL1TxSizeFlag.Name), + MaxPlaintextBatchSize: ctx.GlobalUint64(flags.MaxPlaintextBatchSizeFlag.Name), MinStateRootElements: ctx.GlobalUint64(flags.MinStateRootElementsFlag.Name), MaxStateRootElements: ctx.GlobalUint64(flags.MinStateRootElementsFlag.Name), MaxBatchSubmissionTime: ctx.GlobalDuration(flags.MaxBatchSubmissionTimeFlag.Name), diff --git a/batch-submitter/drivers/sequencer/driver.go b/batch-submitter/drivers/sequencer/driver.go index ef8b5d09ee201..c0f567e07fa43 100644 --- a/batch-submitter/drivers/sequencer/driver.go +++ b/batch-submitter/drivers/sequencer/driver.go @@ -29,16 +29,17 @@ const ( var bigOne = new(big.Int).SetUint64(1) type Config struct { - Name string - L1Client *ethclient.Client - L2Client *l2ethclient.Client - BlockOffset uint64 - MinTxSize uint64 - MaxTxSize uint64 - CTCAddr common.Address - ChainID *big.Int - PrivKey *ecdsa.PrivateKey - BatchType BatchType + Name string + L1Client *ethclient.Client + L2Client *l2ethclient.Client + BlockOffset uint64 + MinTxSize uint64 + MaxTxSize uint64 + MaxPlaintextBatchSize uint64 + CTCAddr common.Address + ChainID *big.Int + PrivKey *ecdsa.PrivateKey + BatchType BatchType } type Driver struct { @@ -187,7 +188,7 @@ func (d *Driver) CraftBatchTx( // Below this set will be further whittled until the raw call data // size also adheres to this constraint. txLen := batchElement.Tx.Size() - if totalTxSize+uint64(TxLenSize+txLen) > d.cfg.MaxTxSize { + if totalTxSize+uint64(TxLenSize+txLen) > d.cfg.MaxPlaintextBatchSize { // Adding this transaction causes the batch to be too large, but // we also record if the batch size without the transaction // fails to meet our minimum size constraint. This is used below @@ -212,24 +213,24 @@ func (d *Driver) CraftBatchTx( return nil, err } - // Use plaintext encoding to enforce size constraints. - plaintextBatchArguments, err := batchParams.Serialize(BatchTypeLegacy) + // Encode the batch arguments using the configured encoding type. + batchArguments, err := batchParams.Serialize(d.cfg.BatchType) if err != nil { return nil, err } appendSequencerBatchID := d.ctcABI.Methods[appendSequencerBatchMethodName].ID - plaintextCalldata := append(appendSequencerBatchID, plaintextBatchArguments...) + calldata := append(appendSequencerBatchID, batchArguments...) log.Info(name+" testing batch size", - "plaintext_size", len(plaintextCalldata), + "calldata_size", len(calldata), "min_tx_size", d.cfg.MinTxSize, "max_tx_size", d.cfg.MaxTxSize) // Continue pruning until plaintext calldata size is less than // configured max. - plaintextCalldataSize := uint64(len(plaintextCalldata)) - if plaintextCalldataSize > d.cfg.MaxTxSize { + calldataSize := uint64(len(calldata)) + if calldataSize > d.cfg.MaxTxSize { oldLen := len(batchElements) newBatchElementsLen := (oldLen * 9) / 10 batchElements = batchElements[:newBatchElementsLen] @@ -259,7 +260,7 @@ func (d *Driver) CraftBatchTx( // becomes too small as a result. This is avoided by only applying // the min size check when the pruneCount is zero. ignoreMinTxSize := pruneCount > 0 || hasLargeNextTx - if !ignoreMinTxSize && plaintextCalldataSize < d.cfg.MinTxSize { + if !ignoreMinTxSize && calldataSize < d.cfg.MinTxSize { log.Info(name+" batch tx size below minimum", "num_txs", len(batchElements)) return nil, nil @@ -268,16 +269,6 @@ func (d *Driver) CraftBatchTx( d.metrics.NumElementsPerBatch().Observe(float64(len(batchElements))) d.metrics.BatchPruneCount.Set(float64(pruneCount)) - // Finally, encode the batch using the configured batch type. - var calldata = plaintextCalldata - if d.cfg.BatchType != BatchTypeLegacy { - batchArguments, err := batchParams.Serialize(d.cfg.BatchType) - if err != nil { - return nil, err - } - calldata = append(appendSequencerBatchID, batchArguments...) - } - log.Info(name+" batch constructed", "num_txs", len(batchElements), "final_size", len(calldata), diff --git a/batch-submitter/flags/flags.go b/batch-submitter/flags/flags.go index 9f01c7b3a9a5c..18dfdc1b2894d 100644 --- a/batch-submitter/flags/flags.go +++ b/batch-submitter/flags/flags.go @@ -66,6 +66,13 @@ var ( Required: true, EnvVar: prefixEnvVar("MAX_L1_TX_SIZE"), } + MaxPlaintextBatchSizeFlag = cli.Uint64Flag{ + Name: "max-plaintext-batch-size", + Usage: "Maximum size in bytes of of the plaintext tx data " + + "encoded in batches", + Required: true, + EnvVar: prefixEnvVar("MAX_PLAINTEXT_BATCH_SIZE"), + } MinStateRootElementsFlag = cli.Uint64Flag{ Name: "min-state-root-elements", Usage: "Minimum number of elements required to submit a state " + @@ -254,6 +261,7 @@ var requiredFlags = []cli.Flag{ SCCAddressFlag, MinL1TxSizeFlag, MaxL1TxSizeFlag, + MaxPlaintextBatchSizeFlag, MinStateRootElementsFlag, MaxStateRootElementsFlag, MaxBatchSubmissionTimeFlag, diff --git a/ops/envs/batch-submitter.env b/ops/envs/batch-submitter.env index a55deab4c1c1d..412f50259b448 100644 --- a/ops/envs/batch-submitter.env +++ b/ops/envs/batch-submitter.env @@ -6,6 +6,7 @@ BATCH_SUBMITTER_LOG_LEVEL=debug BATCH_SUBMITTER_LOG_TERMINAL=true BATCH_SUBMITTER_MIN_L1_TX_SIZE=32 BATCH_SUBMITTER_MAX_L1_TX_SIZE=90000 +BATCH_SUBMITTER_MAX_PLAINTEXT_BATCH_SIZE=120000 BATCH_SUBMITTER_MIN_STATE_ROOT_ELEMENTS=1 BATCH_SUBMITTER_MAX_STATE_ROOT_ELEMENTS=3000 BATCH_SUBMITTER_MAX_BATCH_SUBMISSION_TIME=0 diff --git a/packages/sdk/src/l2-provider.ts b/packages/sdk/src/l2-provider.ts index 5fa2b7a2f4ac9..9b41c7891c095 100644 --- a/packages/sdk/src/l2-provider.ts +++ b/packages/sdk/src/l2-provider.ts @@ -11,6 +11,27 @@ import { toProvider, toNumber, toBigNumber } from './utils' type ProviderTypeIsWrong = any +/** + * Gets a reasonable nonce for the transaction. + * + * @param provider Provider to get the nonce from. + * @param tx Requested transaction. + * @returns A reasonable nonce for the transaction. + */ +const getNonceForTx = async ( + provider: ProviderLike, + tx: TransactionRequest +): Promise => { + if (tx.nonce !== undefined) { + return toNumber(tx.nonce as NumberLike) + } else if (tx.from !== undefined) { + return toProvider(provider).getTransactionCount(tx.from) + } else { + // Large nonce with lots of non-zero bytes + return 0xffffffff + } +} + /** * Returns a Contract object for the GasPriceOracle. * @@ -57,7 +78,7 @@ export const estimateL1Gas = async ( gasPrice: tx.gasPrice, type: tx.type, gasLimit: tx.gasLimit, - nonce: toNumber(tx.nonce as NumberLike), + nonce: await getNonceForTx(l2Provider, tx), }) ) } @@ -81,7 +102,7 @@ export const estimateL1GasCost = async ( gasPrice: tx.gasPrice, type: tx.type, gasLimit: tx.gasLimit, - nonce: toNumber(tx.nonce as NumberLike), + nonce: await getNonceForTx(l2Provider, tx), }) ) }