From fbbc739b85131b81eb9e18c45c647bebc905cccd Mon Sep 17 00:00:00 2001 From: sklppy88 Date: Thu, 15 Aug 2024 10:05:52 +0000 Subject: [PATCH] init --- .../aztec.js/src/wallet/base_wallet.ts | 9 +++++ .../circuit-types/src/interfaces/pxe.ts | 16 ++++++++ .../src/messaging/l1_to_l2_message.ts | 37 +++++++++++++++++++ .../cli-wallet/src/cmds/bridge_fee_juice.ts | 1 + .../cmds/pxe/get_l1_to_l2_message_witness.ts | 25 +++++++++++++ yarn-project/cli/src/cmds/pxe/index.ts | 13 +++++++ .../pxe/src/pxe_service/pxe_service.ts | 13 ++++++- .../pxe/src/simulator_oracle/index.ts | 28 ++++---------- 8 files changed, 120 insertions(+), 22 deletions(-) create mode 100644 yarn-project/cli/src/cmds/pxe/get_l1_to_l2_message_witness.ts diff --git a/yarn-project/aztec.js/src/wallet/base_wallet.ts b/yarn-project/aztec.js/src/wallet/base_wallet.ts index f170202c7196..df7fed113ebd 100644 --- a/yarn-project/aztec.js/src/wallet/base_wallet.ts +++ b/yarn-project/aztec.js/src/wallet/base_wallet.ts @@ -10,6 +10,7 @@ import { type OutgoingNotesFilter, type PXE, type PXEInfo, + type SiblingPath, type SimulatedTx, type SyncStatus, type Tx, @@ -25,6 +26,7 @@ import { type CompleteAddress, type Fq, type Fr, + type L1_TO_L2_MSG_TREE_HEIGHT, type PartialAddress, type Point, } from '@aztec/circuits.js'; @@ -206,4 +208,11 @@ export abstract class BaseWallet implements Wallet { ) { return this.pxe.getEvents(type, eventMetadata, from, limit, vpks); } + public getL1ToL2MembershipWitness( + contractAddress: AztecAddress, + messageHash: Fr, + secret: Fr, + ): Promise<[bigint, SiblingPath]> { + return this.pxe.getL1ToL2MembershipWitness(contractAddress, messageHash, secret); + } } diff --git a/yarn-project/circuit-types/src/interfaces/pxe.ts b/yarn-project/circuit-types/src/interfaces/pxe.ts index bcff339a02fb..37f6e0cbfa5e 100644 --- a/yarn-project/circuit-types/src/interfaces/pxe.ts +++ b/yarn-project/circuit-types/src/interfaces/pxe.ts @@ -3,6 +3,7 @@ import { type CompleteAddress, type Fq, type Fr, + type L1_TO_L2_MSG_TREE_HEIGHT, type PartialAddress, type Point, } from '@aztec/circuits.js'; @@ -19,6 +20,7 @@ import { type L2Block } from '../l2_block.js'; import { type GetUnencryptedLogsResponse, type L1EventPayload, type LogFilter } from '../logs/index.js'; import { type IncomingNotesFilter } from '../notes/incoming_notes_filter.js'; import { type ExtendedNote, type OutgoingNotesFilter, type UniqueNote } from '../notes/index.js'; +import { type SiblingPath } from '../sibling_path/sibling_path.js'; import { type NoteProcessorStats } from '../stats/stats.js'; import { type SimulatedTx, type Tx, type TxHash, type TxReceipt } from '../tx/index.js'; import { type TxEffect } from '../tx_effect.js'; @@ -239,6 +241,20 @@ export interface PXE { */ getIncomingNotes(filter: IncomingNotesFilter): Promise; + /** + * Fetches an L1 to L2 message from the node. + * @param contractAddress - Address of a contract by which the message was emitted. + * @param messageHash - Hash of the message. + * @param secret - Secret used to compute a nullifier. + * @dev Contract address and secret are only used to compute the nullifier to get non-nullified messages + * @returns The l1 to l2 membership witness (index of message in the tree and sibling path). + */ + getL1ToL2MembershipWitness( + contractAddress: AztecAddress, + messageHash: Fr, + secret: Fr, + ): Promise<[bigint, SiblingPath]>; + /** * Gets outgoing notes of accounts registered in this PXE based on the provided filter. * @param filter - The filter to apply to the notes. diff --git a/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts b/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts index 79980b07da4f..56e457f62f1b 100644 --- a/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts +++ b/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts @@ -1,7 +1,13 @@ +import { type L1_TO_L2_MSG_TREE_HEIGHT } from '@aztec/circuits.js'; +import { computeL1ToL2MessageNullifier } from '@aztec/circuits.js/hash'; +import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { sha256ToField } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { type AztecNode } from '../interfaces/aztec-node.js'; +import { MerkleTreeId } from '../merkle_tree_id.js'; +import { type SiblingPath } from '../sibling_path/index.js'; import { L1Actor } from './l1_actor.js'; import { L2Actor } from './l2_actor.js'; @@ -70,3 +76,34 @@ export class L1ToL2Message { return new L1ToL2Message(L1Actor.random(), L2Actor.random(), Fr.random(), Fr.random()); } } + +// This functionality is not on the node because we do not want to pass the node the secret, and give the node the ability to derive a valid nullifer for an L1 to L2 message. +export async function getNonNullifiedL1ToL2MessageWitness( + node: AztecNode, + contractAddress: AztecAddress, + messageHash: Fr, + secret: Fr, +): Promise<[bigint, SiblingPath]> { + let nullifierIndex: bigint | undefined; + let messageIndex = 0n; + let startIndex = 0n; + let siblingPath: SiblingPath; + + // We iterate over messages until we find one whose nullifier is not in the nullifier tree --> we need to check + // for nullifiers because messages can have duplicates. + do { + const response = await node.getL1ToL2MessageMembershipWitness('latest', messageHash, startIndex); + if (!response) { + throw new Error(`No non-nullified L1 to L2 message found for message hash ${messageHash.toString()}`); + } + [messageIndex, siblingPath] = response; + + const messageNullifier = computeL1ToL2MessageNullifier(contractAddress, messageHash, secret, messageIndex); + + nullifierIndex = await node.findLeafIndex('latest', MerkleTreeId.NULLIFIER_TREE, messageNullifier); + + startIndex = messageIndex + 1n; + } while (nullifierIndex !== undefined); + + return [messageIndex, siblingPath]; +} diff --git a/yarn-project/cli-wallet/src/cmds/bridge_fee_juice.ts b/yarn-project/cli-wallet/src/cmds/bridge_fee_juice.ts index 436b4f6a2188..c43e01885df4 100644 --- a/yarn-project/cli-wallet/src/cmds/bridge_fee_juice.ts +++ b/yarn-project/cli-wallet/src/cmds/bridge_fee_juice.ts @@ -42,6 +42,7 @@ export async function bridgeL1FeeJuice( } log(`claimAmount=${claimAmount},claimSecret=${claimSecret}\n`); log(`Note: You need to wait for two L2 blocks before pulling them from the L2 side`); + log(`This command will now continually poll every minute for the inclusion of the newly created L1 to L2 message`); } return claimSecret; } diff --git a/yarn-project/cli/src/cmds/pxe/get_l1_to_l2_message_witness.ts b/yarn-project/cli/src/cmds/pxe/get_l1_to_l2_message_witness.ts new file mode 100644 index 000000000000..aff3b463507c --- /dev/null +++ b/yarn-project/cli/src/cmds/pxe/get_l1_to_l2_message_witness.ts @@ -0,0 +1,25 @@ +import { type AztecAddress, type Fr, createCompatibleClient } from '@aztec/aztec.js'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; + +export async function getL1ToL2MessageWitness( + rpcUrl: string, + contractAddress: AztecAddress, + messageHash: Fr, + secret: Fr, + debugLogger: DebugLogger, + log: LogFn, +) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + const messageWitness = await client.getL1ToL2MembershipWitness(contractAddress, messageHash, secret); + + log( + messageWitness === undefined + ? ` + L1 to L2 Message not found. + ` + : ` + L1 to L2 message index: ${messageWitness[0]} + L1 to L2 message sibling path: ${messageWitness[1]} + `, + ); +} diff --git a/yarn-project/cli/src/cmds/pxe/index.ts b/yarn-project/cli/src/cmds/pxe/index.ts index b712ff6f2239..b7c69af8098d 100644 --- a/yarn-project/cli/src/cmds/pxe/index.ts +++ b/yarn-project/cli/src/cmds/pxe/index.ts @@ -7,6 +7,7 @@ import { logJson, parseAztecAddress, parseEthereumAddress, + parseField, parseFieldFromHexString, parseOptionalAztecAddress, parseOptionalInteger, @@ -165,6 +166,18 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: DebugL await blockNumber(options.rpcUrl, debugLogger, log); }); + program + .command('get-l1-to-l2-message-witness') + .description('Gets a L1 to L2 message witness.') + .requiredOption('-ca, --contract-address
', 'Aztec address of the contract.', parseAztecAddress) + .requiredOption('--message-hash ', 'The L1 to L2 message hash.', parseField) + .requiredOption('-secret ', 'The secret used to claim the L1 to L2 message', parseField) + .addOption(pxeOption) + .action(async ({ contractAddress, messageHash, secret, rpcUrl }) => { + const { getL1ToL2MessageWitness } = await import('./get_l1_to_l2_message_witness.js'); + await getL1ToL2MessageWitness(rpcUrl, contractAddress, messageHash, secret, debugLogger, log); + }); + program .command('get-node-info') .description('Gets the information of an aztec node at a URL.') diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 15a1fc143f70..7ccabd64929f 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -17,6 +17,7 @@ import { type PXE, type PXEInfo, type PrivateKernelProver, + type SiblingPath, SimulatedTx, SimulationError, TaggedLog, @@ -27,16 +28,18 @@ import { type TxReceipt, UnencryptedTxL2Logs, UniqueNote, + getNonNullifiedL1ToL2MessageWitness, isNoirCallStackUnresolved, } from '@aztec/circuit-types'; import { AztecAddress, type CompleteAddress, + type L1_TO_L2_MSG_TREE_HEIGHT, type PartialAddress, computeContractClassId, getContractClassFromArtifact, } from '@aztec/circuits.js'; -import { computeNoteHashNonce, siloNullifier } from '@aztec/circuits.js/hash'; +import { computeL1ToL2MessageNullifier, computeNoteHashNonce, siloNullifier } from '@aztec/circuits.js/hash'; import { type ContractArtifact, type DecodedReturn, @@ -355,6 +358,14 @@ export class PXEService implements PXE { return Promise.all(extendedNotes); } + public async getL1ToL2MembershipWitness( + contractAddress: AztecAddress, + messageHash: Fr, + secret: Fr, + ): Promise<[bigint, SiblingPath]> { + return await getNonNullifiedL1ToL2MessageWitness(this.node, contractAddress, messageHash, secret); + } + public async addNote(note: ExtendedNote, scope?: AztecAddress) { const owner = await this.db.getCompleteAddress(note.owner); if (!owner) { diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 5798a955888e..7e7dc09ab630 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -5,7 +5,7 @@ import { type NoteStatus, type NullifierMembershipWitness, type PublicDataWitness, - type SiblingPath, + getNonNullifiedL1ToL2MessageWitness, } from '@aztec/circuit-types'; import { type AztecAddress, @@ -16,7 +16,6 @@ import { type KeyValidationRequest, type L1_TO_L2_MSG_TREE_HEIGHT, } from '@aztec/circuits.js'; -import { computeL1ToL2MessageNullifier } from '@aztec/circuits.js/hash'; import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi'; import { createDebugLogger } from '@aztec/foundation/log'; import { type KeyStore } from '@aztec/key-store'; @@ -127,25 +126,12 @@ export class SimulatorOracle implements DBOracle { messageHash: Fr, secret: Fr, ): Promise> { - let nullifierIndex: bigint | undefined; - let messageIndex = 0n; - let startIndex = 0n; - let siblingPath: SiblingPath; - - // We iterate over messages until we find one whose nullifier is not in the nullifier tree --> we need to check - // for nullifiers because messages can have duplicates. - do { - const response = await this.aztecNode.getL1ToL2MessageMembershipWitness('latest', messageHash, startIndex); - if (!response) { - throw new Error(`No non-nullified L1 to L2 message found for message hash ${messageHash.toString()}`); - } - [messageIndex, siblingPath] = response; - - const messageNullifier = computeL1ToL2MessageNullifier(contractAddress, messageHash, secret, messageIndex); - nullifierIndex = await this.getNullifierIndex(messageNullifier); - - startIndex = messageIndex + 1n; - } while (nullifierIndex !== undefined); + const [messageIndex, siblingPath] = await getNonNullifiedL1ToL2MessageWitness( + this.aztecNode, + contractAddress, + messageHash, + secret, + ); // Assuming messageIndex is what you intended to use for the index in MessageLoadOracleInputs return new MessageLoadOracleInputs(messageIndex, siblingPath);