Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ The return values are a set of values that are returned from an applications exe
```rust
return_values : BoundedVec\<Field, RETURN_VALUES_LENGTH\>,
```
## Include By Timestamp
## Expiration Timestamp

Some data structures impose time constraints, e.g. they may make it so that a value can only be changed after a certain delay. Interacting with these in private involves creating proofs that are only valid as long as they are included before a certain future point in time. To achieve this, the `set_include_by_timestamp` function can be used to set this property:
Some data structures impose time constraints, e.g. they may make it so that a value can only be changed after a certain delay. Interacting with these in private involves creating proofs that are only valid as long as they are included before a certain future point in time. To achieve this, the `set_expiration_timestamp` function can be used to set this property:

#include_code include-by-timestamp /noir-projects/aztec-nr/aztec/src/context/private_context.nr rust
#include_code expiration-timestamp /noir-projects/aztec-nr/aztec/src/context/private_context.nr rust

A transaction that sets this value will never be included in a block with a timestamp larger than the requested value, since it would be considered invalid. This can also be used to make transactions automatically expire after some time if not included.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ It is sometimes necessary to read public mutable state in private. For example,

`DelayedPublicMutable` is the same as a `PublicMutable` in that it is a public value that can be read and written, but with a caveat: writes only take effect _after some time delay_. These delays are configurable, but they're typically on the order of a couple hours, if not days, making this state variable unsuitable for actions that must be executed immediately - such as an emergency shutdown. It is these very delays that enable private contract functions to _read the current value of a public state variable_, which is otherwise typically impossible.

The existence of minimum delays means that a private function that reads a public value at an anchor block has a guarantee that said historical value will remain the current value until _at least_ some time in the future - before the delay elapses. As long as the transaction gets included in a block before that time (by using the `include_by_timestamp` tx property), the read value is valid.
The existence of minimum delays means that a private function that reads a public value at an anchor block has a guarantee that said historical value will remain the current value until _at least_ some time in the future - before the delay elapses. As long as the transaction gets included in a block before that time (by using the `expiration_timestamp` tx property), the read value is valid.

#### Declaration

Expand All @@ -191,7 +191,7 @@ Returns the current value in a public, private or utility execution context.
#include_code get_current_value /noir-projects/noir-contracts/contracts/app/auth_contract/src/main.nr rust

:::warning Privacy Consideration
Reading `DelayedPublicMutable` in private sets the `include_by_timestamp` property, which may reveal timing information. Choose delays that align with common values to maximize privacy sets.
Reading `DelayedPublicMutable` in private sets the `expiration_timestamp` property, which may reveal timing information. Choose delays that align with common values to maximize privacy sets.
:::

#### `get_scheduled_value`
Expand Down
10 changes: 10 additions & 0 deletions docs/docs-developers/docs/resources/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ Aztec is in active development. Each version may introduce breaking changes that

## TBD

### [Protocol] `include_by_timestamp` renamed to `expiration_timestamp`

The `include_by_timestamp` field has been renamed to `expiration_timestamp` across the protocol to better convey its meaning.
**Noir:**

```diff
- context.set_tx_include_by_timestamp(123456789);
+ context.set_expiration_timestamp(123456789);
```

### [CLI] Dockerless CLI Installation

