Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ import {
tryStop,
} from '@aztec/stdlib/interfaces/server';
import type { LogFilter, PrivateLog, TxScopedL2Log } from '@aztec/stdlib/logs';
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
import { InboxLeaf, type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
import { P2PClientType } from '@aztec/stdlib/p2p';
import type { Offense, SlashPayloadRound } from '@aztec/stdlib/slashing';
import type { NullifierLeafPreimage, PublicDataTreeLeaf, PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
Expand Down Expand Up @@ -860,13 +860,19 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
return [witness.index, witness.path];
}

public async getL1ToL2MessageBlock(l1ToL2Message: Fr): Promise<number | undefined> {
const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message);
return messageIndex ? InboxLeaf.l2BlockFromIndex(messageIndex) : undefined;
}

/**
* Returns whether an L1 to L2 message is synced by archiver and if it's ready to be included in a block.
* @param l1ToL2Message - The L1 to L2 message to check.
* @returns Whether the message is synced and ready to be included in a block.
*/
public async isL1ToL2MessageSynced(l1ToL2Message: Fr): Promise<boolean> {
return (await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message)) !== undefined;
const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message);
return messageIndex !== undefined;
}

/**
Expand Down
1 change: 1 addition & 0 deletions yarn-project/aztec.js/src/api/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export { waitForPXE } from '../utils/pxe.js';
export { waitForNode, createAztecNodeClient, type AztecNode } from '../utils/node.js';
export { getFeeJuiceBalance } from '../utils/fee_juice.js';
export { readFieldCompressedString } from '../utils/field_compressed_string.js';
export { isL1ToL2MessageReady, waitForL1ToL2MessageReady } from '../utils/cross_chain.js';
8 changes: 8 additions & 0 deletions yarn-project/aztec.js/src/contract/batch_call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ export class BatchCall extends BaseContractInteraction {
super(wallet);
}

/**
* Creates a new instance with no actual calls. Useful for triggering a no-op.
* @param wallet - The wallet to use for sending the batch call.
*/
public static empty(wallet: Wallet) {
return new BatchCall(wallet, []);
}

/**
* Returns an execution request that represents this operation.
* @param options - An optional object containing additional configuration for the request generation.
Expand Down
54 changes: 54 additions & 0 deletions yarn-project/aztec.js/src/utils/cross_chain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { Fr } from '@aztec/foundation/fields';
import { retryUntil } from '@aztec/foundation/retry';

import type { PXE } from '../api/interfaces.js';

/**
* Waits for the L1 to L2 message to be ready to be consumed.
* @param pxe - PXE instance
* @param l1ToL2MessageHash - Hash of the L1 to L2 message
* @param opts - Options
*/
export async function waitForL1ToL2MessageReady(
pxe: Pick<PXE, 'getBlockNumber' | 'getL1ToL2MessageBlock'>,
l1ToL2MessageHash: Fr,
opts: {
/** Timeout for the operation in seconds */ timeoutSeconds: number;
/** True if the message is meant to be consumed from a public function */ forPublicConsumption: boolean;
},
) {
const messageBlockNumber = await pxe.getL1ToL2MessageBlock(l1ToL2MessageHash);
return retryUntil(
() => isL1ToL2MessageReady(pxe, l1ToL2MessageHash, { ...opts, messageBlockNumber }),
`L1 to L2 message ${l1ToL2MessageHash.toString()} ready`,
opts.timeoutSeconds,
1,
);
}

/**
* Returns whether the L1 to L2 message is ready to be consumed.
* @param pxe - PXE instance
* @param l1ToL2MessageHash - Hash of the L1 to L2 message
* @param opts - Options
* @returns True if the message is ready to be consumed, false otherwise
*/
export async function isL1ToL2MessageReady(
pxe: Pick<PXE, 'getBlockNumber' | 'getL1ToL2MessageBlock'>,
l1ToL2MessageHash: Fr,
opts: {
/** True if the message is meant to be consumed from a public function */ forPublicConsumption: boolean;
/** Cached synced block number for the message (will be fetched from PXE otherwise) */ messageBlockNumber?: number;
},
): Promise<boolean> {
const blockNumber = await pxe.getBlockNumber();
const messageBlockNumber = opts.messageBlockNumber ?? (await pxe.getL1ToL2MessageBlock(l1ToL2MessageHash));
if (messageBlockNumber === undefined) {
return false;
}

// Note that public messages can be consumed 1 block earlier, since the sequencer will include the messages
// in the L1 to L2 message tree before executing the txs for the block. In private, however, we need to wait
// until the message is included so we can make use of the membership witness.
return opts.forPublicConsumption ? blockNumber + 1 >= messageBlockNumber : blockNumber >= messageBlockNumber;
}
1 change: 1 addition & 0 deletions yarn-project/aztec/src/testing/anvil_test_watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export class AnvilTestWatcher {
}

