Skip to content

Commit 9a8152b

Browse files
authored
feat: merge-train/fairies-v5 (#24443)
BEGIN_COMMIT_OVERRIDE feat(pxe): origin block number timestamp log oracle in log retrieval (#24398) END_COMMIT_OVERRIDE
2 parents 2470706 + c777845 commit 9a8152b

31 files changed

Lines changed: 796 additions & 129 deletions

noir-projects/aztec-nr/aztec/src/facts/mod.nr

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Storage of immutable facts grouped into collections.
1+
//! Per-contract storage of immutable facts grouped into collections.
22
//!
33
//! A fact is a contract-defined, typed, immutable datum. Facts are grouped into collections identified by a
44
//! `(collection type, collection id)` tuple.
@@ -11,13 +11,24 @@ use crate::oracle::fact_store::{
1111
};
1212
use crate::protocol::{address::AztecAddress, traits::{Deserialize, Serialize}};
1313

14+
mod origin_state;
15+
pub use origin_state::OriginBlockState;
16+
1417
/// The block a retractable fact originates from.
1518
#[derive(Deserialize, Eq, Serialize)]
1619
pub struct OriginBlock {
1720
pub block_number: u32,
1821
pub block_hash: Field,
1922
}
2023

24+
/// A retractable fact's origin block.
25+
#[derive(Deserialize, Eq, Serialize)]
26+
pub struct RetractableFactOrigin {
27+
pub block_number: u32,
28+
pub block_hash: Field,
29+
pub block_state: OriginBlockState,
30+
}
31+
2132
/// A single immutable fact in a collection.
2233
#[derive(Deserialize, Serialize)]
2334
pub struct Fact {
@@ -27,7 +38,7 @@ pub struct Fact {
2738
/// The block the fact is associated to, if any. A fact with an origin block is said to be a 'retractable' fact,
2839
/// and will be automatically deleted if its origin block gets pruned in a reorg. Typically used by facts
2940
/// associated with a transaction (e.g. 'processed entry X using data from tx Y in block Z').
30-
pub origin_block: Option<OriginBlock>,
41+
pub origin_block: Option<RetractableFactOrigin>,
3142
}
3243

3344
/// A fact collection as returned by the store.
@@ -219,8 +230,9 @@ mod test {
219230
}
220231

221232
#[test]
222-
unconstrained fn records_a_retractable_fact() {
233+
unconstrained fn records_a_retractable_fact_finalized_at_latest_block() {
223234
let (env, scope) = setup();
235+
let origin_block_number = env.last_block_number();
224236
env.private_context(|context| {
225237
let contract_address = context.this_address();
226238
record_retractable_fact(
@@ -230,7 +242,7 @@ mod test {
230242
COLLECTION_ID,
231243
FACT_TYPE_ID,
232244
make_payload(PAYLOAD),
233-
OriginBlock { block_number: 5, block_hash: 0xabc },
245+
OriginBlock { block_number: origin_block_number, block_hash: 0xabc },
234246
);
235247

236248
let collection = get_fact_collection(contract_address, scope, TYPE_ID, COLLECTION_ID).unwrap();
@@ -240,9 +252,37 @@ mod test {
240252
assert_eq(fact.fact_type_id, FACT_TYPE_ID);
241253
assert_payload(fact.payload);
242254

243-
let origin_block = fact.origin_block.unwrap();
244-
assert_eq(origin_block.block_number, 5);
245-
assert_eq(origin_block.block_hash, 0xabc);
255+
let origin = fact.origin_block.unwrap();
256+
assert_eq(origin.block_number, origin_block_number);
257+
assert_eq(origin.block_hash, 0xabc);
258+
// TXE finalizes every mined block, so an origin at the latest block is Finalized.
259+
assert(origin.block_state.is_finalized());
260+
});
261+
}
262+
263+
#[test]
264+
unconstrained fn retractable_fact_above_proven_tip_is_pending() {
265+
let (env, scope) = setup();
266+
let future_block_number = env.last_block_number() + 100;
267+
env.private_context(|context| {
268+
let contract_address = context.this_address();
269+
record_retractable_fact(
270+
contract_address,
271+
scope,
272+
TYPE_ID,
273+
COLLECTION_ID,
274+
FACT_TYPE_ID,
275+
make_payload(PAYLOAD),
276+
OriginBlock { block_number: future_block_number, block_hash: 0xabc },
277+
);
278+
279+
let origin = get_fact_collection(contract_address, scope, TYPE_ID, COLLECTION_ID)
280+
.unwrap()
281+
.facts
282+
.get(0)
283+
.origin_block
284+
.unwrap();
285+
assert(origin.block_state.is_pending());
246286
});
247287
}
248288

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use crate::protocol::{traits::{Deserialize, Serialize}, utils::reader::Reader};
2+
3+
/// Chain state of a retractable fact's origin block, mirroring the L2 chain tips.
4+
#[derive(Serialize)]
5+
pub struct OriginBlockState {
6+
// Noir has no enums, so this wraps a `u8` discriminant. The discriminant values must stay in sync with the
7+
// TypeScript `OriginBlockState` enum in `yarn-project/pxe/src/storage/fact_store/origin_state.ts`, which PXE
8+
// serializes into the `Fact` oracle response that `from_u8` below deserializes.
9+
inner: u8,
10+
}
11+
12+
impl OriginBlockState {
13+
pub fn pending() -> Self {
14+
Self { inner: 1 }
15+
}
16+
17+
pub fn proven() -> Self {
18+
Self { inner: 2 }
19+
}
20+
21+
pub fn finalized() -> Self {
22+
Self { inner: 3 }
23+
}
24+
25+
/// Whether the origin block is still above the proven tip.
26+
pub fn is_pending(self) -> bool {
27+
self == Self::pending()
28+
}
29+
30+
/// Whether the origin block is proven but not yet finalized.
31+
pub fn is_proven(self) -> bool {
32+
self == Self::proven()
33+
}
34+
35+
/// Whether the origin block is finalized, and thus facts related to it can no longer be retracted.
36+
pub fn is_finalized(self) -> bool {
37+
self == Self::finalized()
38+
}
39+
40+
fn from_u8(inner: u8) -> Self {
41+
let state = Self { inner };
42+
assert(state.is_valid(), "unrecognized origin state");
43+
state
44+
}
45+
46+
fn is_valid(self) -> bool {
47+
self.is_pending() | self.is_proven() | self.is_finalized()
48+
}
49+
}
50+
51+
impl Eq for OriginBlockState {
52+
fn eq(self, other: Self) -> bool {
53+
self.inner == other.inner
54+
}
55+
}
56+
57+
impl Deserialize for OriginBlockState {
58+
let N: u32 = <u8 as Deserialize>::N;
59+
60+
fn deserialize(fields: [Field; Self::N]) -> Self {
61+
Self::from_u8(<u8 as Deserialize>::deserialize(fields))
62+
}
63+
64+
fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {
65+
Self::from_u8(reader.read() as u8)
66+
}
67+
}
68+
69+
mod test {
70+
use crate::protocol::traits::{Deserialize, Serialize};
71+
use super::OriginBlockState;
72+
73+
#[test]
74+
fn query_functions_identify_each_state() {
75+
assert(OriginBlockState::pending().is_pending());
76+
assert(!OriginBlockState::pending().is_proven());
77+
assert(!OriginBlockState::pending().is_finalized());
78+
79+
assert(OriginBlockState::proven().is_proven());
80+
assert(!OriginBlockState::proven().is_pending());
81+
assert(!OriginBlockState::proven().is_finalized());
82+
83+
assert(OriginBlockState::finalized().is_finalized());
84+
assert(!OriginBlockState::finalized().is_pending());
85+
assert(!OriginBlockState::finalized().is_proven());
86+
}
87+
88+
#[test]
89+
fn origin_state_roundtrips_through_serialization() {
90+
let pending = OriginBlockState::pending();
91+
let proven = OriginBlockState::proven();
92+
let finalized = OriginBlockState::finalized();
93+
assert(OriginBlockState::deserialize(pending.serialize()) == pending);
94+
assert(OriginBlockState::deserialize(proven.serialize()) == proven);
95+
assert(OriginBlockState::deserialize(finalized.serialize()) == finalized);
96+
}
97+
98+
#[test(should_fail_with = "unrecognized origin state")]
99+
fn deserializing_invalid_state_fails() {
100+
let _ = OriginBlockState::deserialize([99]);
101+
}
102+
}

noir-projects/aztec-nr/aztec/src/messages/delivery/handshake.nr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ pub(crate) unconstrained fn get_existing_app_siloed_handshake_secrets(
8181
/// Establishes a non-interactive handshake for `(sender, recipient)` and returns its app-siloed secrets.
8282
///
8383
/// The registry inserts a fresh handshake note and returns the app-siloed secrets. The constrained return value is the
84-
/// source of truth for the secrets, so constrained delivery anchoring a freshly bootstrapped handshake needs no separate
85-
/// `validate_handshake`.
84+
/// source of truth for the secrets, so constrained delivery anchoring a freshly bootstrapped handshake needs no
85+
/// separate `validate_handshake`.
8686
///
8787
/// Any handshake already registered for the pair is overwritten.
8888
pub(crate) fn create_non_interactive_handshake(

noir-projects/aztec-nr/aztec/src/oracle/message_processing.nr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ pub unconstrained fn get_logs_by_tag(
5454
get_logs_by_tag_oracle(requests)
5555
}
5656

57+
// TODO: PXE also exposes `getLogsByTagV2`, whose response additionally carries the origin block number and timestamp
58+
// of each log. Switch this oracle (and `LogRetrievalResponse`) over to it once a consumer needs that context.
5759
#[oracle(aztec_utl_getLogsByTag)]
5860
unconstrained fn get_logs_by_tag_oracle(
5961
requests: EphemeralArray<LogRetrievalRequest>,

noir-projects/aztec-nr/aztec/src/oracle/version.nr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
/// immediately if AZTEC_NR_MINOR > PXE_MINOR because if a contract is updated to use a newer Aztec.nr dependency
1212
/// without actually using any of the new oracles then there is no reason to throw.
1313
pub global ORACLE_VERSION_MAJOR: Field = 30;
14-
pub global ORACLE_VERSION_MINOR: Field = 2;
14+
pub global ORACLE_VERSION_MINOR: Field = 3;
1515

1616
/// Asserts that the version of the oracle is compatible with the version expected by the contract.
1717
pub fn assert_compatible_oracle_version() {

noir-projects/contract-snapshots/tests/snapshots/expand/amm_contract/snapshots__expanded.snap

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,7 +1145,7 @@ pub contract AMM {
11451145
returns_hash.get_preimage()
11461146
}
11471147

1148-
pub fn swap_tokens_for_exact_tokens(self, token_in: AztecAddress, token_out: AztecAddress, amount_out: u128, amount_in_max: u128, authwit_nonce: Field) {
1148+
pub fn swap_exact_tokens_for_tokens(self, token_in: AztecAddress, token_out: AztecAddress, amount_in: u128, amount_out_min: u128, authwit_nonce: Field) {
11491149
let mut serialized_params: [Field; 5] = [0_Field; 5];
11501150
let mut offset: u32 = 0_u32;
11511151
let serialized_member: [Field; 1] = <AztecAddress as aztec::protocol::traits::Serialize>::serialize(token_in);
@@ -1160,13 +1160,13 @@ pub contract AMM {
11601160
serialized_params[i + offset] = serialized_member[i];
11611161
};
11621162
offset = offset + serialized_member_len;
1163-
let serialized_member: [Field; 1] = <u128 as aztec::protocol::traits::Serialize>::serialize(amount_out);
1163+
let serialized_member: [Field; 1] = <u128 as aztec::protocol::traits::Serialize>::serialize(amount_in);
11641164
let serialized_member_len: u32 = <u128 as aztec::protocol::traits::Serialize>::N;
11651165
for i in 0_u32..serialized_member_len {
11661166
serialized_params[i + offset] = serialized_member[i];
11671167
};
11681168
offset = offset + serialized_member_len;
1169-
let serialized_member: [Field; 1] = <u128 as aztec::protocol::traits::Serialize>::serialize(amount_in_max);
1169+
let serialized_member: [Field; 1] = <u128 as aztec::protocol::traits::Serialize>::serialize(amount_out_min);
11701170
let serialized_member_len: u32 = <u128 as aztec::protocol::traits::Serialize>::N;
11711171
for i in 0_u32..serialized_member_len {
11721172
serialized_params[i + offset] = serialized_member[i];
@@ -1178,14 +1178,14 @@ pub contract AMM {
11781178
serialized_params[i + offset] = serialized_member[i];
11791179
};
11801180
offset = offset + serialized_member_len;
1181-
let selector: aztec::protocol::abis::function_selector::FunctionSelector = <aztec::protocol::abis::function_selector::FunctionSelector as aztec::protocol::traits::FromField>::from_field(2620890703_Field);
1181+
let selector: aztec::protocol::abis::function_selector::FunctionSelector = <aztec::protocol::abis::function_selector::FunctionSelector as aztec::protocol::traits::FromField>::from_field(2960373586_Field);
11821182
let args_hash: Field = aztec::hash::hash_args(serialized_params);
11831183
aztec::oracle::execution_cache::store(serialized_params, args_hash);
11841184
let returns_hash: aztec::context::ReturnsHash = self.context.call_private_function_with_args_hash(self.address, selector, args_hash, false);
11851185
returns_hash.get_preimage()
11861186
}
11871187

1188-
pub fn swap_exact_tokens_for_tokens(self, token_in: AztecAddress, token_out: AztecAddress, amount_in: u128, amount_out_min: u128, authwit_nonce: Field) {
1188+
pub fn swap_tokens_for_exact_tokens(self, token_in: AztecAddress, token_out: AztecAddress, amount_out: u128, amount_in_max: u128, authwit_nonce: Field) {
11891189
let mut serialized_params: [Field; 5] = [0_Field; 5];
11901190
let mut offset: u32 = 0_u32;
11911191
let serialized_member: [Field; 1] = <AztecAddress as aztec::protocol::traits::Serialize>::serialize(token_in);
@@ -1200,13 +1200,13 @@ pub contract AMM {
12001200
serialized_params[i + offset] = serialized_member[i];
12011201
};
12021202
offset = offset + serialized_member_len;
1203-
let serialized_member: [Field; 1] = <u128 as aztec::protocol::traits::Serialize>::serialize(amount_in);
1203+
let serialized_member: [Field; 1] = <u128 as aztec::protocol::traits::Serialize>::serialize(amount_out);
12041204
let serialized_member_len: u32 = <u128 as aztec::protocol::traits::Serialize>::N;
12051205
for i in 0_u32..serialized_member_len {
12061206
serialized_params[i + offset] = serialized_member[i];
12071207
};
12081208
offset = offset + serialized_member_len;
1209-
let serialized_member: [Field; 1] = <u128 as aztec::protocol::traits::Serialize>::serialize(amount_out_min);
1209+
let serialized_member: [Field; 1] = <u128 as aztec::protocol::traits::Serialize>::serialize(amount_in_max);
12101210
let serialized_member_len: u32 = <u128 as aztec::protocol::traits::Serialize>::N;
12111211
for i in 0_u32..serialized_member_len {
12121212
serialized_params[i + offset] = serialized_member[i];
@@ -1218,7 +1218,7 @@ pub contract AMM {
12181218
serialized_params[i + offset] = serialized_member[i];
12191219
};
12201220
offset = offset + serialized_member_len;
1221-
let selector: aztec::protocol::abis::function_selector::FunctionSelector = <aztec::protocol::abis::function_selector::FunctionSelector as aztec::protocol::traits::FromField>::from_field(2960373586_Field);
1221+
let selector: aztec::protocol::abis::function_selector::FunctionSelector = <aztec::protocol::abis::function_selector::FunctionSelector as aztec::protocol::traits::FromField>::from_field(2620890703_Field);
12221222
let args_hash: Field = aztec::hash::hash_args(serialized_params);
12231223
aztec::oracle::execution_cache::store(serialized_params, args_hash);
12241224
let returns_hash: aztec::context::ReturnsHash = self.context.call_private_function_with_args_hash(self.address, selector, args_hash, false);

0 commit comments

Comments
 (0)