Skip to content

Commit a8df51f

Browse files
committed
feat: store discovered handshakes as facts
1 parent 4057d0b commit a8df51f

7 files changed

Lines changed: 199 additions & 48 deletions

File tree

noir-projects/aztec-nr/aztec/src/messages/processing/log_retrieval_response.nr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,8 @@ pub struct LogRetrievalResponse {
1919
pub unique_note_hashes_in_tx: BoundedVec<Field, MAX_NOTE_HASHES_PER_TX>,
2020
/// The first nullifier created by `tx_hash`
2121
pub first_nullifier_in_tx: Field,
22+
/// The block in which `tx_hash` was included
23+
pub block_number: u32,
24+
/// The timestamp of the block in which `tx_hash` was included
25+
pub block_timestamp: u64,
2226
}

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,7 @@ 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.
59-
#[oracle(aztec_utl_getLogsByTag)]
57+
#[oracle(aztec_utl_getLogsByTagV2)]
6058
unconstrained fn get_logs_by_tag_oracle(
6159
requests: EphemeralArray<LogRetrievalRequest>,
6260
) -> EphemeralArray<EphemeralArray<LogRetrievalResponse>> {}

noir-projects/aztec-nr/aztec/src/standard_addresses.nr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22
use protocol_types::{address::AztecAddress, traits::FromField};
33

44
pub global STANDARD_AUTH_REGISTRY_ADDRESS: AztecAddress = AztecAddress::from_field(
5-
0x186fbbe15f50d01d67c471d5ed8b8be43b8826e77147cb0084302804e336e5d2,
5+
0x2e5ddde017507a7faa104e9a26584bfcfb91873412ac912e127fc0280e96d44f,
66
);
77

88
pub global STANDARD_MULTI_CALL_ENTRYPOINT_ADDRESS: AztecAddress = AztecAddress::from_field(
9-
0x10a91e72a359e7441d4af9e418f14862abe994348970909d484ff2dad1594a58,
9+
0x19cd5344d6da9545c5bed85045f33c3db45abd5bbb2ca54b110bd29f53503d70,
1010
);
1111

1212
pub global STANDARD_PUBLIC_CHECKS_ADDRESS: AztecAddress = AztecAddress::from_field(
13-
0x1f6f565db58df81c2bbb8e9932fa92d2d1f537887a06e2cb965184f77a5235d2,
13+
0x2af70394e3fe40203bc3271bc8e1ad9d6e8c9ffb547a354aaab7f1aca63c8697,
1414
);
1515

1616
pub global STANDARD_HANDSHAKE_REGISTRY_ADDRESS: AztecAddress = AztecAddress::from_field(
17-
0x18233660a8797d74f63318c4223332e23b63f570da11fb142993ebc802a96728,
17+
0x171fcd1d024cc5b3273945e50d7e6eb715dab71f2e13cb6dfc49a3ca6fc8642f,
1818
);

noir-projects/noir-contracts/contracts/standard/handshake_registry_contract/src/sync.nr

