diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index 26199d2f93e5..55ce6beeb8c6 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -7,7 +7,7 @@ use crate::{ oracle::{ key_validation_request::get_key_validation_request, arguments, returns, call_private_function::call_private_function_internal, header::get_header_at, - logs::{emit_encrypted_log, emit_encrypted_note_log, compute_encrypted_log}, + logs::{emit_encrypted_log, emit_encrypted_note_log, compute_encrypted_note_log, compute_encrypted_log}, logs_traits::{LensForEncryptedLog, ToBytesForUnencryptedLog}, enqueue_public_function_call::{ enqueue_public_function_call_internal, set_public_teardown_function_call_internal, @@ -338,7 +338,7 @@ impl PrivateContext { let counter = self.next_counter(); // TODO(#1139 | #6408): perform encryption in the circuit - let encrypted_log: [u8; M] = compute_encrypted_log(contract_address, storage_slot, note_type_id, ivpk_m, preimage); + let encrypted_log: [u8; M] = compute_encrypted_note_log(contract_address, storage_slot, note_type_id, ivpk_m, preimage); emit_encrypted_note_log(note_hash, encrypted_log, counter); // Current unoptimized size of the encrypted log diff --git a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr index 148b12a79b59..d5eebb4ad42b 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr @@ -19,13 +19,33 @@ unconstrained pub fn emit_encrypted_log(encrypted_note: [u8; M], counter: u32 emit_encrypted_log_oracle(encrypted_note, counter) } +// = 480 + 32 * N bytes +#[oracle(computeEncryptedNoteLog)] +fn compute_encrypted_note_log_oracle( + _contract_address: AztecAddress, + _storage_slot: Field, + _note_type_id: Field, + _encryption_pub_key: GrumpkinPoint, + _preimage: [Field; N] +) -> [u8; M] {} + +unconstrained pub fn compute_encrypted_note_log( + contract_address: AztecAddress, + storage_slot: Field, + note_type_id: Field, + ivpk_m: GrumpkinPoint, + preimage: [Field; N] +) -> [u8; M] { + compute_encrypted_note_log_oracle(contract_address, storage_slot, note_type_id, ivpk_m, preimage) +} + // = 480 + 32 * N bytes #[oracle(computeEncryptedLog)] fn compute_encrypted_log_oracle( _contract_address: AztecAddress, _storage_slot: Field, _note_type_id: Field, - _encryption_pub_key: GrumpkinPoint, + _ivpk_m: GrumpkinPoint, _preimage: [Field; N] ) -> [u8; M] {} diff --git a/noir-projects/noir-contracts/Nargo.toml b/noir-projects/noir-contracts/Nargo.toml index 12fbe2958744..661bcf000026 100644 --- a/noir-projects/noir-contracts/Nargo.toml +++ b/noir-projects/noir-contracts/Nargo.toml @@ -35,7 +35,7 @@ members = [ "contracts/schnorr_single_key_account_contract", "contracts/stateful_test_contract", "contracts/test_contract", - "contracts/token_contract", + "contracts/log_test_contract", "contracts/token_blacklist_contract", "contracts/token_bridge_contract", "contracts/uniswap_contract", diff --git a/noir-projects/noir-contracts/contracts/log_test_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/log_test_contract/Nargo.toml new file mode 100644 index 000000000000..6c05d1bf0d80 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/log_test_contract/Nargo.toml @@ -0,0 +1,10 @@ +[package] +name = "log_test_contract" +authors = [""] +compiler_version = ">=0.25.0" +type = "contract" + +[dependencies] +aztec = { path = "../../../aztec-nr/aztec" } +value_note = { path = "../../../aztec-nr/value-note" } +token_portal_content_hash_lib = { path = "../token_portal_content_hash_lib" } diff --git a/noir-projects/noir-contracts/contracts/log_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/log_test_contract/src/main.nr new file mode 100644 index 000000000000..7cf6a19e4b9e --- /dev/null +++ b/noir-projects/noir-contracts/contracts/log_test_contract/src/main.nr @@ -0,0 +1,53 @@ +contract LogTest { + + use dep::aztec::prelude::{ + AztecAddress, EthAddress, FunctionSelector, NoteHeader, NoteGetterOptions, NoteViewerOptions, + PrivateContext, PrivateImmutable, PrivateSet, SharedImmutable + }; + + use dep::aztec::protocol_types::{ + abis::private_circuit_public_inputs::PrivateCircuitPublicInputs, + constants::{MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, CANONICAL_KEY_REGISTRY_ADDRESS}, + traits::{Serialize, ToField, FromField}, grumpkin_point::GrumpkinPoint, + grumpkin_private_key::GrumpkinPrivateKey + }; + + use dep::aztec::encrypted_logs::header::EncryptedLogHeader; + use dep::aztec::encrypted_logs::incoming_body::EncryptedLogIncomingBody; + use dep::aztec::encrypted_logs::outgoing_body::EncryptedLogOutgoingBody; + + use dep::aztec::note::constants::MAX_NOTES_PER_PAGE; + + use dep::aztec::state_vars::{shared_mutable::SharedMutablePrivateGetter, map::derive_storage_slot_in_map}; + + use dep::aztec::{ + keys::getters::{get_npk_m, get_ivpk_m, get_npk_m_hash}, + context::inputs::private_context_inputs::PrivateContextInputs, + hash::{pedersen_hash, compute_secret_hash, ArgsHasher}, + note::{ + lifecycle::{create_note, destroy_note}, note_getter::{get_notes, view_notes}, + note_getter_options::NoteStatus + }, + deploy::deploy_contract as aztec_deploy_contract, + oracle::{encryption::aes128_encrypt, unsafe_rand::unsafe_rand} + }; + use dep::token_portal_content_hash_lib::{get_mint_private_content_hash, get_mint_public_content_hash}; + use dep::value_note::value_note::ValueNote; + + #[aztec(event)] + struct ExampleEvent { + value: Field, + } + + #[aztec(storage)] + struct Storage { + example_set: PrivateSet, + } + + #[aztec(private)] + fn emit_encrypted_log() { + let msg_sender_ivpk_m = get_ivpk_m(&mut context, context.msg_sender()); + + context.encrypt_and_emit_log(context.this_address(), 0, 0, msg_sender_ivpk_m, [1, 2, 3]); + } +} diff --git a/yarn-project/end-to-end/src/e2e_logs.test.ts b/yarn-project/end-to-end/src/e2e_logs.test.ts new file mode 100644 index 000000000000..b16461682849 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_logs.test.ts @@ -0,0 +1,57 @@ +import { createAccounts } from '@aztec/accounts/testing'; +import { type AccountWallet, AztecAddress, type AztecNode, Fr, type L2Block, type PXE, LogType, EncryptedL2BlockL2Logs } from '@aztec/aztec.js'; +import { + CompleteAddress, + GeneratorIndex, + INITIAL_L2_BLOCK_NUM, + Point, + PublicKeys, + computeAppNullifierSecretKey, + deriveMasterNullifierSecretKey, +} from '@aztec/circuits.js'; +import { siloNullifier } from '@aztec/circuits.js/hash'; +import { poseidon2Hash } from '@aztec/foundation/crypto'; +import { KeyRegistryContract, LogTestContract, TestContract } from '@aztec/noir-contracts.js'; +import { getCanonicalKeyRegistryAddress } from '@aztec/protocol-contracts/key-registry'; + +import { jest } from '@jest/globals'; + +import { publicDeployAccounts, setup } from './fixtures/utils.js'; + +const TIMEOUT = 120_000; + +const SHARED_MUTABLE_DELAY = 5; + +describe('Logs', () => { + let pxe: PXE; + let aztecNode: AztecNode; + let logTestContract: LogTestContract; + jest.setTimeout(TIMEOUT); + + let wallets: AccountWallet[]; + + let teardown: () => Promise; + + const account = CompleteAddress.random(); + + beforeAll(async () => { + ({ aztecNode, teardown, pxe, wallets } = await setup(2)); + + await publicDeployAccounts(wallets[0], wallets.slice(0, 2)); + + logTestContract = await LogTestContract.deploy(wallets[0]).send().deployed(); + }); + + afterAll(() => teardown()); + + describe('emits an encrypted log', () => { + it('works', async () => { + const res = await logTestContract.methods.emit_encrypted_log().send().wait(); + + const encryptedLogs = await aztecNode.getLogs(res.blockNumber!, 1, LogType.ENCRYPTED); + const unrolledLogs = EncryptedL2BlockL2Logs.unrollLogs(encryptedLogs); + console.log('Hello'); + console.log(unrolledLogs.length); + }) + }); +}); diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index 8ff5628a12a0..69b00f3e14a1 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -317,6 +317,29 @@ export class Oracle { return bytes; } + computeEncryptedNoteLog( + [contractAddress]: ACVMField[], + [storageSlot]: ACVMField[], + [noteTypeId]: ACVMField[], + [publicKeyX]: ACVMField[], + [publicKeyY]: ACVMField[], + preimage: ACVMField[], + ): ACVMField[] { + const publicKey = new Point(fromACVMField(publicKeyX), fromACVMField(publicKeyY)); + const encLog = this.typedOracle.computeEncryptedNoteLog( + AztecAddress.fromString(contractAddress), + Fr.fromString(storageSlot), + Fr.fromString(noteTypeId), + publicKey, + preimage.map(fromACVMField), + ); + const bytes: ACVMField[] = []; + encLog.forEach(v => { + bytes.push(toACVMField(v)); + }); + return bytes; + } + emitUnencryptedLog( [contractAddress]: ACVMField[], [eventSelector]: ACVMField[], diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 3ad314f41a4e..40ed4896e205 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -194,6 +194,16 @@ export abstract class TypedOracle { throw new OracleMethodNotAvailableError('computeEncryptedLog'); } + computeEncryptedNoteLog( + _contractAddress: AztecAddress, + _storageSlot: Fr, + _noteTypeId: Fr, + _publicKey: PublicKey, + _preimage: Fr[], + ): Buffer { + throw new OracleMethodNotAvailableError('computeEncryptedNoteLog'); + } + emitUnencryptedLog(_log: UnencryptedL2Log, _counter: number): void { throw new OracleMethodNotAvailableError('emitUnencryptedLog'); } diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 148cddf078eb..db259ae6babb 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -375,6 +375,34 @@ export class ClientExecutionContext extends ViewDataOracle { this.noteCache.addNewLog(encryptedLog, noteHash); } + /** + * Encrypt a note + * @param contractAddress - The contract address of the note. + * @param storageSlot - The storage slot the note is at. + * @param noteTypeId - The type ID of the note. + * @param ivpk - The master incoming viewing public key. + * @param preimage - The note preimage. + */ + public override computeEncryptedNoteLog( + contractAddress: AztecAddress, + storageSlot: Fr, + noteTypeId: Fr, + ivpk: Point, + preimage: Fr[], + ) { + const note = new Note(preimage); + const l1NotePayload = new L1NotePayload(note, contractAddress, storageSlot, noteTypeId); + const taggedNote = new TaggedNote(l1NotePayload); + + const ephSk = GrumpkinScalar.random(); + + // @todo Issue(#6410) Right now we are completely ignoring the outgoing log. Just drawing random data. + const ovsk = GrumpkinScalar.random(); + const recipient = AztecAddress.random(); + + return taggedNote.encrypt(ephSk, recipient, ivpk, ovsk); + } + /** * Encrypt a note * @param contractAddress - The contract address of the note.