The Aztec CLI is now installed without Docker. The installation command has changed:
Expand Down
30 changes: 14 additions & 16 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ use crate::protocol::{
address::{AztecAddress, EthAddress},
constants::{
CONTRACT_CLASS_LOG_SIZE_IN_FIELDS, MAX_CONTRACT_CLASS_LOGS_PER_CALL, MAX_ENQUEUED_CALLS_PER_CALL,
MAX_INCLUDE_BY_TIMESTAMP_DURATION, MAX_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_L2_TO_L1_MSGS_PER_CALL,
MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NOTE_HASHES_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL,
MAX_NULLIFIERS_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PRIVATE_LOGS_PER_CALL,
MAX_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_L2_TO_L1_MSGS_PER_CALL, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL,
MAX_NOTE_HASHES_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_NULLIFIERS_PER_CALL,
MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PRIVATE_LOGS_PER_CALL, MAX_TX_LIFETIME,
NULL_MSG_SENDER_CONTRACT_ADDRESS, PRIVATE_LOG_SIZE_IN_FIELDS,
},
hash::poseidon2_hash,
Expand Down Expand Up @@ -148,7 +148,7 @@ pub struct PrivateContext {
pub args_hash: Field,
pub return_hash: Field,

pub include_by_timestamp: u64,
pub expiration_timestamp: u64,

pub(crate) note_hash_read_requests: BoundedVec<Scoped<Counted<Field>>, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL>,
pub(crate) nullifier_read_requests: BoundedVec<Scoped<Counted<Field>>, MAX_NULLIFIER_READ_REQUESTS_PER_CALL>,
Expand Down Expand Up @@ -180,16 +180,14 @@ pub struct PrivateContext {

impl PrivateContext {
pub fn new(inputs: PrivateContextInputs, args_hash: Field) -> PrivateContext {
let max_allowed_include_by_timestamp =
inputs.anchor_block_header.global_variables.timestamp + MAX_INCLUDE_BY_TIMESTAMP_DURATION;
PrivateContext {
inputs,
side_effect_counter: inputs.start_side_effect_counter + 1,
min_revertible_side_effect_counter: 0,
is_fee_payer: false,
args_hash,
return_hash: 0,
include_by_timestamp: max_allowed_include_by_timestamp,
expiration_timestamp: inputs.anchor_block_header.global_variables.timestamp + MAX_TX_LIFETIME,
note_hash_read_requests: BoundedVec::new(),
nullifier_read_requests: BoundedVec::new(),
key_validation_requests_and_generators: BoundedVec::new(),
Expand Down Expand Up @@ -430,7 +428,7 @@ impl PrivateContext {
///
/// # Advanced
/// * All private functions of a tx read from the same anchor block header.
/// * The protocol asserts that the `include_by_timestamp` of every tx is at most 24 hours beyond the timestamp of
/// * The protocol asserts that the `expiration_timestamp` of every tx is at most 24 hours beyond the timestamp of
/// the tx's chosen anchor block header. This enables the network's nodes to safely prune old txs from the mempool.
/// Therefore, the chosen block header _must_ be one from within the last 24 hours.
///
Expand Down Expand Up @@ -496,7 +494,7 @@ impl PrivateContext {
returns_hash: self.return_hash,
min_revertible_side_effect_counter: self.min_revertible_side_effect_counter,
is_fee_payer: self.is_fee_payer,
include_by_timestamp: self.include_by_timestamp,
expiration_timestamp: self.expiration_timestamp,
note_hash_read_requests: ClaimedLengthArray::from_bounded_vec(self.note_hash_read_requests),
nullifier_read_requests: ClaimedLengthArray::from_bounded_vec(self.nullifier_read_requests),
key_validation_requests_and_generators: ClaimedLengthArray::from_bounded_vec(
Expand Down Expand Up @@ -598,11 +596,11 @@ impl PrivateContext {
/// This expiry timestamp is publicly visible. See the "Advanced" section for privacy concerns.
///
/// # Arguments
/// * `include_by_timestamp` - Unix timestamp (seconds) deadline for inclusion. The include-by timestamp of this tx
/// * `expiration_timestamp` - Unix timestamp (seconds) deadline for inclusion. The include-by timestamp of this tx
/// will be _at most_ the timestamp specified.
///
/// # Advanced
/// * If multiple functions set differing `include_by_timestamp`s, the kernel circuits will set it to be the
/// * If multiple functions set differing `expiration_timestamp`s, the kernel circuits will set it to be the
/// _minimum_ of the two. This ensures the tx expiry requirements of all functions in the tx are met.
/// * Rollup circuits will reject expired txs.
/// * The protocol enforces that all transactions must be included within 24 hours of their chosen anchor block's
Expand All @@ -617,10 +615,10 @@ impl PrivateContext {
/// will need to be discussed. Wallets that deviate from a standard might accidentally reveal which wallet each
/// transaction originates from.
///
// docs:start:include-by-timestamp
pub fn set_include_by_timestamp(&mut self, include_by_timestamp: u64) {
// docs:end:include-by-timestamp
self.include_by_timestamp = std::cmp::min(self.include_by_timestamp, include_by_timestamp);
// docs:start:expiration-timestamp
pub fn set_expiration_timestamp(&mut self, expiration_timestamp: u64) {
// docs:end:expiration-timestamp
self.expiration_timestamp = std::cmp::min(self.expiration_timestamp, expiration_timestamp);
}

/// Asserts that a note has been created.
Expand Down Expand Up @@ -1460,7 +1458,7 @@ impl Empty for PrivateContext {
is_fee_payer: false,
args_hash: 0,
return_hash: 0,
include_by_timestamp: 0,
expiration_timestamp: 0,
note_hash_read_requests: BoundedVec::new(),
nullifier_read_requests: BoundedVec::new(),
key_validation_requests_and_generators: BoundedVec::new(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ where

// We prevent this transaction from being included in any timestamp after the time horizon, ensuring that the
// historical public value matches the current one, since it can only change after the horizon.
self.context.set_include_by_timestamp(time_horizon);
self.context.set_expiration_timestamp(time_horizon);

value_change.get_current_at(anchor_timestamp)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ unconstrained fn get_current_value_in_private_initial() {

assert_eq(state_var.get_current_value(), zeroed());
assert_eq(
context.include_by_timestamp,
context.expiration_timestamp,
context.get_anchor_block_header().global_variables.timestamp + TEST_INITIAL_DELAY,
);
});
Expand All @@ -251,7 +251,7 @@ unconstrained fn get_current_value_in_private_before_change() {
let state_var = in_private(context);

assert_eq(state_var.get_current_value(), MockStruct::empty());
assert_eq(context.include_by_timestamp, timestamp_of_change - 1);
assert_eq(context.expiration_timestamp, timestamp_of_change - 1);
});
}

Expand All @@ -275,10 +275,10 @@ unconstrained fn get_current_value_in_private_immediately_before_change() {

let state_var = in_private(context);

// Note that this transaction would never be valid since the include_by_timestamp is the same as the anchor
// Note that this transaction would never be valid since the expiration_timestamp is the same as the anchor
// block's timestamp, i.e. in the past.
assert_eq(state_var.get_current_value(), MockStruct::empty());
assert_eq(context.include_by_timestamp, timestamp_of_change - 1);
assert_eq(context.expiration_timestamp, timestamp_of_change - 1);
});
}

Expand All @@ -304,7 +304,7 @@ unconstrained fn get_current_value_in_private_at_change() {

assert_eq(state_var.get_current_value(), new_value);
assert_eq(
context.include_by_timestamp,
context.expiration_timestamp,
context.get_anchor_block_header().global_variables.timestamp + TEST_INITIAL_DELAY,
);
});
Expand Down Expand Up @@ -332,7 +332,7 @@ unconstrained fn get_current_value_in_private_after_change() {

assert_eq(state_var.get_current_value(), new_value);
assert_eq(
context.include_by_timestamp,
context.expiration_timestamp,
context.get_anchor_block_header().global_variables.timestamp + TEST_INITIAL_DELAY,
);
});
Expand Down Expand Up @@ -361,7 +361,7 @@ unconstrained fn get_current_value_in_private_with_non_initial_delay() {
let state_var = in_private(context);

assert_eq(state_var.get_current_value(), new_value);
assert_eq(context.include_by_timestamp, historical_timestamp + new_delay);
assert_eq(context.expiration_timestamp, historical_timestamp + new_delay);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::protocol::{
address::{AztecAddress, EthAddress},
constants::{
CONTRACT_CLASS_LOG_SIZE_IN_FIELDS, MAX_CONTRACT_CLASS_LOGS_PER_CALL,
MAX_ENQUEUED_CALLS_PER_CALL, MAX_INCLUDE_BY_TIMESTAMP_DURATION, MAX_L2_TO_L1_MSGS_PER_CALL,
MAX_ENQUEUED_CALLS_PER_CALL, MAX_TX_LIFETIME, MAX_L2_TO_L1_MSGS_PER_CALL,
MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_NULLIFIERS_PER_CALL,
MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PRIVATE_LOGS_PER_CALL,
NULL_MSG_SENDER_CONTRACT_ADDRESS, PRIVATE_LOG_SIZE_IN_FIELDS,
Expand All @@ -54,7 +54,7 @@ pub struct PrivateContext {
pub args_hash: Field,
pub return_hash: Field,

pub include_by_timestamp: u64,
pub expiration_timestamp: u64,

pub nullifier_read_requests: BoundedVec<Scoped<Counted<Field>>, MAX_NULLIFIER_READ_REQUESTS_PER_CALL>,

Expand All @@ -77,16 +77,15 @@ pub struct PrivateContext {

impl PrivateContext {
pub fn new(inputs: PrivateContextInputs, args_hash: Field) -> PrivateContext {
let max_allowed_include_by_timestamp = inputs.anchor_block_header.global_variables.timestamp
+ MAX_INCLUDE_BY_TIMESTAMP_DURATION;
PrivateContext {
inputs,
side_effect_counter: inputs.start_side_effect_counter + 1,
min_revertible_side_effect_counter: 0,
is_fee_payer: false,
args_hash,
return_hash: 0,
include_by_timestamp: max_allowed_include_by_timestamp,
expiration_timestamp: inputs.anchor_block_header.global_variables.timestamp
+ MAX_TX_LIFETIME,
nullifier_read_requests: BoundedVec::new(),
nullifiers: BoundedVec::new(),
anchor_block_header: inputs.anchor_block_header,
Expand Down Expand Up @@ -161,7 +160,7 @@ impl PrivateContext {
returns_hash: self.return_hash,
min_revertible_side_effect_counter: self.min_revertible_side_effect_counter,
is_fee_payer: self.is_fee_payer,
include_by_timestamp: self.include_by_timestamp,
expiration_timestamp: self.expiration_timestamp,
note_hash_read_requests: ClaimedLengthArray::empty(), // Not used by protocol contracts
nullifier_read_requests: ClaimedLengthArray::from_bounded_vec(
self.nullifier_read_requests,
Expand Down Expand Up @@ -214,8 +213,8 @@ impl PrivateContext {
}

/// Sets a deadline for when this transaction must be included in a block.
pub fn set_include_by_timestamp(&mut self, include_by_timestamp: u64) {
self.include_by_timestamp = std::cmp::min(self.include_by_timestamp, include_by_timestamp);
pub fn set_expiration_timestamp(&mut self, expiration_timestamp: u64) {
self.expiration_timestamp = std::cmp::min(self.expiration_timestamp, expiration_timestamp);
}

/// Pushes a new nullifier. Used by class_registry and instance_registry.
Expand Down Expand Up @@ -402,7 +401,7 @@ impl Empty for PrivateContext {
is_fee_payer: false,
args_hash: 0,
return_hash: 0,
include_by_timestamp: 0,
expiration_timestamp: 0,
nullifier_read_requests: BoundedVec::new(),
nullifiers: BoundedVec::new(),
private_call_requests: BoundedVec::new(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ where
let time_horizon = value_change.get_time_horizon(anchor_timestamp, effective_minimum_delay);

// We prevent this transaction from being included in any timestamp after the time horizon.
self.context.set_include_by_timestamp(time_horizon);
self.context.set_expiration_timestamp(time_horizon);

value_change.get_current_at(anchor_timestamp)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ pub contract Test {
}

#[external("private")]
fn set_include_by_timestamp(include_by_timestamp: u64, make_tx_hybrid: bool) {
self.context.set_include_by_timestamp(include_by_timestamp);
fn set_expiration_timestamp(expiration_timestamp: u64, make_tx_hybrid: bool) {
self.context.set_expiration_timestamp(expiration_timestamp);

if make_tx_hybrid {
self.enqueue_self.dummy_public_call()
Expand Down
4 changes: 2 additions & 2 deletions noir-projects/noir-protocol-circuits/ABOUT.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ Validator:
- Asserts equality (between the tx and current chain) of the chain_id, version, vk_tree_root, protocol_contracts_hash.
- Asserts that the tx's chosen gas prices are sufficiently high, relative to the block's minimum requirements.
- Asserts that the tx doesn't exceed the L2 gas limit.
- Asserts that the tx's `include_by_timestamp` hasn't already passed, relative to the block's timestamp.
- Asserts that the tx's `expiration_timestamp` hasn't already passed, relative to the block's timestamp.
- Hashes the `contract_class_log_fields` and compares them against the tx's claimed contract class log hash.

Composer:
Expand Down Expand Up @@ -177,7 +177,7 @@ Validator:
- Asserts equality (between the tx and current chain) of the chain_id, version, vk_tree_root, protocol_contracts_hash.
- Asserts that the tx's chosen gas prices are sufficiently high, relative to the block's minimum requirements.
- Asserts that the tx doesn't exceed the L2 gas limit.
- Asserts that the tx's `include_by_timestamp` hasn't already passed, relative to the block's timestamp.
- Asserts that the tx's `expiration_timestamp` hasn't already passed, relative to the block's timestamp.

Composer `.finish()`:
- **Appends the tx effects to the next available position of a blob.**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ expected_non_revertible_side_effect_counter = "0x0000000000000000000000000000000
expected_revertible_side_effect_counter = "0x0000000000000000000000000000000000000000000000000000000000000000"
min_revertible_side_effect_counter = "0x0000000000000000000000000000000000000000000000000000000000000005"
is_fee_payer = true
include_by_timestamp = "0x0000000000000000000000000000000000000000000000000000000069904e3d"
expiration_timestamp = "0x0000000000000000000000000000000000000000000000000000000069904e3d"

[app_public_inputs.call_context]
is_static_call = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ sibling_path = [

[previous_kernel_public_inputs]
min_revertible_side_effect_counter = "0x0000000000000000000000000000000000000000000000000000000000000005"
include_by_timestamp = "0x0000000000000000000000000000000000000000000000000000000069904e3d"
expiration_timestamp = "0x0000000000000000000000000000000000000000000000000000000069904e3d"
is_private_only = true
claimed_first_nullifier = "0x2024835fd8b74a764c77337cdecd852f4412858e32f7be5c0207f01b5da068b5"
claimed_revertible_counter = "0x0000000000000000000000000000000000000000000000000000000000000005"
Expand Down Expand Up @@ -6657,7 +6657,7 @@ expected_non_revertible_side_effect_counter = "0x0000000000000000000000000000000
expected_revertible_side_effect_counter = "0x0000000000000000000000000000000000000000000000000000000000000007"
min_revertible_side_effect_counter = "0x0000000000000000000000000000000000000000000000000000000000000000"
is_fee_payer = false
include_by_timestamp = "0x0000000000000000000000000000000000000000000000000000000069904e3d"
expiration_timestamp = "0x0000000000000000000000000000000000000000000000000000000069904e3d"

[app_public_inputs.call_context]
is_static_call = false
Expand Down
Loading
Loading