Lines changed: 122 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use aztec::{
2-
capsules::CapsuleArray,
32
context::UtilityContext,
43
ephemeral::EphemeralArray,
4+
facts::{
5+
delete_fact_collection, Fact, FactCollection, get_fact_collection, get_fact_collections_by_type, OriginBlock,
6+
record_retractable_fact,
7+
},
58
messages::{
69
delivery::handshake::DiscoveredHandshake,
710
discovery::{
@@ -14,22 +17,34 @@ use aztec::{
1417
offchain::OffchainInboxSync,
1518
},
1619
},
17-
oracle::{capsules, message_processing},
20+
oracle::message_processing,
1821
protocol::{
1922
address::AztecAddress,
2023
constants::DOM_SEP__NON_INTERACTIVE_HANDSHAKE_LOG_TAG,
21-
hash::{compute_log_tag, sha256_to_field},
22-
traits::ToField,
24+
hash::{compute_log_tag, poseidon2_hash, sha256_to_field},
25+
traits::{Deserialize, Serialize, ToField},
2326
},
2427
utils::point::point_from_x_coord_and_sign,
2528
};
2629

27-
/// Capsule base slot for the per-recipient list of discovered handshakes populated by
28-
/// [`handshake_registry_sync`] and read by [`get_all_handshakes`].
29-
global HANDSHAKES_CAPSULE_SLOT: Field = sha256_to_field("HANDSHAKE_REGISTRY::HANDSHAKES".as_bytes());
30+
/// Fact-collection type id of the per-recipient discovered handshakes populated by [`handshake_registry_sync`] and
31+
/// read by [`get_all_handshakes`]. Each discovered handshake lives in its own collection (see
32+
/// [`handshake_collection_id`]).
33+
pub(crate) global DISCOVERED_HANDSHAKES_TYPE_ID: Field =
34+
sha256_to_field("HANDSHAKE_REGISTRY::DISCOVERED_HANDSHAKES".as_bytes());
35+
36+
/// Fact type id of the fact storing a serialized [`DiscoveredHandshake`] inside a discovered-handshake collection.
37+
pub(crate) global HANDSHAKE_DISCOVERED: Field = sha256_to_field("HANDSHAKE_REGISTRY::HANDSHAKE_DISCOVERED".as_bytes());
38+
39+
/// Fact-collection type id of the per-recipient sync cursor.
40+
pub(crate) global SYNC_CURSOR_TYPE_ID: Field = sha256_to_field("HANDSHAKE_REGISTRY::SYNC_CURSOR".as_bytes());
3041

31-
/// Capsule slot for the per-recipient sync cursor: the next block (inclusive) the discovery scan should start from.
32-
global SYNC_CURSOR_CAPSULE_SLOT: Field = sha256_to_field("HANDSHAKE_REGISTRY::SYNC_CURSOR".as_bytes());
42+
/// Fact type id of the single fact in the cursor collection. The fact has an empty payload: the cursor value is its
43+
/// origin block (the anchor block of the last completed scan).
44+
pub(crate) global SYNC_CURSOR: Field = sha256_to_field("HANDSHAKE_REGISTRY::SYNC_CURSOR_FACT".as_bytes());
45+
46+
/// Collection id of the singleton cursor collection.
47+
pub(crate) global SYNC_CURSOR_COLLECTION_ID: Field = 0;
3348

3449
/// Ephemeral-array slot used by [`get_handshake_logs_in_range`] to batch a single log-retrieval request to the oracle.
3550
global LOG_RETRIEVAL_REQUEST_ARRAY_SLOT: Field =
@@ -38,11 +53,11 @@ global LOG_RETRIEVAL_REQUEST_ARRAY_SLOT: Field =
3853
/// Discovers non-interactive handshakes addressed to the syncing account.
3954
///
4055
/// On every sync, scans the recipient-keyed bootstrap tag over the blocks mined since the previous sync, decrypts
41-
/// each matching log to recover the handshake `eph_pk`, and appends it to the recipient's
42-
/// capsule list. It then falls through to [`do_sync_state`] so that normal note/event/partial-note discovery still
43-
/// happens.
56+
/// each matching log to recover the handshake `eph_pk`, and records it as a retractable fact whose origin is the block
57+
/// the log landed in, so a reorg pruning that block makes the handshake invisible again. It then falls through to
58+
/// [`do_sync_state`] so that normal note/event/partial-note discovery still happens.
4459
///
45-
/// `scope` is the account PXE is syncing for, which doubles as both the recipient whose tag we scan and the capsule
60+
/// `scope` is the account PXE is syncing for, which doubles as both the recipient whose tag we scan and the fact
4661
/// scope we persist under, so handshakes for one account never leak into another's list.
4762
pub(crate) unconstrained fn handshake_registry_sync(
4863
contract_address: AztecAddress,
@@ -52,7 +67,7 @@ pub(crate) unconstrained fn handshake_registry_sync(
5267
offchain_inbox_sync: Option<OffchainInboxSync>,
5368
scope: AztecAddress,
5469
) {
55-
let cursor = capsules::load::<u32>(contract_address, SYNC_CURSOR_CAPSULE_SLOT, scope);
70+
let cursor = load_sync_cursor(contract_address, scope);
5671
let anchor_block = UtilityContext::new().block_number();
5772

5873
// `to_block` is exclusive, so we add 1 to include the anchor block in the scan
@@ -63,26 +78,18 @@ pub(crate) unconstrained fn handshake_registry_sync(
6378
let tag = compute_log_tag(scope.to_field(), DOM_SEP__NON_INTERACTIVE_HANDSHAKE_LOG_TAG);
6479
let logs = get_handshake_logs_in_range(contract_address, tag, cursor, Option::some(next_block));
6580

66-
// Store all valid handshakes
67-
let handshakes: CapsuleArray<DiscoveredHandshake> =
68-
CapsuleArray::at(contract_address, HANDSHAKES_CAPSULE_SLOT, scope);
81+
// Record all valid handshakes
6982
logs.for_each(|_i, log| {
7083
let ciphertext = aztec::utils::array::subbvec(log.log_payload, 0);
7184

7285
let _ = AES128::decrypt(ciphertext, scope, contract_address)
7386
.and_then(|pt| {
7487
point_from_x_coord_and_sign(pt.get(0), true).map(|pk| DiscoveredHandshake { eph_pk: pk })
7588
})
76-
.map(|h| handshakes.push(h));
89+
.map(|h| record_discovered_handshake(contract_address, scope, h, log.block_number));
7790
});
7891

79-
// Update the cursor
80-
capsules::store(
81-
contract_address,
82-
SYNC_CURSOR_CAPSULE_SLOT,
83-
next_block,
84-
scope,
85-
);
92+
store_sync_cursor(contract_address, scope, anchor_block);
8693
}
8794

8895
do_sync_state_without_handshake_discovery(
@@ -95,12 +102,99 @@ pub(crate) unconstrained fn handshake_registry_sync(
95102
);
96103
}
97104

98-
/// Returns the capsule array of discovered handshakes for `recipient`.
105+
/// Returns the discovered handshakes for `recipient`, one per canonical handshake fact collection.
106+
///
107+
/// Ordering is unspecified.
99108
pub(crate) unconstrained fn get_all_handshakes(
100109
contract_address: AztecAddress,
101110
recipient: AztecAddress,
102-
) -> CapsuleArray<DiscoveredHandshake> {
103-
CapsuleArray::at(contract_address, HANDSHAKES_CAPSULE_SLOT, recipient)
111+
) -> EphemeralArray<DiscoveredHandshake> {
112+
get_fact_collections_by_type(contract_address, recipient, DISCOVERED_HANDSHAKES_TYPE_ID)
113+
.map(|collection: FactCollection| {
114+
let fact = collection.facts.find(|f: Fact| f.fact_type_id == HANDSHAKE_DISCOVERED).unwrap();
115+
handshake_from_payload(fact.payload)
116+
})
117+
}
118+
119+
/// Records a discovered handshake as a retractable fact originated at the block its announcement log landed in.
120+
///
121+
/// The collection id is a hash of the handshake, so re-recording the same handshake at the same origin (e.g. when
122+
/// rescanning after the cursor was retracted by a reorg) is a no-op, and the same `eph_pk` can never appear twice.
123+
unconstrained fn record_discovered_handshake(
124+
contract_address: AztecAddress,
125+
scope: AztecAddress,
126+
handshake: DiscoveredHandshake,
127+
block_number: u32,
128+
) {
129+
record_retractable_fact(
130+
contract_address,
131+
scope,
132+
DISCOVERED_HANDSHAKES_TYPE_ID,
133+
handshake_collection_id(handshake),
134+
HANDSHAKE_DISCOVERED,
135+
handshake_to_payload(handshake),
136+
// The log retrieval oracle intentionally exposes only the origin block number, and FactStore retraction and
137+
// origin-state classification key on block number alone, so no real hash is available or needed.
138+
OriginBlock { block_number, block_hash: 0 },
139+
);
140+
}
141+
142+
/// Computes the fact-collection id identifying `handshake` in the [fact store](aztec::facts).
143+
fn handshake_collection_id(handshake: DiscoveredHandshake) -> Field {
144+
poseidon2_hash(handshake.serialize())
145+
}
146+
147+
/// Serializes a [`DiscoveredHandshake`] into a fact payload.
148+
unconstrained fn handshake_to_payload(handshake: DiscoveredHandshake) -> EphemeralArray<Field> {
149+
let fields = handshake.serialize();
150+
let payload: EphemeralArray<Field> = EphemeralArray::empty();
151+
for i in 0..fields.len() {
152+
payload.push(fields[i]);
153+
}
154+
payload
155+
}
156+
157+
/// Deserializes a [`DiscoveredHandshake`] from a fact payload.
158+
unconstrained fn handshake_from_payload(payload: EphemeralArray<Field>) -> DiscoveredHandshake {
159+
let n = <DiscoveredHandshake as Deserialize>::N;
160+
let mut fields = [0; <DiscoveredHandshake as Deserialize>::N];
161+
for i in 0..n {
162+
fields[i] = payload.get(i);
163+
}
164+
Deserialize::deserialize(fields)
165+
}
166+
167+
/// Returns the next block (inclusive) the discovery scan should start from, or `None` if no canonical cursor exists,
168+
/// either because this scope never synced or because the cursor fact's origin block was reorg'd away. In the latter
169+
/// case the caller rescans the full range and fact idempotency deduplicates re-discovered handshakes.
170+
unconstrained fn load_sync_cursor(contract_address: AztecAddress, scope: AztecAddress) -> Option<u32> {
171+
get_fact_collection(
172+
contract_address,
173+
scope,
174+
SYNC_CURSOR_TYPE_ID,
175+
SYNC_CURSOR_COLLECTION_ID,
176+
)
177+
.map(|collection: FactCollection| collection.facts.get(0).origin_block.unwrap().block_number + 1)
178+
}
179+
180+
/// Replaces the cursor fact with one originated at `anchor_block` (the block the completed scan ran against), so a
181+
/// reorg pruning any scanned block also retracts the cursor and the next sync rescans.
182+
unconstrained fn store_sync_cursor(contract_address: AztecAddress, scope: AztecAddress, anchor_block: u32) {
183+
delete_fact_collection(
184+
contract_address,
185+
scope,
186+
SYNC_CURSOR_TYPE_ID,
187+
SYNC_CURSOR_COLLECTION_ID,
188+
);
189+
record_retractable_fact(
190+
contract_address,
191+
scope,
192+
SYNC_CURSOR_TYPE_ID,
193+
SYNC_CURSOR_COLLECTION_ID,
194+
SYNC_CURSOR,
195+
EphemeralArray::empty(),
196+
OriginBlock { block_number: anchor_block, block_hash: 0 },
197+
);
104198
}
105199

106200
/// Fetches all private logs emitted under `tag` in the block range `[from_block, to_block)`.

noir-projects/noir-contracts/contracts/standard/handshake_registry_contract/src/test.nr

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::HandshakeRegistry;
2+
use crate::sync::{DISCOVERED_HANDSHAKES_TYPE_ID, SYNC_CURSOR_COLLECTION_ID, SYNC_CURSOR_TYPE_ID};
23

34
use aztec::{
5+
facts::{delete_fact_collection, get_fact_collections_by_type},
46
messages::delivery::{
57
constrained_delivery::VALIDATE_HANDSHAKE_SELECTOR,
68
handshake::{
@@ -485,7 +487,7 @@ unconstrained fn repeated_sync_does_not_refetch_logs() {
485487
assert_eq(after_first.items.len(), 1);
486488

487489
// Mock the log-retrieval oracle so we can assert it is never called on re-sync.
488-
let log_oracle = OracleMock::mock("aztec_utl_getLogsByTag");
490+
let log_oracle = OracleMock::mock("aztec_utl_getLogsByTagV2");
489491
let _ = env.execute_utility(registry.get_handshakes(recipient, 0));
490492
assert_eq(log_oracle.times_called(), 0);
491493
log_oracle.clear();
@@ -554,3 +556,56 @@ unconstrained fn get_handshakes_out_of_bounds_offset_returns_empty() {
554556
assert_eq(page.items.len(), 0);
555557
assert_eq(page.total_count, 1);
556558
}
559+
560+
// The cursor fact is retractable: a reorg pruning its origin block deletes it, forcing the next sync to rescan the
561+
// full range. Simulate that state by deleting the cursor collection and verify the rescan does not duplicate
562+
// handshakes whose facts survived (they are content-addressed, so re-recording them is a no-op).
563+
#[test]
564+
unconstrained fn rescan_after_cursor_retraction_does_not_duplicate_handshakes() {
565+
let (env, registry_address, sender, _, recipient) = setup();
566+
let registry = HandshakeRegistry::at(registry_address);
567+
568+
let _ = env.call_private(sender, registry.non_interactive_handshake(sender, recipient));
569+
570+
let after_first = env.execute_utility(registry.get_handshakes(recipient, 0));
571+
assert_eq(after_first.items.len(), 1);
572+
573+
env.utility_context_at(registry_address, |_| delete_fact_collection(
574+
registry_address,
575+
recipient,
576+
SYNC_CURSOR_TYPE_ID,
577+
SYNC_CURSOR_COLLECTION_ID,
578+
));
579+
// Advance the anchor so a fact wrongly originated at the sync anchor would get a distinct origin and duplicate.
580+
env.mine_block();
581+
582+
let after_rescan = env.execute_utility(registry.get_handshakes(recipient, 0));
583+
assert_eq(after_rescan.items.len(), 1);
584+
assert_eq(after_rescan.items.get(0).eph_pk.x, after_first.items.get(0).eph_pk.x);
585+
}
586+
587+
// The handshake fact must originate at the block its announcement log landed in (not the sync anchor) so a reorg
588+
// retracts exactly the handshakes carried by pruned blocks.
589+
#[test]
590+
unconstrained fn handshake_fact_originates_at_the_log_block() {
591+
let (env, registry_address, sender, _, recipient) = setup();
592+
let registry = HandshakeRegistry::at(registry_address);
593+
594+
let _ = env.call_private(sender, registry.non_interactive_handshake(sender, recipient));
595+
let handshake_block = env.last_block_number();
596+
597+
// Advance the chain so the sync anchor is later than the block carrying the handshake log.
598+
env.mine_block();
599+
env.mine_block();
600+
601+
let discovered = env.execute_utility(registry.get_handshakes(recipient, 0));
602+
assert_eq(discovered.items.len(), 1);
603+
604+
// Ephemeral arrays are frame-scoped, so read the fact and extract the origin inside the registry's context.
605+
let origin_block_number = env.utility_context_at(registry_address, |_| {
606+
let collections = get_fact_collections_by_type(registry_address, recipient, DISCOVERED_HANDSHAKES_TYPE_ID);
607+
assert_eq(collections.len(), 1);
608+
collections.get(0).facts.get(0).origin_block.unwrap().block_number
609+
});
610+
assert_eq(origin_block_number, handshake_block);
611+
}
11.4 KB
Binary file not shown.

yarn-project/standard-contracts/src/standard_contract_data.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,44 +20,44 @@ export const StandardContractSalt: Record<StandardContractName, Fr> = {
2020
};
2121

2222
export const StandardContractAddress: Record<StandardContractName, AztecAddress> = {
23-
AuthRegistry: AztecAddress.fromStringUnsafe('0x186fbbe15f50d01d67c471d5ed8b8be43b8826e77147cb0084302804e336e5d2'),
23+
AuthRegistry: AztecAddress.fromStringUnsafe('0x2e5ddde017507a7faa104e9a26584bfcfb91873412ac912e127fc0280e96d44f'),
2424
MultiCallEntrypoint: AztecAddress.fromStringUnsafe(
25-
'0x10a91e72a359e7441d4af9e418f14862abe994348970909d484ff2dad1594a58',
25+
'0x19cd5344d6da9545c5bed85045f33c3db45abd5bbb2ca54b110bd29f53503d70',
2626
),
27-
PublicChecks: AztecAddress.fromStringUnsafe('0x1f6f565db58df81c2bbb8e9932fa92d2d1f537887a06e2cb965184f77a5235d2'),
27+
PublicChecks: AztecAddress.fromStringUnsafe('0x2af70394e3fe40203bc3271bc8e1ad9d6e8c9ffb547a354aaab7f1aca63c8697'),
2828
HandshakeRegistry: AztecAddress.fromStringUnsafe(
29-
'0x18233660a8797d74f63318c4223332e23b63f570da11fb142993ebc802a96728',
29+
'0x171fcd1d024cc5b3273945e50d7e6eb715dab71f2e13cb6dfc49a3ca6fc8642f',
3030
),
3131
};
3232

3333
export const StandardContractClassId: Record<StandardContractName, Fr> = {
34-
AuthRegistry: Fr.fromString('0x1684ac4f8c250f8ecc1c7629a344fa36002f5130d3b1bbe6e2c0e73e14640f0a'),
35-
MultiCallEntrypoint: Fr.fromString('0x003a615f16c5401ef184876baf2aa54f67ff693672a03134cc8920cfb58957c7'),
36-
PublicChecks: Fr.fromString('0x086e79a9008498c03a83dd1e9b69b6cbe8d1fafe31b2a775d0b6610dac7d681c'),
37-
HandshakeRegistry: Fr.fromString('0x1007fb490420810f54215128260e808c7ab2b1dc4c1801156833d4720696bf31'),
34+
AuthRegistry: Fr.fromString('0x2f6b1e4de76f5082a87a416ef1e0d87cdb43bd01bdbce95c5b8e0a9ecab592ad'),
35+
MultiCallEntrypoint: Fr.fromString('0x1d1bf60ebb7e3d4fd38eb0de5bfe71ced78f47533fb6fd2b07c1fa1d430463c7'),
36+
PublicChecks: Fr.fromString('0x0b018b124a3757f765634cb805914c5af46bec0bd91647f617f1056a5508460f'),
37+
HandshakeRegistry: Fr.fromString('0x096b9c0fa53c09a8969471b5b78d5812385bd056650febc03852bf80724ea30a'),
3838
};
3939

4040
export const StandardContractClassIdPreimage: Record<
4141
StandardContractName,
4242
{ artifactHash: Fr; privateFunctionsRoot: Fr; publicBytecodeCommitment: Fr }
4343
> = {
4444
AuthRegistry: {
45-
artifactHash: Fr.fromString('0x175607a9b31428948f4041db7d4e1667492292fb7850f8138c1d3cf5ffde8b34'),
45+
artifactHash: Fr.fromString('0x1e0d42f3e2cc6a510110e2ddafd909691038754134610e6306813d34240bc176'),
4646
privateFunctionsRoot: Fr.fromString('0x17b584350f4c3ccafd8f688729afb9feab8976114fb40012e9dee65022c072a4'),
4747
publicBytecodeCommitment: Fr.fromString('0x2545f39893766508ce37bb5cea5e4dcab04c6f7f79f3089b1c076876e9d268b2'),
4848
},
4949
MultiCallEntrypoint: {
50-
artifactHash: Fr.fromString('0x23af448187164c7d5e6f392346958e688690370515359de8909f3f05f2e26760'),
50+
artifactHash: Fr.fromString('0x2377af88372bbb2961478e296d4c888aa3748b8e8606d282fcadd745308f8444'),
5151
privateFunctionsRoot: Fr.fromString('0x0e68dfbb256e80b08b3aef47aca1f2669e97a9c6259787893c1223ac083ad5d5'),
5252
publicBytecodeCommitment: Fr.fromString('0x0ce4c618c3ed7f3a20410e618c06bb701e150af7fe28a3e92f68e7733809f33e'),
5353
},
5454
PublicChecks: {
55-
artifactHash: Fr.fromString('0x2f3869d9d89bf7b428da7f8c620aea11acc022cc0c7222544cc841e5ceedd8dc'),
55+
artifactHash: Fr.fromString('0x0ab0b0a0fdf8b8646b7a1a574da6691f3ddf82d07ad55053e41fa0c5a37971ff'),
5656
privateFunctionsRoot: Fr.fromString('0x202860adb1b8975971eeaf571aaaa88a27f4035290d58532ae7d60b0dfaad54c'),
5757
publicBytecodeCommitment: Fr.fromString('0x013c4f854a5c87c9daf86c5f9bc07a42c2a061f1d924a5b3564ec7edc8e18cb7'),
5858
},
5959
HandshakeRegistry: {
60-
artifactHash: Fr.fromString('0x080bd5c37240f9425fea5c87b844a67ffda312dbf58c58288b7ba6b4d5db7663'),
60+
artifactHash: Fr.fromString('0x1ad17c0563153f1d495e906b5e6dbee256ff0d9a51c65c7dc48327247d2b2758'),
6161
privateFunctionsRoot: Fr.fromString('0x14fed008fa16ae6e45b7561aa0101d0d97953e2fd86caf7aaa603f14807797a8'),
6262
publicBytecodeCommitment: Fr.fromString('0x0ce4c618c3ed7f3a20410e618c06bb701e150af7fe28a3e92f68e7733809f33e'),
6363
},

0 commit comments

Comments
 (0)