Skip to content

Commit

Permalink
fix: Sequencer times out L1 tx at end of L2 slot (#11112)
Browse files Browse the repository at this point in the history
Sets the L1 tx utils timeout to the end of the L2 slot.
  • Loading branch information
spalladino authored and benesjan committed Jan 15, 2025
1 parent 4f06b97 commit 2d16700
Show file tree
Hide file tree
Showing 41 changed files with 359 additions and 235 deletions.
41 changes: 22 additions & 19 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,11 @@ impl PrivateContext {
// about secrets from other contracts. We therefore silo secret keys, and rely on the private kernel to
// validate that we siloed secret key corresponds to correct siloing of the master secret key that hashes
// to `pk_m_hash`.
let request = unsafe { get_key_validation_request(pk_m_hash, key_index) };
assert(request.pk_m.hash() == pk_m_hash);
let request = unsafe {
//@safety Kernels verify that the key validation request is valid and below we verify that a request
// for the correct public key has been received.
get_key_validation_request(pk_m_hash, key_index)
};

self.key_validation_requests_and_generators.push(
KeyValidationRequestAndGenerator {
Expand Down Expand Up @@ -388,12 +391,12 @@ impl PrivateContext {
let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call;
let start_side_effect_counter = self.side_effect_counter;

// The oracle simulates the private call and returns the value of the side effects counter after execution of
// the call (which means that end_side_effect_counter - start_side_effect_counter is the number of side effects
// that took place), along with the hash of the return values. We validate these by requesting a private kernel
// iteration in which the return values are constrained to hash to `returns_hash` and the side effects counter
// to increment from start to end.
let (end_side_effect_counter, returns_hash) = unsafe {
//@safety The oracle simulates the private call and returns the value of the side effects counter after
// execution of the call (which means that end_side_effect_counter - start_side_effect_counter is
// the number of side effects that took place), along with the hash of the return values. We validate these
// by requesting a private kernel iteration in which the return values are constrained to hash
// to `returns_hash` and the side effects counter to increment from start to end.
call_private_function_internal(
contract_address,
function_selector,
Expand Down Expand Up @@ -491,13 +494,13 @@ impl PrivateContext {
let counter = self.next_counter();

let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call;
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/8985): Fix this.
// WARNING: This is insecure and should be temporary!
// The oracle hashes the arguments and returns a new args_hash.
// new_args = [selector, ...old_args], so as to make it suitable to call the public dispatch function.
// We don't validate or compute it in the circuit because a) it's harder to do with slices, and
// b) this is only temporary.
let args_hash = unsafe {
//@safety TODO(https://github.com/AztecProtocol/aztec-packages/issues/8985): Fix this.
// WARNING: This is insecure and should be temporary!
// The oracle repacks the arguments and returns a new args_hash.
// new_args = [selector, ...old_args], so as to make it suitable to call the public dispatch function.
// We don't validate or compute it in the circuit because a) it's harder to do with slices, and
// b) this is only temporary.
enqueue_public_function_call_internal(
contract_address,
function_selector,
Expand Down Expand Up @@ -547,13 +550,13 @@ impl PrivateContext {
let counter = self.next_counter();

let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call;
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/8985): Fix this.
// WARNING: This is insecure and should be temporary!
// The oracle hashes the arguments and returns a new args_hash.
// new_args = [selector, ...old_args], so as to make it suitable to call the public dispatch function.
// We don't validate or compute it in the circuit because a) it's harder to do with slices, and
// b) this is only temporary.
let args_hash = unsafe {
//@safety TODO(https://github.com/AztecProtocol/aztec-packages/issues/8985): Fix this.
// WARNING: This is insecure and should be temporary!
// The oracle repacks the arguments and returns a new args_hash.
// new_args = [selector, ...old_args], so as to make it suitable to call the public dispatch function.
// We don't validate or compute it in the circuit because a) it's harder to do with slices, and
// b) this is only temporary.
set_public_teardown_function_call_internal(
contract_address,
function_selector,
Expand Down
92 changes: 59 additions & 33 deletions noir-projects/aztec-nr/aztec/src/context/public_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,34 @@ impl PublicContext {
where
T: Serialize<N>,
{
// AVM opcodes are constrained by the AVM itself
unsafe { emit_unencrypted_log(Serialize::serialize(log).as_slice()) };
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
emit_unencrypted_log(Serialize::serialize(log).as_slice())
};
}

pub fn note_hash_exists(_self: Self, note_hash: Field, leaf_index: Field) -> bool {
// AVM opcodes are constrained by the AVM itself
unsafe { note_hash_exists(note_hash, leaf_index) } == 1
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
note_hash_exists(note_hash, leaf_index)
}
== 1
}

pub fn l1_to_l2_msg_exists(_self: Self, msg_hash: Field, msg_leaf_index: Field) -> bool {
// AVM opcodes are constrained by the AVM itself
unsafe { l1_to_l2_msg_exists(msg_hash, msg_leaf_index) } == 1
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
l1_to_l2_msg_exists(msg_hash, msg_leaf_index)
}
== 1
}

pub fn nullifier_exists(_self: Self, unsiloed_nullifier: Field, address: AztecAddress) -> bool {
// AVM opcodes are constrained by the AVM itself
unsafe { nullifier_exists(unsiloed_nullifier, address.to_field()) } == 1
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
nullifier_exists(unsiloed_nullifier, address.to_field())
}
== 1
}

pub fn consume_l1_to_l2_message(
Expand Down Expand Up @@ -73,8 +84,10 @@ impl PublicContext {
}

pub fn message_portal(_self: &mut Self, recipient: EthAddress, content: Field) {
// AVM opcodes are constrained by the AVM itself
unsafe { send_l2_to_l1_msg(recipient, content) };
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
send_l2_to_l1_msg(recipient, content)
};
}

pub unconstrained fn call_public_function(
Expand Down Expand Up @@ -114,30 +127,36 @@ impl PublicContext {
}

pub fn push_note_hash(_self: &mut Self, note_hash: Field) {
// AVM opcodes are constrained by the AVM itself
unsafe { emit_note_hash(note_hash) };
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
emit_note_hash(note_hash)
};
}
pub fn push_nullifier(_self: &mut Self, nullifier: Field) {
// AVM opcodes are constrained by the AVM itself
unsafe { emit_nullifier(nullifier) };
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
emit_nullifier(nullifier)
};
}

pub fn this_address(_self: Self) -> AztecAddress {
// AVM opcodes are constrained by the AVM itself
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
address()
}
}
pub fn msg_sender(_self: Self) -> AztecAddress {
// AVM opcodes are constrained by the AVM itself
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
sender()
}
}
pub fn selector(_self: Self) -> FunctionSelector {
// The selector is the first element of the calldata when calling a public function through dispatch.
// AVM opcodes are constrained by the AVM itself.
let raw_selector: [Field; 1] = unsafe { calldata_copy(0, 1) };
let raw_selector: [Field; 1] = unsafe {
//@safety AVM opcodes are constrained by the AVM itself
calldata_copy(0, 1)
};
FunctionSelector::from_field(raw_selector[0])
}
pub fn get_args_hash(mut self) -> Field {
Expand All @@ -148,71 +167,76 @@ impl PublicContext {
self.args_hash.unwrap_unchecked()
}
pub fn transaction_fee(_self: Self) -> Field {
// AVM opcodes are constrained by the AVM itself
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
transaction_fee()
}
}

pub fn chain_id(_self: Self) -> Field {
// AVM opcodes are constrained by the AVM itself
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
chain_id()
}
}
pub fn version(_self: Self) -> Field {
// AVM opcodes are constrained by the AVM itself
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
version()
}
}
pub fn block_number(_self: Self) -> Field {
// AVM opcodes are constrained by the AVM itself
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
block_number()
}
}
pub fn timestamp(_self: Self) -> u64 {
// AVM opcodes are constrained by the AVM itself
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
timestamp()
}
}
pub fn fee_per_l2_gas(_self: Self) -> Field {
// AVM opcodes are constrained by the AVM itself
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
fee_per_l2_gas()
}
}
pub fn fee_per_da_gas(_self: Self) -> Field {
// AVM opcodes are constrained by the AVM itself
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
fee_per_da_gas()
}
}

