Skip to content
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: use simulation to estimate gas used #11211

Open
wants to merge 63 commits into
base: master
Choose a base branch
from
Open

Conversation

spypsy
Copy link
Member

@spypsy spypsy commented Jan 14, 2025

Relevant to #10991 but not 'Fixes' because anvil doesn't support eth_simulateV1 so we can't yet remove all time-based inputs in L1

Fixes #11341

  • We now try to simulate the 'propose' & 'proposeAndClaim' transactions, using the appropriate timestamp, instead of using hardcoded 12M gas.
  • Also some updates to logging where we
    1. Try to decode errors from our Rollup ABI
    2. truncate large Hexes but still include them in error data so txs can be investigated
  • Update slot duration to 36 for sepolia deployments

Follow-up from this: #11390

@spypsy spypsy marked this pull request as ready for review January 14, 2025 12:09
@spypsy spypsy force-pushed the spy/simulate-l1-txs branch from a3586fb to dc8198a Compare January 17, 2025 15:33
@spypsy spypsy requested a review from spalladino January 20, 2025 17:41
Copy link
Collaborator

@spalladino spalladino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Love to see this API being put to good use. Left a few comments. And yeah, it'd be nice to have a sort-of-unit-test that spins up a reth (instead of an anvil) to test this, as you mentioned today.

yarn-project/archiver/src/archiver/archiver.test.ts Outdated Show resolved Hide resolved
Comment on lines +460 to +461
const { message } = formatViemError(err, abi);
expect(message).toBe('Test_Error(33)');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it

Comment on lines 216 to 217
} else if (gasConfig.gasLimit) {
gasLimit = this.bumpGasLimit(gasConfig.gasLimit, gasConfig);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} else if (gasConfig.gasLimit) {
gasLimit = this.bumpGasLimit(gasConfig.gasLimit, gasConfig);
} else if (gasConfig.gasLimit) {
gasLimit = gasConfig.gasLimit;

I find it confusing that if I set a fixed value, the library then bumps it. I'd expect it to use the exact fixed value I requested. Open to discussion though!

Copy link
Member Author

@spypsy spypsy Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was on the fence on this, I kept fixedGas for this purpose but I might just be making it confusing.
The reason was, I wanted to bump the result of simulateGasUsed in l1-publisher without having a fixed number.
Probably can just extract the gas settings inside l1-publisher though, and multiply it there

}
return receipt;
}
} catch (err) {
if (err instanceof Error && err.message.includes('reverted')) {
throw err;
throw formatViemError(err);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if we pass something that is not a ViemError to formatViemError? I'm worried we may get a non-viem error but still something that contains the string "reverted". But if we're fine sending anything into formatViemError, no worries then!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah probably a good idea to check inside formatViemError and add a test case for it 👍

if (error instanceof BaseError) {
const revertError = error.walk(err => err instanceof ContractFunctionRevertedError);
if (revertError instanceof ContractFunctionRevertedError) {
const errorName = revertError.data?.errorName ?? '';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we extract the error selector here if we don't find the errorName?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh, I did have a case for that where we extract .signature, probably lost in the sea of commits 🤦
Good catch, will add back

@@ -983,7 +987,8 @@ export class L1Publisher {
// we will fail estimation in the case where we are simulating for the
// first ethereum block within our slot (as current time is not in the
// slot yet).
const gasGuesstimate = blobEvaluationGas + L1Publisher.PROPOSE_GAS_GUESS;
// const gasGuesstimate = blobEvaluationGas + L1Publisher.PROPOSE_GAS_GUESS;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we delete this and the comment above?

data,
},
{
time: timestamp + 1n,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a comment why this +1n

// @note we override checkBlob to false since blobs are not part simulate()
stateDiff: [
{
slot: toHex(9n, true),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be nice if we tweaked generate-artifacts.sh in yarn-project so we could extract the storage locations from the artifact output. If that ends up being too much, do you think we could add a unit test or something to check that this slot doesn't change?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could probably just use the forge inspect <CONTRACT> storageLayout --json to populate, then it should be fairly easy to fetch from there. Sounds like a good idea to have.

Its a kinda annoying one though, as ideally the variable itself should not exists 😬.

if (err instanceof MethodNotFoundRpcError || err instanceof MethodNotSupportedRpcError) {
// Node doesn't support simulation, return -1n gas estimate
this.logger?.error('Node does not support simulation API');
return -1n;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd vote for rethrowing instead of using -1, in case we forget to check for that in the caller and end up propagating an invalid value.

@@ -528,17 +528,19 @@ describe('L1Publisher integration', () => {
await expect(publisher.proposeL2Block(block)).resolves.toEqual(false);

// Test for both calls
expect(loggerErrorSpy).toHaveBeenCalledTimes(2);
// NOTE: First error is from the simulate fn, which isn't supported by anvil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The simulate with time overrides?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep

* @param abi - The ABI to use for decoding.
* @returns A FormattedViemError instance.
*/
export function formatViemError(error: any, abi: Abi = RollupAbi): FormattedViemError {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I altered the script that we use to generate the artifacts such that the errors should be included inside the other ABIs. So they are in the rollup one or whetever is being used. But maybe it should not be specifically the RollupAbi that is being checked against here. Mainly if some other contract were used this formatter gets pretty strange.

https://github.com/AztecProtocol/aztec-packages/pull/10697/files

Comment on lines 612 to 613
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
// make time consistency checks break.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not be the case. Also, we should be able to get rid of the validateBlockForSubmission here if the send can actually simulate it.

// @note we override checkBlob to false since blobs are not part simulate()
stateDiff: [
{
slot: toHex(9n, true),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could probably just use the forge inspect <CONTRACT> storageLayout --json to populate, then it should be fairly easy to fetch from there. Sounds like a good idea to have.

Its a kinda annoying one though, as ideally the variable itself should not exists 😬.

],
);

if (simulationResult === -1n) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing an error as mentioner earlier would make more sense to me here.

Copy link
Contributor

Changes to public function bytecode sizes

Generated at commit: 4c2833fa0650f18192cd4572b22d5f2ef6000a15, compared to commit: 30a063a65f95403773d13da0d9a896da45d9608d

🧾 Summary (100% most significant diffs)

Program Bytecode size in bytes (+/-) %
AvmTest::bulk_testing +70 ❌ +0.31%
AvmTest::public_dispatch +59 ❌ +0.09%

Full diff report 👇
Program Bytecode size in bytes (+/-) %
AvmTest::bulk_testing 22,676 (+70) +0.31%
AvmTest::public_dispatch 62,199 (+59) +0.09%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Include arguments in errors
3 participants