diff --git a/yarn-project/bot/src/base_bot.ts b/yarn-project/bot/src/base_bot.ts index 29e440149b81..3b68352b8a65 100644 --- a/yarn-project/bot/src/base_bot.ts +++ b/yarn-project/bot/src/base_bot.ts @@ -3,6 +3,8 @@ import { FeeJuicePaymentMethod, type SendMethodOptions, SentTx, + TxHash, + TxReceipt, type Wallet, createLogger, waitForProven, @@ -20,7 +22,7 @@ export abstract class BaseBot { protected constructor(public readonly pxe: PXE, public readonly wallet: Wallet, public config: BotConfig) {} - public async run() { + public async run(): Promise { this.attempts++; const logCtx = { runId: Date.now() * 1000 + Math.floor(Math.random() * 1000) }; const { followChain, txMinedWaitSeconds } = this.config; @@ -32,7 +34,7 @@ export abstract class BaseBot { if (followChain === 'NONE') { this.log.info(`Transaction ${txHash} sent, not waiting for it to be mined`); - return; + return txHash; } this.log.verbose( @@ -50,6 +52,7 @@ export abstract class BaseBot { `Tx #${this.attempts} ${receipt.txHash} successfully mined in block ${receipt.blockNumber} (stats: ${this.successes}/${this.attempts} success)`, logCtx, ); + return receipt; } protected abstract createAndSendTx(logCtx: object): Promise; diff --git a/yarn-project/end-to-end/bootstrap.sh b/yarn-project/end-to-end/bootstrap.sh index 1996eddd802d..a4e6221f3142 100755 --- a/yarn-project/end-to-end/bootstrap.sh +++ b/yarn-project/end-to-end/bootstrap.sh @@ -113,6 +113,9 @@ function test_cmds { echo "$prefix simple e2e_token_contract/transfer_to_public" echo "$prefix simple e2e_token_contract/transfer.test" + # other + echo "$prefix simple e2e_sequencer_config" + # circuit_recorder sub-tests echo "$prefix simple e2e_circuit_recorder" diff --git a/yarn-project/end-to-end/src/e2e_bot.test.ts b/yarn-project/end-to-end/src/e2e_bot.test.ts index a8b04dfb7d92..c9afaa2fe827 100644 --- a/yarn-project/end-to-end/src/e2e_bot.test.ts +++ b/yarn-project/end-to-end/src/e2e_bot.test.ts @@ -12,7 +12,9 @@ describe('e2e_bot', () => { beforeAll(async () => { const initialFundedAccounts = await getInitialTestAccounts(); - ({ teardown, pxe } = await setup(1, { initialFundedAccounts })); + ({ teardown, pxe } = await setup(1, { + initialFundedAccounts, + })); }); afterAll(() => teardown()); @@ -85,7 +87,7 @@ describe('e2e_bot', () => { it('swaps tokens from the bot', async () => { const balancesBefore = await bot.getBalances(); - await expect(bot.run()).resolves.toBeUndefined(); + await expect(bot.run()).resolves.toBeDefined(); const balancesAfter = await bot.getBalances(); expect(balancesAfter.senderPrivate.token0).toBeLessThan(balancesBefore.senderPrivate.token0); expect(balancesAfter.senderPrivate.token1).toBeGreaterThan(balancesBefore.senderPrivate.token1); diff --git a/yarn-project/end-to-end/src/e2e_sequencer_config.test.ts b/yarn-project/end-to-end/src/e2e_sequencer_config.test.ts new file mode 100644 index 000000000000..96e9dc52efc2 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_sequencer_config.test.ts @@ -0,0 +1,113 @@ +import { getInitialTestAccounts } from '@aztec/accounts/testing'; +import type { PXE, TxReceipt } from '@aztec/aztec.js'; +import { Bot, type BotConfig, getBotDefaultConfig } from '@aztec/bot'; +import type { Logger } from '@aztec/foundation/log'; +import type { SequencerClient } from '@aztec/sequencer-client'; + +import { jest } from '@jest/globals'; +import 'jest-extended'; + +import { setup } from './fixtures/utils.js'; + +describe('e2e_sequencer_config', () => { + jest.setTimeout(20 * 60 * 1000); // 20 minutes + + let teardown: () => Promise; + let sequencer: SequencerClient | undefined; + let config: BotConfig; + let bot: Bot; + let pxe: PXE; + let logger: Logger; + + afterEach(() => { + jest.restoreAllMocks(); + }); + + describe('bad config', () => { + it('fails to create sequencer if maxL2BlockGas is less than manaTarget', async () => { + const manaTarget = 21e18; + await expect( + setup(1, { + manaTarget: BigInt(manaTarget), + // The max is defined as 2x the manaTarget + maxL2BlockGas: manaTarget * 3, + }), + ).rejects.toThrow(/provided maxL2BlockGas of \d+ is greater than the maximum allowed by the L1 \(\d+\)/); + }); + }); + + describe('Sequencer config', () => { + const manaTarget = 21e18; + beforeAll(async () => { + const initialFundedAccounts = await getInitialTestAccounts(); + ({ teardown, sequencer, pxe, logger } = await setup(1, { + maxL2BlockGas: manaTarget * 2, + manaTarget: BigInt(manaTarget), + initialFundedAccounts, + })); + config = { + ...getBotDefaultConfig(), + followChain: 'PENDING', + ammTxs: false, + txMinedWaitSeconds: 12, + }; + bot = await Bot.create(config, { pxe }); + }); + + afterAll(() => teardown()); + + it('properly sets config', () => { + if (!sequencer) { + throw new Error('Sequencer not found'); + } + expect(sequencer.maxL2BlockGas).toBe(manaTarget * 2); + }); + + it('respects maxL2BlockGas', async () => { + await sequencer!.updateSequencerConfig({ + maxTxsPerBlock: 1, + minTxsPerBlock: 0, + }); + sequencer!.flush(); + + // Run a tx to get the total mana used + const receipt: TxReceipt = (await bot.run()) as TxReceipt; + expect(receipt).toBeDefined(); + expect(receipt.status).toBe('success'); + const block = await pxe.getBlock(receipt.blockNumber!); + expect(block).toBeDefined(); + const totalManaUsed = block?.header.totalManaUsed!.toBigInt(); + + logger.info(`Total mana used: ${totalManaUsed}`); + expect(totalManaUsed).toBeGreaterThan(0n); + bot.updateConfig({ + l2GasLimit: Number(totalManaUsed), + daGasLimit: Number(totalManaUsed), + }); + + // Set the maxL2BlockGas to the total mana used + await sequencer!.updateSequencerConfig({ + maxL2BlockGas: Number(totalManaUsed), + }); + + // Flush the sequencer to make sure the new config is applied to the next tx + sequencer!.flush(); + + // Run a tx and expect it to succeed + const receipt2: TxReceipt = (await bot.run()) as TxReceipt; + expect(receipt2).toBeDefined(); + expect(receipt2.status).toBe('success'); + + // Set the maxL2BlockGas to the total mana used - 1 + await sequencer!.updateSequencerConfig({ + maxL2BlockGas: Number(totalManaUsed) - 1, + }); + + // Flush the sequencer to make sure the new config is applied to the next tx + sequencer!.flush(); + + // Try to run a tx and expect it to fail + await expect(bot.run()).rejects.toThrow(/Timeout awaiting isMined/); + }); + }); +}); diff --git a/yarn-project/ethereum/src/config.ts b/yarn-project/ethereum/src/config.ts index a519f426c949..35c6772a8e40 100644 --- a/yarn-project/ethereum/src/config.ts +++ b/yarn-project/ethereum/src/config.ts @@ -45,7 +45,7 @@ export const DefaultL1ContractsConfig = { slashingRoundSize: 10, governanceProposerQuorum: 6, governanceProposerRoundSize: 10, - manaTarget: BigInt(100e6), + manaTarget: BigInt(1e10), provingCostPerMana: BigInt(100), } satisfies L1ContractsConfig; diff --git a/yarn-project/sequencer-client/src/client/sequencer-client.ts b/yarn-project/sequencer-client/src/client/sequencer-client.ts index b3b6672d1957..6e487a7c55df 100644 --- a/yarn-project/sequencer-client/src/client/sequencer-client.ts +++ b/yarn-project/sequencer-client/src/client/sequencer-client.ts @@ -141,6 +141,14 @@ export class SequencerClient { const ethereumSlotDuration = config.ethereumSlotDuration; + const rollupManaLimit = await rollupContract.getManaLimit(); + const sequencerManaLimit = config.maxL2BlockGas ?? Number(rollupManaLimit); + if (sequencerManaLimit > Number(rollupManaLimit)) { + throw new Error( + `provided maxL2BlockGas of ${sequencerManaLimit} is greater than the maximum allowed by the L1 (${rollupManaLimit})`, + ); + } + // When running in anvil, assume we can post a tx up until the very last second of an L1 slot. // Otherwise, assume we must have broadcasted the tx before the slot started (we use a default // maxL1TxInclusionTimeIntoSlot of zero) to get the tx into that L1 slot. @@ -170,7 +178,7 @@ export class SequencerClient { contractDataSource, l1Constants, deps.dateProvider, - { ...config, maxL1TxInclusionTimeIntoSlot }, + { ...config, maxL1TxInclusionTimeIntoSlot, maxL2BlockGas: sequencerManaLimit }, telemetryClient, ); await validatorClient?.start(); @@ -220,4 +228,8 @@ export class SequencerClient { get validatorAddress(): EthAddress | undefined { return this.sequencer.getValidatorAddress(); } + + get maxL2BlockGas(): number | undefined { + return this.sequencer.maxL2BlockGas; + } } diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 5a8c100d7028..4d18f17147aa 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -773,4 +773,8 @@ export class Sequencer { get feeRecipient(): AztecAddress { return this._feeRecipient; } + + get maxL2BlockGas(): number | undefined { + return this.config.maxL2BlockGas; + } }