Skip to content
Draft
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
15 changes: 9 additions & 6 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -878,8 +878,11 @@ impl PrivateContext {
/// to wait until the messages are copied before you can consume them.
///
/// # Arguments
/// * `content` - The message content that was sent from L1
/// * `secret` - Secret fields used for message privacy (if needed)
/// * `public_content_hash` - Hash of the public content of the message that was sent from L1. Only the hash is
/// needed since the public content has an application-defined layout and is only used to rebuild the leaf.
/// * `private_content` - Private content of the message (preimage of the private content hash sent from L1),
/// application-defined - e.g. a claim secret, or the L2 recipient plus a blinding value. The preimage is
/// required since it's mixed into the nullifier, which is what ties consumption to knowledge of the secret.
/// * `sender` - Ethereum address that sent the message
/// * `leaf_index` - Index of the message in the L1-to-L2 message tree
///
Expand All @@ -888,8 +891,8 @@ impl PrivateContext {
/// double-consumption.
pub fn consume_l1_to_l2_message<let N: u32>(
&mut self,
content: Field,
secret: [Field; N],
public_content_hash: Field,
private_content: [Field; N],
sender: EthAddress,
leaf_index: Field,
) {
Expand All @@ -899,8 +902,8 @@ impl PrivateContext {
sender,
self.chain_id(),
self.version(),
content,
secret,
public_content_hash,
private_content,
leaf_index,
);

Expand Down
19 changes: 10 additions & 9 deletions noir-projects/aztec-nr/aztec/src/context/public_context.nr
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
context::gas::GasOpts,
hash::{
compute_l1_to_l2_message_hash, compute_l1_to_l2_message_nullifier, compute_secret_hash,
compute_l1_to_l2_message_hash, compute_l1_to_l2_message_nullifier, compute_private_content_hash,
compute_siloed_nullifier,
},
oracle::avm,
Expand Down Expand Up @@ -225,8 +225,9 @@ impl PublicContext {
/// you will need to wait until the messages are copied before you can consume them.
///
/// # Arguments
/// * `content` - The message content that was sent from L1
/// * `secret` - Secret fields used for message privacy (if needed)
/// * `public_content_hash` - Hash of the public content of the message that was sent from L1
/// * `private_content` - Private content of the message,
/// application-defined - e.g. a claim secret, or the L2 recipient plus a blinding value
/// * `sender` - Ethereum address that sent the message
/// * `leaf_index` - Index of the message in the L1-to-L2 message tree
///
Expand All @@ -238,23 +239,23 @@ impl PublicContext {
///
pub fn consume_l1_to_l2_message<let N: u32>(
self: Self,
content: Field,
secret: [Field; N],
public_content_hash: Field,
private_content: [Field; N],
sender: EthAddress,
leaf_index: Field,
) {
let secret_hash = compute_secret_hash(secret);
let private_content_hash = compute_private_content_hash(private_content);
let message_hash = compute_l1_to_l2_message_hash(
sender,
self.chain_id(),
/*recipient=*/
self.this_address(),
self.version(),
content,
secret_hash,
public_content_hash,
private_content_hash,
leaf_index,
);
let nullifier = compute_l1_to_l2_message_nullifier(message_hash, secret);
let nullifier = compute_l1_to_l2_message_nullifier(message_hash, private_content);

assert(!self.nullifier_exists_unsafe(nullifier, self.this_address()), "L1-to-L2 message is already nullified");
assert(self.l1_to_l2_msg_exists(message_hash, leaf_index), "Tried to consume nonexistent L1-to-L2 message");
Expand Down
39 changes: 21 additions & 18 deletions noir-projects/aztec-nr/aztec/src/hash.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,56 @@
use crate::protocol::{
address::{AztecAddress, EthAddress},
constants::{
DOM_SEP__FUNCTION_ARGS, DOM_SEP__MESSAGE_NULLIFIER, DOM_SEP__PUBLIC_BYTECODE, DOM_SEP__PUBLIC_CALLDATA,
DOM_SEP__SECRET_HASH, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS,
DOM_SEP__FUNCTION_ARGS, DOM_SEP__MESSAGE_NULLIFIER, DOM_SEP__PRIVATE_CONTENT_HASH, DOM_SEP__PUBLIC_BYTECODE,
DOM_SEP__PUBLIC_CALLDATA, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS,
},
hash::{poseidon2_hash_subarray, poseidon2_hash_with_separator, sha256_to_field},
traits::ToField,
};

pub use crate::protocol::hash::compute_siloed_nullifier;

pub fn compute_secret_hash<let N: u32>(secret: [Field; N]) -> Field {
poseidon2_hash_with_separator(secret, DOM_SEP__SECRET_HASH)
pub fn compute_private_content_hash<let N: u32>(private_content: [Field; N]) -> Field {
poseidon2_hash_with_separator(private_content, DOM_SEP__PRIVATE_CONTENT_HASH)
}

pub fn compute_l1_to_l2_message_hash(
sender: EthAddress,
chain_id: Field,
recipient: AztecAddress,
version: Field,
content: Field,
secret_hash: Field,
public_content_hash: Field,
private_content_hash: Field,
leaf_index: Field,
) -> Field {
let mut hash_bytes = [0 as u8; 224];
let sender_bytes: [u8; 32] = sender.to_field().to_be_bytes();
let chain_id_bytes: [u8; 32] = chain_id.to_be_bytes();
let recipient_bytes: [u8; 32] = recipient.to_field().to_be_bytes();
let version_bytes: [u8; 32] = version.to_be_bytes();
let content_bytes: [u8; 32] = content.to_be_bytes();
let secret_hash_bytes: [u8; 32] = secret_hash.to_be_bytes();
let public_content_hash_bytes: [u8; 32] = public_content_hash.to_be_bytes();
let private_content_hash_bytes: [u8; 32] = private_content_hash.to_be_bytes();
let leaf_index_bytes: [u8; 32] = leaf_index.to_be_bytes();

for i in 0..32 {
hash_bytes[i] = sender_bytes[i];
hash_bytes[i + 32] = chain_id_bytes[i];
hash_bytes[i + 64] = recipient_bytes[i];
hash_bytes[i + 96] = version_bytes[i];
hash_bytes[i + 128] = content_bytes[i];
hash_bytes[i + 160] = secret_hash_bytes[i];
hash_bytes[i + 128] = public_content_hash_bytes[i];
hash_bytes[i + 160] = private_content_hash_bytes[i];
hash_bytes[i + 192] = leaf_index_bytes[i];
}

sha256_to_field(hash_bytes)
}

// The nullifier of an l1 to l2 message is the hash of the message salted with the secret.
pub fn compute_l1_to_l2_message_nullifier<let N: u32>(message_hash: Field, secret: [Field; N]) -> Field {
poseidon2_hash_with_separator([message_hash].concat(secret), DOM_SEP__MESSAGE_NULLIFIER)
// The nullifier of an l1 to l2 message is the hash of the message salted with the private content.
pub fn compute_l1_to_l2_message_nullifier<let N: u32>(message_hash: Field, private_content: [Field; N]) -> Field {
poseidon2_hash_with_separator(
[message_hash].concat(private_content),
DOM_SEP__MESSAGE_NULLIFIER,
)
}

// Computes the hash of input arguments or return values for private functions, or for authwit creation.
Expand Down Expand Up @@ -94,14 +97,14 @@ pub fn compute_public_bytecode_commitment(
}

#[test]
unconstrained fn secret_hash_matches_typescript() {
let secret = 8;
let hash = compute_secret_hash([secret]);
unconstrained fn private_content_hash_matches_typescript() {
let private_content = [8];
let private_content_hash = compute_private_content_hash(private_content);

// The following value was generated by `yarn-project/stdlib/src/hash/hash.test.ts`
let secret_hash_from_ts = 0x1848b066724ab0ffb50ecb0ee3398eb839f162823d262bad959721a9c13d1e96;
let private_content_hash_from_ts = 0x2c8f08f651c706fc41742a1bddf91e10c710c827266b71fbb50c7ee4f1db70d7;

assert_eq(hash, secret_hash_from_ts);
assert_eq(private_content_hash, private_content_hash_from_ts);
}

#[test]
Expand Down
14 changes: 7 additions & 7 deletions noir-projects/aztec-nr/aztec/src/messaging.nr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
hash::{compute_l1_to_l2_message_hash, compute_l1_to_l2_message_nullifier, compute_secret_hash},
hash::{compute_l1_to_l2_message_hash, compute_l1_to_l2_message_nullifier, compute_private_content_hash},
oracle::get_l1_to_l2_membership_witness::{get_l1_to_l2_membership_witness, UnsiloedNullifier},
};

Expand All @@ -11,22 +11,22 @@ pub fn process_l1_to_l2_message<let N: u32>(
portal_contract_address: EthAddress,
chain_id: Field,
version: Field,
content: Field,
secret: [Field; N],
public_content_hash: Field,
private_content: [Field; N],
leaf_index: Field,
) -> Field {
let secret_hash = compute_secret_hash(secret);
let private_content_hash = compute_private_content_hash(private_content);
let message_hash = compute_l1_to_l2_message_hash(
portal_contract_address,
chain_id,
contract_address,
version,
content,
secret_hash,
public_content_hash,
private_content_hash,
leaf_index,
);

let nullifier = compute_l1_to_l2_message_nullifier(message_hash, secret);
let nullifier = compute_l1_to_l2_message_nullifier(message_hash, private_content);

// We prove that `message_hash` is in the tree by showing the derivation of the tree root, using a merkle path we
// get from an oracle.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ unconstrained fn try_get_l1_to_l2_membership_witness_oracle(
) -> Option<(Field, [Field; L1_TO_L2_MSG_TREE_HEIGHT])> {}

mod test {
use crate::hash::{compute_l1_to_l2_message_hash, compute_l1_to_l2_message_nullifier, compute_secret_hash};
use crate::hash::{compute_l1_to_l2_message_hash, compute_l1_to_l2_message_nullifier, compute_private_content_hash};
use crate::protocol::{
address::{AztecAddress, EthAddress},
merkle_tree::root::root_from_sibling_path,
Expand All @@ -54,8 +54,8 @@ mod test {
use crate::test::helpers::test_environment::TestEnvironment;
use super::{try_get_l1_to_l2_membership_witness, UnsiloedNullifier};

global CONTENT: Field = 0x0a0b0c;
global SECRET: Field = 0x1234;
global PUBLIC_CONTENT_HASH: Field = 0x0a0b0c;
global PRIVATE_CONTENT: [Field; 1] = [0x1234];

#[test]
unconstrained fn try_get_returns_witness_for_existing_message() {
Expand Down Expand Up @@ -90,15 +90,15 @@ mod test {
let (sender, recipient, leaf_index, message_hash) = send_message(env);
let nullifier = UnsiloedNullifier {
contract_address: recipient,
nullifier: compute_l1_to_l2_message_nullifier(message_hash, [SECRET]),
nullifier: compute_l1_to_l2_message_nullifier(message_hash, PRIVATE_CONTENT),
};

env.utility_context(|_| {
assert(try_get_l1_to_l2_membership_witness(message_hash, Option::some(nullifier)).is_some());
});

env.private_context_at(recipient, |context| {
context.consume_l1_to_l2_message(CONTENT, [SECRET], sender, leaf_index);
context.consume_l1_to_l2_message(PUBLIC_CONTENT_HASH, PRIVATE_CONTENT, sender, leaf_index);
});
env.utility_context(|_| {
assert(try_get_l1_to_l2_membership_witness(message_hash, Option::some(nullifier)).is_none());
Expand All @@ -109,15 +109,15 @@ mod test {
unconstrained fn send_message(env: TestEnvironment) -> (EthAddress, AztecAddress, Field, Field) {
let sender = EthAddress::from_field(0xdead);
let recipient = AztecAddress::from_field(17);
let leaf_index = env.send_l1_to_l2_message(CONTENT, SECRET, sender, recipient);
let leaf_index = env.send_l1_to_l2_message(PUBLIC_CONTENT_HASH, PRIVATE_CONTENT, sender, recipient);
let message_hash = env.utility_context(|context| {
compute_l1_to_l2_message_hash(
sender,
context.chain_id(),
recipient,
context.version(),
CONTENT,
compute_secret_hash([SECRET]),
PUBLIC_CONTENT_HASH,
compute_private_content_hash(PRIVATE_CONTENT),
leaf_index,
)
});
Expand Down
17 changes: 9 additions & 8 deletions noir-projects/aztec-nr/aztec/src/test/domain_separators.nr
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@ use crate::protocol::constants::{
DOM_SEP__IVSK_M, DOM_SEP__MERKLE_HASH, DOM_SEP__MESSAGE_NULLIFIER, DOM_SEP__MSSK_M, DOM_SEP__NHK_M,
DOM_SEP__NON_INTERACTIVE_HANDSHAKE_LOG_TAG, DOM_SEP__NOTE_HASH, DOM_SEP__NOTE_HASH_NONCE, DOM_SEP__NOTE_NULLIFIER,
DOM_SEP__NULLIFIER_MERKLE, DOM_SEP__OVSK_M, DOM_SEP__PARTIAL_ADDRESS, DOM_SEP__PARTIAL_NOTE_VALIDITY_COMMITMENT,
DOM_SEP__PRIVATE_FUNCTION_LEAF, DOM_SEP__PRIVATE_INITIALIZATION_NULLIFIER, DOM_SEP__PRIVATE_LOG_FIRST_FIELD,
DOM_SEP__PRIVATE_TX_HASH, DOM_SEP__PROTOCOL_CONTRACTS, DOM_SEP__PROTOCOL_NULLIFIER, DOM_SEP__PUBLIC_BYTECODE,
DOM_SEP__PUBLIC_CALLDATA, DOM_SEP__PUBLIC_DATA_MERKLE, DOM_SEP__PUBLIC_INITIALIZATION_NULLIFIER,
DOM_SEP__PUBLIC_KEYS_HASH, DOM_SEP__PUBLIC_LEAF_SLOT, DOM_SEP__PUBLIC_STORAGE_MAP_SLOT, DOM_SEP__PUBLIC_TX_HASH,
DOM_SEP__RETRIEVED_BYTECODES_MERKLE, DOM_SEP__SALTED_INITIALIZATION_HASH, DOM_SEP__SECRET_HASH,
DOM_SEP__SIGNATURE_PAYLOAD, DOM_SEP__SILOED_NOTE_HASH, DOM_SEP__SILOED_NULLIFIER, DOM_SEP__SINGLE_PUBLIC_KEY_HASH,
DOM_SEP__TSK_M, DOM_SEP__UNCONSTRAINED_MSG_LOG_TAG, DOM_SEP__UNIQUE_NOTE_HASH, DOM_SEP__WRITTEN_SLOTS_MERKLE,
DOM_SEP__PRIVATE_CONTENT_HASH, DOM_SEP__PRIVATE_FUNCTION_LEAF, DOM_SEP__PRIVATE_INITIALIZATION_NULLIFIER,
DOM_SEP__PRIVATE_LOG_FIRST_FIELD, DOM_SEP__PRIVATE_TX_HASH, DOM_SEP__PROTOCOL_CONTRACTS,
DOM_SEP__PROTOCOL_NULLIFIER, DOM_SEP__PUBLIC_BYTECODE, DOM_SEP__PUBLIC_CALLDATA, DOM_SEP__PUBLIC_DATA_MERKLE,
DOM_SEP__PUBLIC_INITIALIZATION_NULLIFIER, DOM_SEP__PUBLIC_KEYS_HASH, DOM_SEP__PUBLIC_LEAF_SLOT,
DOM_SEP__PUBLIC_STORAGE_MAP_SLOT, DOM_SEP__PUBLIC_TX_HASH, DOM_SEP__RETRIEVED_BYTECODES_MERKLE,
DOM_SEP__SALTED_INITIALIZATION_HASH, DOM_SEP__SIGNATURE_PAYLOAD, DOM_SEP__SILOED_NOTE_HASH,
DOM_SEP__SILOED_NULLIFIER, DOM_SEP__SINGLE_PUBLIC_KEY_HASH, DOM_SEP__TSK_M, DOM_SEP__UNCONSTRAINED_MSG_LOG_TAG,
DOM_SEP__UNIQUE_NOTE_HASH, DOM_SEP__WRITTEN_SLOTS_MERKLE,
};
use crate::protocol::hash::poseidon2_hash_bytes;

Expand Down Expand Up @@ -89,7 +90,7 @@ global PROTOCOL_DOMAIN_SEPARATORS: [u32; 59] = [
DOM_SEP__PARTIAL_NOTE_VALIDITY_COMMITMENT,
DOM_SEP__PUBLIC_INITIALIZATION_NULLIFIER,
DOM_SEP__PRIVATE_INITIALIZATION_NULLIFIER,
DOM_SEP__SECRET_HASH,
DOM_SEP__PRIVATE_CONTENT_HASH,
DOM_SEP__SIGNATURE_PAYLOAD,
DOM_SEP__BLOCK_HEADERS_HASH,
DOM_SEP__BLOB_HASHED_Y_LIMBS,
Expand Down
38 changes: 22 additions & 16 deletions noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
UtilityContext,
},
event::{event_interface::EventInterface, EventMessage},
hash::{compute_secret_hash, hash_args},
hash::{compute_private_content_hash, hash_args},
messages::{
delivery::OnchainDeliveryMode,
discovery::{
Expand Down Expand Up @@ -986,34 +986,40 @@ impl TestEnvironment {
/// [`PrivateContext::consume_l1_to_l2_message`](crate::context::PrivateContext::consume_l1_to_l2_message) or its
/// public counterpart. An empty block is mined carrying the message leaf.
///
/// `content`, `secret`, `sender` and `recipient` must match what the consuming contract uses. The secret is hashed
/// with the standard [`crate::hash::compute_secret_hash`] - use
/// [`send_l1_to_l2_message_from_secret_hash`](TestEnvironment::send_l1_to_l2_message_from_secret_hash) if the
/// contract commits to its secret differently).
/// `public_content_hash`, `private_content`, `sender` and `recipient` must match what the consuming contract uses.
/// The private content is hashed with the standard [`crate::hash::compute_private_content_hash`]. Contracts that
/// commit to their private content differently should use
/// [`TestEnvironment::send_l1_to_l2_message_from_private_content_hash`] instead.
///
/// Returns the global leaf index to pass as `consume_l1_to_l2_message`'s `leaf_index`.
pub unconstrained fn send_l1_to_l2_message(
pub unconstrained fn send_l1_to_l2_message<let N: u32>(
self,
content: Field,
secret: Field,
public_content_hash: Field,
private_content: [Field; N],
sender: EthAddress,
recipient: AztecAddress,
) -> Field {
self.send_l1_to_l2_message_from_secret_hash(content, compute_secret_hash([secret]), sender, recipient)
let private_content_hash = compute_private_content_hash(private_content);
self.send_l1_to_l2_message_from_private_content_hash(
public_content_hash,
private_content_hash,
sender,
recipient,
)
}

/// Variant of [`send_l1_to_l2_message`](TestEnvironment::send_l1_to_l2_message) that takes the secret hash
/// directly.
/// Variant of [`send_l1_to_l2_message`](TestEnvironment::send_l1_to_l2_message) that takes the private content
/// hash directly.
///
/// This is only required by contracts that commit to their consumption secret with a non-standard hash function.
pub unconstrained fn send_l1_to_l2_message_from_secret_hash(
/// This is only required by contracts that commit to their private content with a non-standard hash function.
pub unconstrained fn send_l1_to_l2_message_from_private_content_hash(
_self: Self,
content: Field,
secret_hash: Field,
public_content_hash: Field,
private_content_hash: Field,
sender: EthAddress,
recipient: AztecAddress,
) -> Field {
txe_oracles::send_l1_to_l2_message(content, secret_hash, sender, recipient)
txe_oracles::send_l1_to_l2_message(public_content_hash, private_content_hash, sender, recipient)
}

/// Creates a new account that can be used as the `from` parameter in contract calls, e.g. in `private_call` or
Expand Down
Loading
Loading