Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
sklppy88 committed Aug 15, 2024
1 parent 3d61bdf commit 1ac9c4f
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 27 deletions.
16 changes: 16 additions & 0 deletions yarn-project/circuit-types/src/interfaces/pxe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -239,6 +241,20 @@ export interface PXE {
*/
getIncomingNotes(filter: IncomingNotesFilter): Promise<UniqueNote[]>;

/**
* 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<typeof L1_TO_L2_MSG_TREE_HEIGHT>] | undefined>;

/**
* Gets outgoing notes of accounts registered in this PXE based on the provided filter.
* @param filter - The filter to apply to the notes.
Expand Down
37 changes: 37 additions & 0 deletions yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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<typeof L1_TO_L2_MSG_TREE_HEIGHT>]> {
let nullifierIndex: bigint | undefined;
let messageIndex = 0n;
let startIndex = 0n;
let siblingPath: SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>;

// 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];
}
18 changes: 15 additions & 3 deletions yarn-project/cli-wallet/src/cmds/bridge_fee_juice.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createCompatibleClient } from '@aztec/aztec.js';
import { type AztecAddress } from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/circuits.js';
import { FeeJuicePortalManager, prettyPrintJSON } from '@aztec/cli/utils';
import { createEthereumChain, createL1Clients } from '@aztec/ethereum';
import { type DebugLogger, type LogFn } from '@aztec/foundation/log';
Expand All @@ -26,7 +26,9 @@ export async function bridgeL1FeeJuice(

// Setup portal manager
const portal = await FeeJuicePortalManager.create(client, publicClient, walletClient, debugLogger);
const { secret } = await portal.prepareTokensOnL1(amount, amount, recipient, mint);
const { secret, msgHash } = await portal.prepareTokensOnL1(amount, amount, recipient, mint);

const l2TokenAddress = AztecAddress.fromString(await portal.getPortalContract().read.l2TokenAddress());

if (json) {
const out = {
Expand All @@ -40,8 +42,18 @@ export async function bridgeL1FeeJuice(
} else {
log(`Bridged ${amount} fee juice to L2 portal`);
}
log(`claimAmount=${amount},claimSecret=${secret}\n`);
log(`claimAmount=${amount},claimSecret=${secret},contractAddress=${l2TokenAddress},msgHash=${msgHash}\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`);
}

const recheck = setInterval(async () => {
const l1ToL2MembershipWitness = await client.getL1ToL2MembershipWitness(l2TokenAddress, msgHash, secret);
if (l1ToL2MembershipWitness) {
log(`Successfully retrieved L1 to L2 message. Index: ${l1ToL2MembershipWitness[0]}`);
clearInterval(recheck);
}
}, 60_000);

return secret;
}
25 changes: 25 additions & 0 deletions yarn-project/cli/src/cmds/pxe/get_l1_to_l2_message_witness.ts
Original file line number Diff line number Diff line change
@@ -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]}
`,
);
}
13 changes: 13 additions & 0 deletions yarn-project/cli/src/cmds/pxe/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
logJson,
parseAztecAddress,
parseEthereumAddress,
parseField,
parseFieldFromHexString,
parseOptionalAztecAddress,
parseOptionalInteger,
Expand Down Expand Up @@ -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 <address>', 'Aztec address of the contract.', parseAztecAddress)
.requiredOption('--message-hash <messageHash>', 'The L1 to L2 message hash.', parseField)
.requiredOption('-secret <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.')
Expand Down
10 changes: 9 additions & 1 deletion yarn-project/cli/src/utils/portal_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ abstract class PortalManager {
}

export class FeeJuicePortalManager extends PortalManager {
public getPortalContract() {
return getContract({
address: this.tokenPortalAddress.toString(),
abi: FeeJuicePortalAbi,
client: this.walletClient,
});
}

async bridgeTokens(to: AztecAddress, amount: bigint, secretHash: Fr): Promise<Hex> {
const portal = getContract({
address: this.tokenPortalAddress.toString(),
Expand All @@ -113,7 +121,7 @@ export class FeeJuicePortalManager extends PortalManager {
publicClient: PublicClient<HttpTransport, Chain>,
walletClient: WalletClient<HttpTransport, Chain, Account>,
logger: DebugLogger,
): Promise<PortalManager> {
): Promise<FeeJuicePortalManager> {
const {
l1ContractAddresses: { feeJuiceAddress, feeJuicePortalAddress },
} = await pxe.getNodeInfo();
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/ivc-integration/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
},
{
"path": "../bb-prover"
},
}
],
"include": ["src", "artifacts/*.d.json.ts", "artifacts/**/*.d.json.ts"]
}
13 changes: 12 additions & 1 deletion yarn-project/pxe/src/pxe_service/pxe_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
type PXE,
type PXEInfo,
type PrivateKernelProver,
type SiblingPath,
SimulatedTx,
SimulationError,
TaggedLog,
Expand All @@ -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,
Expand Down Expand Up @@ -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<typeof L1_TO_L2_MSG_TREE_HEIGHT>]> {
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) {
Expand Down
28 changes: 7 additions & 21 deletions yarn-project/pxe/src/simulator_oracle/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
type NoteStatus,
type NullifierMembershipWitness,
type PublicDataWitness,
type SiblingPath,
getNonNullifiedL1ToL2MessageWitness,
} from '@aztec/circuit-types';
import {
type AztecAddress,
Expand All @@ -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';
Expand Down Expand Up @@ -127,25 +126,12 @@ export class SimulatorOracle implements DBOracle {
messageHash: Fr,
secret: Fr,
): Promise<MessageLoadOracleInputs<typeof L1_TO_L2_MSG_TREE_HEIGHT>> {
let nullifierIndex: bigint | undefined;
let messageIndex = 0n;
let startIndex = 0n;
let siblingPath: SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>;

// 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);
Expand Down

0 comments on commit 1ac9c4f

Please sign in to comment.