setIsMarkingAsProven(isMarkingAsProven: boolean) {
this.logger.warn(`Watcher is now ${isMarkingAsProven ? 'marking' : 'not marking'} blocks as proven`);
this.isMarkingAsProven = isMarkingAsProven;
}

Expand Down
2 changes: 1 addition & 1 deletion yarn-project/bot/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export const botConfigMappings: ConfigMappingsType<BotConfig> = {
l1ToL2MessageTimeoutSeconds: {
env: 'BOT_L1_TO_L2_TIMEOUT_SECONDS',
description: 'How long to wait for L1 to L2 messages to become available on L2',
...numberConfigHelper(60),
...numberConfigHelper(3600),
},
senderPrivateKey: {
env: 'BOT_PRIVATE_KEY',
Expand Down
26 changes: 12 additions & 14 deletions yarn-project/bot/src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
type PXE,
createLogger,
createPXEClient,
retryUntil,
waitForL1ToL2MessageReady,
} from '@aztec/aztec.js';
import { createEthereumChain, createExtendedL1Client } from '@aztec/ethereum';
import { Fr } from '@aztec/foundation/fields';
Expand Down Expand Up @@ -112,8 +112,10 @@ export class BotFactory {
private async setupAccount() {
const privateKey = this.config.senderPrivateKey?.getValue();
if (privateKey) {
this.log.info(`Setting up account with provided private key`);
return await this.setupAccountWithPrivateKey(privateKey);
} else {
this.log.info(`Setting up test account`);
return await this.setupTestAccount();
}
}
Expand Down Expand Up @@ -395,35 +397,31 @@ export class BotFactory {
const mintAmount = await portal.getTokenManager().getMintAmount();
const claim = await portal.bridgeTokensPublic(recipient, mintAmount, true /* mint */);

const isSynced = async () => await this.pxe.isL1ToL2MessageSynced(Fr.fromHexString(claim.messageHash));
await retryUntil(isSynced, `message ${claim.messageHash} sync`, this.config.l1ToL2MessageTimeoutSeconds, 1);
await this.withNoMinTxsPerBlock(() =>
waitForL1ToL2MessageReady(this.pxe, Fr.fromHexString(claim.messageHash), {
timeoutSeconds: this.config.l1ToL2MessageTimeoutSeconds,
forPublicConsumption: false,
}),
);

this.log.info(`Created a claim for ${mintAmount} L1 fee juice to ${recipient}.`, claim);

// Progress by 2 L2 blocks so that the l1ToL2Message added above will be available to use on L2.
await this.advanceL2Block();
await this.advanceL2Block();

return claim;
}

private async withNoMinTxsPerBlock<T>(fn: () => Promise<T>): Promise<T> {
if (!this.nodeAdmin || !this.config.flushSetupTransactions) {
this.log.verbose(`No node admin client or flushing not requested (not setting minTxsPerBlock to 0)`);
return fn();
}
const { minTxsPerBlock } = await this.nodeAdmin.getConfig();
this.log.warn(`Setting sequencer minTxsPerBlock to 0 from ${minTxsPerBlock} to flush setup transactions`);
await this.nodeAdmin.setConfig({ minTxsPerBlock: 0 });
try {
return await fn();
} finally {
this.log.warn(`Restoring sequencer minTxsPerBlock to ${minTxsPerBlock}`);
await this.nodeAdmin.setConfig({ minTxsPerBlock });
}
}

private async advanceL2Block() {
await this.withNoMinTxsPerBlock(async () => {
const initialBlockNumber = await this.node!.getBlockNumber();
await retryUntil(async () => (await this.node!.getBlockNumber()) >= initialBlockNumber + 1);
});
}
}
41 changes: 29 additions & 12 deletions yarn-project/end-to-end/src/e2e_bot.test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import { getInitialTestAccountsData } from '@aztec/accounts/testing';
import type { PXE } from '@aztec/aztec.js';
import { Fr, type PXE } from '@aztec/aztec.js';
import { AmmBot, Bot, type BotConfig, SupportedTokenContracts, getBotDefaultConfig } from '@aztec/bot';
import { AVM_MAX_PROCESSABLE_L2_GAS, MAX_PROCESSABLE_DA_GAS_PER_BLOCK } from '@aztec/constants';
import { SecretValue } from '@aztec/foundation/config';
import { bufferToHex } from '@aztec/foundation/string';

import { setup } from './fixtures/utils.js';
import { type EndToEndContext, getPrivateKeyFromIndex, setup } from './fixtures/utils.js';

describe('e2e_bot', () => {
let pxe: PXE;
let teardown: () => Promise<void>;

let config: BotConfig;
let ctx: EndToEndContext;

beforeAll(async () => {
const initialFundedAccounts = await getInitialTestAccountsData();
({ teardown, pxe } = await setup(1, {
initialFundedAccounts,
}));
ctx = await setup(1, { initialFundedAccounts });
({ teardown, pxe } = ctx);
});

afterAll(() => teardown());
Expand Down Expand Up @@ -59,13 +61,7 @@ describe('e2e_bot', () => {
});

it('sends token from the bot using PrivateToken', async () => {
const easyBot = await Bot.create(
{
...config,
contract: SupportedTokenContracts.PrivateTokenContract,
},
{ pxe },
);
const easyBot = await Bot.create({ ...config, contract: SupportedTokenContracts.PrivateTokenContract }, { pxe });
const { recipient: recipientBefore } = await easyBot.getBalances();

await easyBot.run();
Expand Down Expand Up @@ -105,4 +101,25 @@ describe('e2e_bot', () => {
).toBeTrue();
});
});

describe('setup via bridging funds cross-chain', () => {
beforeAll(() => {
config = {
...getBotDefaultConfig(),
followChain: 'PENDING',
ammTxs: false,
senderPrivateKey: new SecretValue(Fr.random()),
l1PrivateKey: new SecretValue(bufferToHex(getPrivateKeyFromIndex(8)!)),
l1RpcUrls: ctx.config.l1RpcUrls,
flushSetupTransactions: true,
};
});

// See 'can consume L1 to L2 message in %s after inbox drifts away from the rollup'
// in end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts for context on this test.
it('creates bot after inbox drift', async () => {
await ctx.cheatCodes.rollup.advanceInboxInProgress(10);
await Bot.create(config, { pxe, node: ctx.aztecNode, nodeAdmin: ctx.aztecNodeAdmin });
}, 300_000);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { AztecNodeConfig } from '@aztec/aztec-node';
import { AztecAddress, type AztecNode, EthAddress, type Logger, type PXE, createLogger } from '@aztec/aztec.js';
import { CheatCodes } from '@aztec/aztec/testing';
import {
type DeployL1ContractsArgs,
type DeployL1ContractsReturnType,
type ExtendedViemWalletClient,
createExtendedL1Client,
Expand All @@ -23,6 +24,7 @@ import {
deployAccounts,
publicDeployAccounts,
} from '../fixtures/snapshot_manager.js';
import type { SetupOptions } from '../fixtures/utils.js';
import { CrossChainTestHarness } from '../shared/cross_chain_test_harness.js';

const { E2E_DATA_PATH: dataPath } = process.env;
Expand All @@ -34,6 +36,7 @@ export class CrossChainMessagingTest {
pxe!: PXE;
aztecNodeConfig!: AztecNodeConfig;
aztecNodeAdmin!: AztecNodeAdmin;
ctx!: SubsystemsContext;

l1Client!: ExtendedViemWalletClient | undefined;

Expand All @@ -52,23 +55,26 @@ export class CrossChainMessagingTest {

deployL1ContractsValues!: DeployL1ContractsReturnType;

constructor(testName: string) {
constructor(testName: string, opts: SetupOptions = {}, deployL1ContractsArgs: Partial<DeployL1ContractsArgs> = {}) {
this.logger = createLogger(`e2e:e2e_cross_chain_messaging:${testName}`);
this.snapshotManager = createSnapshotManager(`e2e_cross_chain_messaging/${testName}`, dataPath);
this.snapshotManager = createSnapshotManager(`e2e_cross_chain_messaging/${testName}`, dataPath, opts, {
initialValidators: [],
...deployL1ContractsArgs,
});
}

async assumeProven() {
await this.cheatCodes.rollup.markAsProven();
}

async setup() {
const { aztecNode, pxe, aztecNodeConfig, deployL1ContractsValues } = await this.snapshotManager.setup();
this.aztecNode = aztecNode;
this.pxe = pxe;
this.aztecNodeConfig = aztecNodeConfig;
this.ctx = await this.snapshotManager.setup();
this.aztecNode = this.ctx.aztecNode;
this.pxe = this.ctx.pxe;
this.aztecNodeConfig = this.ctx.aztecNodeConfig;
this.cheatCodes = await CheatCodes.create(this.aztecNodeConfig.l1RpcUrls, this.pxe);
this.deployL1ContractsValues = deployL1ContractsValues;
this.aztecNodeAdmin = aztecNode;
this.deployL1ContractsValues = this.ctx.deployL1ContractsValues;
this.aztecNodeAdmin = this.ctx.aztecNode;
}

snapshot = <T>(
Expand Down
Loading
Loading