pub fn l2_gas_left(_self: Self) -> Field {
// AVM opcodes are constrained by the AVM itself
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
l2_gas_left()
}
}
pub fn da_gas_left(_self: Self) -> Field {
// AVM opcodes are constrained by the AVM itself
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
da_gas_left()
}
}
pub fn is_static_call(_self: Self) -> bool {
// AVM opcodes are constrained by the AVM itself
unsafe { is_static_call() } == 1
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
is_static_call()
}
== 1
}

pub fn raw_storage_read<let N: u32>(_self: Self, storage_slot: Field) -> [Field; N] {
let mut out = [0; N];
for i in 0..N {
// AVM opcodes are constrained by the AVM itself
out[i] = unsafe { storage_read(storage_slot + i as Field) };
out[i] = unsafe {
//@safety AVM opcodes are constrained by the AVM itself
storage_read(storage_slot + i as Field)
};
}
out
}
Expand All @@ -226,8 +250,10 @@ impl PublicContext {

pub fn raw_storage_write<let N: u32>(_self: Self, storage_slot: Field, values: [Field; N]) {
for i in 0..N {
// AVM opcodes are constrained by the AVM itself
unsafe { storage_write(storage_slot + i as Field, values[i]) };
unsafe {
//@safety AVM opcodes are constrained by the AVM itself
storage_write(storage_slot + i as Field, values[i])
};
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,11 @@ where
Event: EventInterface<N>,
{
|e: Event| {
// Unconstrained logs have both their content and encryption unconstrained - it could occur that the
// recipient is unable to decrypt the payload.
let encrypted_log =
unsafe { compute_payload_unconstrained(*context, e, recipient, sender) };
let encrypted_log = unsafe {
//@safety Unconstrained logs have both their content and encryption unconstrained - it could occur that the
// recipient is unable to decrypt the payload.
compute_payload_unconstrained(*context, e, recipient, sender)
};
context.emit_private_log(encrypted_log);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,18 @@ where
Note: NoteInterface<N>,
{
|e: NoteEmission<Note>| {
// Unconstrained logs have both their content and encryption unconstrained - it could occur that the
// recipient is unable to decrypt the payload.
// Regarding the note hash counter, this is used for squashing. The kernel assumes that a given note can have
// more than one log and removes all of the matching ones, so all a malicious sender could do is either: cause
// for the log to be deleted when it shouldn't have (which is fine - they can already make the content be
// whatever), or cause for the log to not be deleted when it should have (which is also fine - it'll be a log
// for a note that doesn't exist).
// It's important here that we do not
// return the log from this function to the app, otherwise it could try to do stuff with it and then that might
// be wrong.
let (encrypted_log, note_hash_counter) =
unsafe { compute_payload_unconstrained(*context, e.note, recipient, sender) };
let (encrypted_log, note_hash_counter) = unsafe {
//@safety Unconstrained logs have both their content and encryption unconstrained - it could occur that
// the recipient is unable to decrypt the payload.
// Regarding the note hash counter, this is used for squashing. The kernel assumes that a given note can
// have more than one log and removes all of the matching ones, so all a malicious sender could do is
// either: cause for the log to be deleted when it shouldn't have (which is fine - they can already make
// the content be whatever), or cause for the log to not be deleted when it should have (which is also fine
// - it'll be a log for a note that doesn't exist).
// It's important here that we do not return the log from this function to the app, otherwise it could
// try to do stuff with it and then that might be wrong.
compute_payload_unconstrained(*context, e.note, recipient, sender)
};
context.emit_raw_note_log(encrypted_log, note_hash_counter);
}
}
38 changes: 24 additions & 14 deletions noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,12 @@ pub fn compute_private_log_payload<let P: u32>(
let encrypted: [u8; ENCRYPTED_PAYLOAD_SIZE_IN_BYTES] =
compute_encrypted_log(contract_address, recipient, extended_plaintext);

// We assume that the sender wants for the recipient to find the tagged note, and therefore that they will cooperate
// and use the correct tag. Usage of a bad tag will result in the recipient not being able to find the note
// automatically.
let tag = unsafe { get_app_tag_as_sender(sender, recipient) };
let tag = unsafe {
//@safety We assume that the sender wants for the recipient to find the tagged note, and therefore that they
// will cooperate and use the correct tag. Usage of a bad tag will result in the recipient not being able to
// find the note automatically.
get_app_tag_as_sender(sender, recipient)
};
increment_app_tagging_secret_index_as_sender(sender, recipient);

array_concat([tag], bytes_to_fields(encrypted))
Expand All @@ -82,10 +84,12 @@ pub fn compute_partial_public_log_payload<let P: u32, let M: u32>(
let encrypted: [u8; M - 32] =
compute_encrypted_log(contract_address, recipient, extended_plaintext);

// We assume that the sender wants for the recipient to find the tagged note, and therefore that they will cooperate
// and use the correct tag. Usage of a bad tag will result in the recipient not being able to find the note
// automatically.
let tag = unsafe { get_app_tag_as_sender(sender, recipient) };
let tag = unsafe {
//@safety We assume that the sender wants for the recipient to find the tagged note, and therefore that they
// will cooperate and use the correct tag. Usage of a bad tag will result in the recipient not being able to
// find the note automatically.
get_app_tag_as_sender(sender, recipient)
};
increment_app_tagging_secret_index_as_sender(sender, recipient);
// Silo the tag with contract address.
// This is done by the kernel circuit to the private logs, but since the partial log will be finalized and emitted
Expand Down Expand Up @@ -158,7 +162,11 @@ fn compute_encrypted_log<let P: u32, let M: u32>(
// 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() };
let mut padded = unsafe {
//@safety A malicious sender could reveal the whole contents of the encrypted log so trusting it to set
// a random padding in plaintext is fine.
get_random_bytes()
};
padded[0] = (P >> 8) as u8;
padded[1] = P as u8;
for i in 0..P {
Expand Down Expand Up @@ -192,11 +200,13 @@ fn fr_to_fq(r: Field) -> Scalar {

fn generate_ephemeral_key_pair() -> (Scalar, Point) {
// @todo Need to draw randomness from the full domain of Fq not only Fr
// We use the randomness to preserve the privacy of both the sender and recipient via encryption, so a malicious
// sender could use non-random values to reveal the plaintext. But they already know it themselves anyway, and so
// the recipient already trusts them to not disclose this information. We can therefore assume that the sender will
// cooperate in the random value generation.
let randomness = unsafe { random() };
let randomness = unsafe {
//@safety We use the randomness to preserve the privacy of both the sender and recipient via encryption, so
// a malicious sender could use non-random values to reveal the plaintext. But they already know it themselves
// anyway, and so the recipient already trusts them to not disclose this information. We can therefore assume
// that the sender will cooperate in the random value generation.
random()
};

// We use the unsafe version of `fr_to_fq` because multi_scalar_mul (called by derive_public_key) will constrain
// the scalars.
Expand Down
1 change: 1 addition & 0 deletions noir-projects/aztec-nr/aztec/src/history/note_inclusion.nr
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ impl ProveNoteInclusion for BlockHeader {
let note_hash = compute_note_hash_for_nullify(note);

let witness = unsafe {
//@safety The witness is only used as a "magical value" that makes the merkle proof below pass. Hence it's safe.
get_note_hash_membership_witness(self.global_variables.block_number as u32, note_hash)
};

Expand Down
Loading

0 comments on commit 2d16700

Please sign in to comment.