Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fixed private log size #9585

Merged
merged 8 commits into from
Oct 31, 2024
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
1 change: 1 addition & 0 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ library Constants {
uint256 internal constant FUNCTION_SELECTOR_NUM_BYTES = 4;
uint256 internal constant INITIALIZATION_SLOT_SEPARATOR = 1000000000;
uint256 internal constant INITIAL_L2_BLOCK_NUM = 1;
uint256 internal constant PRIVATE_LOG_SIZE_IN_BYTES = 576;
uint256 internal constant BLOB_SIZE_IN_BYTES = 126976;
uint256 internal constant ETHEREUM_SLOT_DURATION = 12;
uint256 internal constant AZTEC_SLOT_DURATION = 24;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use crate::{
context::PrivateContext, encrypted_logs::payload::compute_private_log_payload,
event::event_interface::EventInterface, keys::getters::get_ovsk_app, oracle::random::random,
};
use dep::protocol_types::{address::AztecAddress, hash::sha256_to_field, public_keys::OvpkM};
use dep::protocol_types::{
address::AztecAddress, constants::PRIVATE_LOG_SIZE_IN_BYTES, hash::sha256_to_field,
public_keys::OvpkM,
};

/// Computes private event log payload and a log hash
fn compute_payload_and_hash<Event, let N: u32>(
Expand All @@ -13,22 +16,20 @@ fn compute_payload_and_hash<Event, let N: u32>(
ovpk: OvpkM,
recipient: AztecAddress,
sender: AztecAddress,
) -> ([u8; 384 + N * 32], Field)
) -> ([u8; PRIVATE_LOG_SIZE_IN_BYTES], Field)
where
Event: EventInterface<N>,
{
let contract_address: AztecAddress = context.this_address();
let plaintext = event.private_to_be_bytes(randomness);

// For event logs we never include public values prefix as there are never any public values
let encrypted_log: [u8; 384 + N * 32] = compute_private_log_payload(
let encrypted_log = compute_private_log_payload(
contract_address,
ovsk_app,
ovpk,
recipient,
sender,
plaintext,
false,
);
let log_hash = sha256_to_field(encrypted_log);
(encrypted_log, log_hash)
Expand All @@ -41,7 +42,7 @@ unconstrained fn compute_payload_and_hash_unconstrained<Event, let N: u32>(
ovpk: OvpkM,
recipient: AztecAddress,
sender: AztecAddress,
) -> ([u8; 384 + N * 32], Field)
) -> ([u8; PRIVATE_LOG_SIZE_IN_BYTES], Field)
where
Event: EventInterface<N>,
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::{
note::{note_emission::NoteEmission, note_interface::NoteInterface},
};
use dep::protocol_types::{
abis::note_hash::NoteHash, address::AztecAddress, hash::sha256_to_field, public_keys::OvpkM,
abis::note_hash::NoteHash, address::AztecAddress, constants::PRIVATE_LOG_SIZE_IN_BYTES,
hash::sha256_to_field, public_keys::OvpkM,
};

/// Computes private note log payload and a log hash
Expand All @@ -16,7 +17,7 @@ fn compute_payload_and_hash<Note, let N: u32>(
ovpk: OvpkM,
recipient: AztecAddress,
sender: AztecAddress,
) -> (u32, [u8; 385 + N * 32], Field)
) -> (u32, [u8; PRIVATE_LOG_SIZE_IN_BYTES], Field)
where
Note: NoteInterface<N>,
{
Expand All @@ -32,15 +33,13 @@ where

let plaintext = note.to_be_bytes(storage_slot);

// For note logs we always include public values prefix
let encrypted_log: [u8; 385 + N * 32] = compute_private_log_payload(
let encrypted_log = compute_private_log_payload(
contract_address,
ovsk_app,
ovpk,
recipient,
sender,
plaintext,
true,
);
let log_hash = sha256_to_field(encrypted_log);

Expand All @@ -53,7 +52,7 @@ unconstrained fn compute_payload_and_hash_unconstrained<Note, let N: u32>(
ovpk: OvpkM,
recipient: AztecAddress,
sender: AztecAddress,
) -> (u32, [u8; 385 + N * 32], Field)
) -> (u32, [u8; PRIVATE_LOG_SIZE_IN_BYTES], Field)
where
Note: NoteInterface<N>,
{
Expand Down
182 changes: 151 additions & 31 deletions noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use dep::protocol_types::{
address::AztecAddress, constants::GENERATOR_INDEX__SYMMETRIC_KEY,
hash::poseidon2_hash_with_separator, point::Point, public_keys::OvpkM, scalar::Scalar,
address::AztecAddress,
constants::{GENERATOR_INDEX__SYMMETRIC_KEY, PRIVATE_LOG_SIZE_IN_BYTES},
hash::poseidon2_hash_with_separator,
point::Point,
public_keys::OvpkM,
scalar::Scalar,
};
use std::{
aes128::aes128_encrypt, embedded_curve_ops::fixed_base_scalar_mul as derive_public_key,
Expand All @@ -14,14 +18,97 @@ use crate::{
};
use protocol_types::public_keys::AddressPoint;

fn compute_private_log_payload<let P: u32, let M: u32>(
pub comptime global PRIVATE_LOG_OVERHEAD_IN_BYTES: u32 = 304;

// 1 byte for storage slot, 1 byte for note type id, allowing 6 bytes for custom note fields.
global MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES: u32 = 8 * 32;

global MAX_PRIVATE_EVENT_LOG_PLAINTEXT_SIZE_IN_BYTES: u32 =
MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES - 32; // Reserve 1 field for address tag.

// PRIVATE_LOG_SIZE_IN_BYTES
// - PRIVATE_LOG_OVERHEAD_IN_BYTES, consisting of:
// - 32 bytes for incoming_tag
// - 32 bytes for eph_pk
// - 48 bytes for incoming_header
// - 48 bytes for outgoing_header
// - 144 bytes for outgoing_body
// - 16 + MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES for incoming_body, consisting of:
// - 1 byte for plaintext length
// - MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES for the actual plaintext and padded random values
// - 15 bytes for AES padding

// Note: Update PRIVATE_LOG_SIZE_IN_BYTES in `constants.nr` if any of the above fields change.
// This value ideally should be set by the protocol, allowing users (or `aztec-nr`) to fit data within the defined size limits.
// Currently, we adjust this value as the structure changes, then update `constants.nr` to match.
// Once the structure is finalized with defined overhead and max note field sizes, this value will be fixed and should remain unaffected by further payload composition changes.

pub fn compute_private_log_payload<let P: u32>(
contract_address: AztecAddress,
ovsk_app: Field,
ovpk: OvpkM,
recipient: AztecAddress,
sender: AztecAddress,
plaintext: [u8; P],
) -> [u8; PRIVATE_LOG_SIZE_IN_BYTES] {
let extended_plaintext: [u8; MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES + 1] =
extend_private_log_plaintext(plaintext);
compute_encrypted_log(
contract_address,
ovsk_app,
ovpk,
recipient,
sender,
extended_plaintext,
)
}

pub fn compute_event_log_payload<let P: u32>(
contract_address: AztecAddress,
ovsk_app: Field,
ovpk: OvpkM,
recipient: AztecAddress,
sender: AztecAddress,
plaintext: [u8; P],
) -> [u8; PRIVATE_LOG_SIZE_IN_BYTES] {
let extended_plaintext: [u8; MAX_PRIVATE_EVENT_LOG_PLAINTEXT_SIZE_IN_BYTES + 1] =
extend_private_log_plaintext(plaintext);
compute_encrypted_log(
contract_address,
ovsk_app,
ovpk,
recipient,
sender,
extended_plaintext,
)
}

pub fn compute_partial_public_log_payload<let P: u32, let M: u32>(
contract_address: AztecAddress,
ovsk_app: Field,
ovpk: OvpkM,
recipient: AztecAddress,
sender: AztecAddress,
plaintext: [u8; P],
) -> [u8; M] {
let extended_plaintext: [u8; P + 1] = extend_private_log_plaintext(plaintext);
compute_encrypted_log(
contract_address,
ovsk_app,
ovpk,
recipient,
sender,
extended_plaintext,
)
}

fn compute_encrypted_log<let P: u32, let M: u32>(
contract_address: AztecAddress,
ovsk_app: Field,
ovpk: OvpkM,
recipient: AztecAddress,
sender: AztecAddress,
plaintext: [u8; P],
include_public_values_prefix: bool,
) -> [u8; M] {
let (eph_sk, eph_pk) = generate_ephemeral_key_pair();

Expand All @@ -35,50 +122,71 @@ fn compute_private_log_payload<let P: u32, let M: u32>(
let outgoing_body_ciphertext: [u8; 144] =
compute_outgoing_body_ciphertext(recipient, fr_to_fq(ovsk_app), eph_sk, eph_pk);

// If we include the prefix for number of public values, we need to add 1 byte to the offset
let mut offset = if include_public_values_prefix { 1 } else { 0 };
let mut encrypted_bytes = [0; M];
let mut offset = 0;

let mut encrypted_bytes: [u8; M] = [0; M];
// @todo We ignore the tags for now
// incoming_tag
offset += 32;

// eph_pk
let eph_pk_bytes = point_to_bytes(eph_pk);
for i in 0..32 {
encrypted_bytes[offset + i] = eph_pk_bytes[i];
}

offset += 32;

// incoming_header
// outgoing_header
for i in 0..48 {
encrypted_bytes[offset + i] = incoming_header_ciphertext[i];
encrypted_bytes[offset + 48 + i] = outgoing_header_ciphertext[i];
}

offset += 48 * 2;

// outgoing_body
for i in 0..144 {
encrypted_bytes[offset + i] = outgoing_body_ciphertext[i];
}

offset += 144;

// incoming_body
// Then we fill in the rest as the incoming body ciphertext
let size = M - offset;
assert_eq(size, incoming_body_ciphertext.len(), "ciphertext length mismatch");
for i in 0..size {
encrypted_bytes[offset + i] = incoming_body_ciphertext[i];
}

// Current unoptimized size of the encrypted log
// empty_prefix (1 byte)
// incoming_tag (32 bytes)
// outgoing_tag (32 bytes)
// eph_pk (32 bytes)
// incoming_header (48 bytes)
// outgoing_header (48 bytes)
// outgoing_body (144 bytes)
// incoming_body_fixed (64 bytes)
// incoming_body_variable (P + 16 bytes padding)
encrypted_bytes
}

// Prepend the plaintext length as the first byte, then copy the plaintext itself starting from the second byte.
// Fill the remaining bytes with random values to reach a fixed length of N.
fn extend_private_log_plaintext<let P: u32, let N: u32>(plaintext: [u8; P]) -> [u8; N] {
let mut padded = unsafe { get_random_bytes() };
padded[0] = P as u8;
for i in 0..P {
padded[i + 1] = plaintext[i];
}
padded
}

unconstrained fn get_random_bytes<let N: u32>() -> [u8; N] {
let mut bytes = [0; N];
let mut idx = 32;
let mut randomness = [0; 32];
for i in 0..N {
if idx == 32 {
randomness = random().to_be_bytes();
idx = 1; // Skip the first byte as it's always 0.
}
bytes[i] = randomness[idx];
idx += 1;
}
bytes
}

/// Converts a base field element to scalar field element.
/// This is fine because modulus of the base field is smaller than the modulus of the scalar field.
fn fr_to_fq(r: Field) -> Scalar {
Expand Down Expand Up @@ -167,7 +275,7 @@ pub fn compute_outgoing_body_ciphertext(
mod test {
use crate::encrypted_logs::payload::{
compute_incoming_body_ciphertext, compute_outgoing_body_ciphertext,
compute_private_log_payload,
compute_private_log_payload, MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES,
};
use dep::protocol_types::{
address::AztecAddress, point::Point, public_keys::OvpkM, scalar::Scalar,
Expand All @@ -178,7 +286,7 @@ mod test {

#[test]
unconstrained fn test_encrypted_log_matches_typescript() {
// All the values in this test were copied over from `tagged_log.test.ts`
// All the values in this test were copied over from `encrypted_log_payload.test.ts`
let contract_address = AztecAddress::from_field(
0x10f48cd9eff7ae5b209c557c70de2e657ee79166868676b787e9417e19260e04,
);
Expand All @@ -200,8 +308,13 @@ mod test {
101, 153, 0, 0, 16, 39,
];

let randomness = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f;
let _ = OracleMock::mock("getRandomField").returns(randomness).times(
(MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES as u64 + 1 + 30) / 31,
);

let eph_sk = 0x1358d15019d4639393d62b97e1588c095957ce74a1c32d6ec7d62fe6705d9538;
let _ = OracleMock::mock("getRandomField").returns(eph_sk);
let _ = OracleMock::mock("getRandomField").returns(eph_sk).times(1);

let recipient = AztecAddress::from_field(
0x25afb798ea6d0b8c1618e50fdeafa463059415013d3b7c75d46abf5e242be70c,
Expand All @@ -218,7 +331,6 @@ mod test {
recipient,
sender,
plaintext,
false,
);

// The following value was generated by `encrypted_log_payload.test.ts`
Expand All @@ -239,13 +351,21 @@ mod test {
208, 176, 145, 50, 180, 152, 245, 55, 112, 40, 153, 180, 78, 54, 102, 119, 98, 56, 235,
246, 51, 179, 86, 45, 127, 18, 77, 187, 168, 41, 24, 232, 113, 149, 138, 148, 33, 143,
215, 150, 188, 105, 131, 254, 236, 199, 206, 56, 44, 130, 134, 29, 99, 254, 69, 153,
146, 68, 234, 148, 148, 178, 38, 221, 182, 148, 178, 100, 13, 206, 0, 91, 71, 58, 207,
26, 227, 190, 21, 143, 85, 138, 209, 202, 34, 142, 159, 121, 61, 9, 57, 2, 48, 162, 89,
126, 14, 83, 173, 40, 247, 170, 154, 112, 12, 204, 48, 38, 7, 173, 108, 38, 234, 20, 16,
115, 91, 106, 140, 121, 63, 99, 23, 247, 0, 148, 9, 163, 145, 43, 21, 238, 47, 40, 204,
241, 124, 246, 201, 75, 114, 3, 1, 229, 197, 130, 109, 227, 158, 133, 188, 125, 179,
220, 51, 170, 121, 175, 202, 243, 37, 103, 13, 27, 53, 157, 8, 177, 11, 208, 120, 64,
211, 148, 201, 240, 56,
146, 68, 234, 148, 148, 178, 38, 221, 182, 103, 252, 139, 7, 246, 132, 29, 232, 78, 102,
126, 28, 136, 8, 219, 180, 162, 14, 62, 71, 118, 40, 147, 93, 87, 188, 231, 32, 93, 56,
193, 194, 197, 120, 153, 164, 139, 114, 18, 149, 2, 226, 19, 170, 250, 249, 128, 56,
236, 93, 14, 101, 115, 20, 173, 73, 192, 53, 229, 7, 23, 59, 11, 176, 9, 147, 175, 168,
206, 48, 127, 126, 76, 51, 211, 66, 232, 16, 132, 243, 14, 196, 181, 118, 12, 71, 236,
250, 253, 71, 249, 122, 30, 23, 23, 19, 89, 47, 193, 69, 240, 164, 34, 128, 110, 13,
133, 198, 7, 165, 14, 31, 239, 210, 146, 78, 67, 86, 32, 159, 244, 214, 246, 121, 246,
233, 252, 20, 131, 221, 28, 146, 222, 119, 222, 162, 250, 252, 189, 18, 147, 12, 142,
177, 222, 178, 122, 248, 113, 197, 40, 199, 152, 251, 91, 81, 243, 25, 156, 241, 141,
60, 12, 99, 103, 169, 97, 32, 112, 37, 244, 255, 126, 46, 114, 226, 113, 223, 249, 27,
3, 31, 41, 233, 28, 8, 23, 84, 99, 25, 186, 65, 33, 9, 35, 74, 16, 52, 169, 48, 161,
134, 233, 242, 136, 39, 162, 105, 205, 43, 253, 183, 36, 138, 186, 87, 31, 7, 248, 125,
227, 193, 172, 155, 98, 33, 61, 186, 158, 241, 192, 23, 28, 186, 100, 222, 174, 19, 64,
224, 113, 251, 143, 45, 152, 81, 67, 116, 16, 95, 189, 83, 31, 124, 39, 155, 142, 66, 0,
120, 197, 221, 161, 62, 75, 192, 255, 186, 200, 10, 135, 7,
];
assert_eq(encrypted_log_from_typescript, log);
}
Expand Down
Loading
Loading