-
Couldn't load subscription status.
- Fork 3.8k
fix: estimate gas when pending tx in mempool #1024
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: estimate gas when pending tx in mempool #1024
Conversation
🦋 Changeset detectedLatest commit: e2985bf The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
34cd37a to
b979d38
Compare
Codecov Report
@@ Coverage Diff @@
## develop #1024 +/- ##
========================================
Coverage 86.05% 86.05%
========================================
Files 49 49
Lines 1936 1936
Branches 307 307
========================================
Hits 1666 1666
Misses 270 270 Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Questions:
- Can you clarify (maybe in the code) how we are getting around the gas estimation error being thrown despite still making a call to
this.signer.provider.estimateGas? Does an explicit call to this method not "rebase" on pending transactions? - Does the old
appendSequencerBatchoverride now become unused? If so we should delete this code. - Does this now have the effect of making this code unable to submit multiple batches in series assuming each of them will succeed? Do we want this behaviour, even?
Requests:
- We need to add a test case or two for these scenarios that would fail before this branch is merged
| const nonce = await this.signer.getTransactionCount() | ||
| // Generate the transaction we will repeatedly submit | ||
| const tx = await this.chainContract.populateTransaction.appendQueueBatch( | ||
| 99999999 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This 9999999 constant really needs a comment or some kind of explanation, it's unclear why this number exists here in the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, we could delete this code path completely since queue batch append isn't currently allowed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's worth keeping around for posterity sake until we split the CTC and can delete it completely. We don't want to forget that in our current design this codepath is absolutely required for the eventual proper functioning of the system.
packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts
Outdated
Show resolved
Hide resolved
| l1tipHeight, | ||
| }) | ||
|
|
||
| const nonce = await this.signer.getTransactionCount() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to this PR, but why are we even doing this if we are using populateTransaction; shouldn't that automatically be handling the nonce selection for us?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it definitely should. Just removed it.
I left it in to avoid changing lines of code that I didn't have to, but I think removing the manual nonce setting is a good improvement.
| data: '0x' + methodId + calldata, | ||
| data: getEncodedCalldata(batch), | ||
| ...options, | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this method now unused?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wow yes it is! I'm thinking it could make sense to leave it in in case we ever tried to call appendSequencerBatch and it fails because it doesn't accept any parameters. But you can let me know if you want me to just delete it -- I'm happy to
packages/batch-submitter/src/batch-submitter/state-batch-submitter.ts
Outdated
Show resolved
Hide resolved
packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts
Outdated
Show resolved
Hide resolved
| l1tipHeight, | ||
| }) | ||
|
|
||
| const nonce = await this.signer.getTransactionCount() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting the nonce both outside and inside of customPopulateTransaction.appendSequencerBatch seems like its prone to a race condition. Note that I do think its very unlikely in practice, but the function does take an optional second argument which may be able to take the nonce.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes agreed, I just deleted the nonce logic
| public customPopulateTransaction = { | ||
| appendSequencerBatch: async ( | ||
| batch: AppendSequencerBatchParams, | ||
| options?: TransactionRequest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The options aren't really used at all here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah good catch, removing the options.
| from: await this.signer.getAddress(), | ||
| data, | ||
| }) | ||
| const value = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
value looks unused?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I just set it to zero explicitly. Probably could leave it out entirely but was just making zero value explicit. Let me know if you want me to remove that line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its not returned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah you're right -- deleted it
|
I'd just re-assert that we should add a test case before merging. |
|
How important is this? Its been open for awhile and is almost there |
|
It has caused extended issues on mainnet production in the past. It is a priority |
|
@snario how would you suggest testing this? The options I see are as follows:
This is another example of where the batch submitter's structure is extremely difficult to test. Does option (1) suffice you think? If so I'll go ahead and add it -- should be a pretty quick refactor |
👍 |
|
This is going to need to have its commits cleaned up now that we do a rebase/merge workflow |
packages/batch-submitter/src/batch-submitter/tx-batch-submitter.ts
Outdated
Show resolved
Hide resolved
3fc6772 to
41ea3ad
Compare
| [batch, startBlock] | ||
| ) | ||
| const batchSizeInBytes = remove0x(tx).length / 2 | ||
| const batchSizeInBytes = remove0x(calldata).length / 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is utility in ethers called hexDataLength we could use here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally everything was Buffers instead of strings or even uint8 arrays so that we didn't have to deal with constant prefix slicing
| const APPEND_SEQUENCER_BATCH_METHOD_ID = keccak256( | ||
| Buffer.from('appendSequencerBatch()') | ||
| ).slice(2, 10) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also can do
const { Interface, Fragment } = ethers.utils;
Interface.getSighash(Fragment.from('function appendSequencerBatch()'));| const getEncodedCalldata = (batch: AppendSequencerBatchParams): string => { | ||
| const methodId = APPEND_SEQUENCER_BATCH_METHOD_ID | ||
| const calldata = encodeAppendSequencerBatch(batch) | ||
| return '0x' + remove0x(methodId) + remove0x(calldata) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ethers.utils.hexConcat(methodId, calldata)
|
|
||
| const receipt = await batchSubmitter.submitNextBatch() | ||
| expect(sequencer.getGasPrice).to.have.been.calledOnce | ||
| expect(sequencer.getGasPrice).to.have.been.calledTwice |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does it call twice now?
| expect(called.sendTransaction).to.be.true | ||
| expect(called.waitForTransaction).to.be.true | ||
| expect(called.beforeSendTransaction).to.be.true | ||
| expect(called.onTransactionResponse).to.be.true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
You could also do more checks here on the inputs; e.g., is the expected nonce present
|
We need to merge this |
What can we do to help get this over the line? |
style: address pr feedback
41ea3ad to
e2985bf
Compare
Description
We have observed that when a transaction is stuck in the mempool, the replacement transactions fail to be applied and would return
UNPREDICTABLE_GAS_LIMITwith the error that the starting index in the batch is incorrect.The reason the starting index was seemingly incorrect was because
eth_estimateGashasblockTag=pendingby default.That meant that when we attempted to resubmit our transaction, while estimating gas, it would attempt to reapply the same batch on top of the pending transaction. We explicitly do not want this behavior.
This PR changes the batch submitter submission logic in such a way that before submitting the transaction it populates the transaction fields including the
gasLimit. This way we explicitly specify agasLimit, we don't callestimateGas, and the transaction resubmission can occur.Fixes OP-834