Skip to content

Commit 50a4b6c

Browse files
committed
Make the compliance circuit output a digest of the instance instead of the instance itself.
1 parent bb40771 commit 50a4b6c

5 files changed

Lines changed: 5290 additions & 9 deletions

File tree

circuits/crates/compliance/src/main.nr

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@ use core::{MAX_CONSUMED, write_bytes, NullifierKey, Resource};
22
use bigcurve::{Secp256k1, BigCurve, Secp256k1Scalar};
33
use std::embedded_curve_ops::{fixed_base_scalar_mul, multi_scalar_mul, EmbeddedCurvePoint, EmbeddedCurveScalar};
44
use poseidon::poseidon2;
5+
use std::static_assert;
56

67
// --- Circuit Globals ---
78
global DIGEST_BYTES: u32 = 32;
89
global MAX_CREATED: u32 = 4;
910
global MAX_KINDS: u32 = 8;
1011
global MAX_TREE_DEPTH: u32 = 32; // Set this to your actual max commitment tree depth
12+
global CONSUMED_COUNT_BYTES: u32 = 4;
13+
global CREATED_COUNT_BYTES: u32 = 4;
14+
global BASE_FIELD_BYTES: u32 = 32;
15+
global MAX_COMPLIANCE_DIGEST_BUF_LEN: u32 = 3*DIGEST_BYTES*MAX_CONSUMED + 2*DIGEST_BYTES*MAX_CREATED + CONSUMED_COUNT_BYTES + CREATED_COUNT_BYTES + 2*BASE_FIELD_BYTES;
1116

1217
/// A constant padding leaf used in Merkle trees.
1318
/// This is the hash of an empty string.
@@ -102,6 +107,33 @@ struct ComplianceInstance {
102107
delta: EmbeddedCurvePoint,
103108
}
104109

110+
impl ComplianceInstance {
111+
fn digest(self) -> Field {
112+
let mut buf = [0; MAX_COMPLIANCE_DIGEST_BUF_LEN];
113+
let mut buf_len = 0;
114+
let consumed_count_bytes = (self.consumed_count as Field).to_le_bytes::<CONSUMED_COUNT_BYTES>();
115+
write_bytes(&mut buf, &mut buf_len, consumed_count_bytes);
116+
for consumed_public in self.consumed_publics {
117+
write_bytes(&mut buf, &mut buf_len, consumed_public.resource_nullifier);
118+
write_bytes(&mut buf, &mut buf_len, consumed_public.resource_logic_ref);
119+
write_bytes(&mut buf, &mut buf_len, consumed_public.commitment_tree_root);
120+
}
121+
122+
let created_count_bytes = (self.created_count as Field).to_le_bytes::<CREATED_COUNT_BYTES>();
123+
write_bytes(&mut buf, &mut buf_len, created_count_bytes);
124+
for created_public in self.created_publics {
125+
write_bytes(&mut buf, &mut buf_len, created_public.resource_commitment);
126+
write_bytes(&mut buf, &mut buf_len, created_public.resource_logic_ref);
127+
}
128+
129+
write_bytes(&mut buf, &mut buf_len, self.delta.x.to_le_bytes::<BASE_FIELD_BYTES>());
130+
write_bytes(&mut buf, &mut buf_len, self.delta.y.to_le_bytes::<BASE_FIELD_BYTES>());
131+
132+
static_assert(buf_len == MAX_COMPLIANCE_DIGEST_BUF_LEN, "compliance instance digest pre-image malformed");
133+
Field::from_le_bytes(keccak256::keccak256(buf, buf_len))
134+
}
135+
}
136+
105137
/// The compliance witness contains all private inputs to the compliance proof.
106138
struct ComplianceWitness {
107139
/// Private information of consumed resources
@@ -264,8 +296,8 @@ fn accumulate_kind(
264296
}
265297

266298
// --- Main Entry Point ---
267-
fn main(witness: ComplianceWitness) -> pub ComplianceInstance {
268-
witness._constrain()
299+
fn main(witness: ComplianceWitness) -> pub Field {
300+
witness._constrain().digest()
269301
}
270302

271303
#[test]
@@ -334,13 +366,8 @@ fn test_compliance_valid() {
334366
};
335367

336368
// 8. Execute the main constraint logic
337-
let instance = main(witness);
369+
let compliance_digest = main(witness);
338370

339371
// 9. Verify the resulting public instance matches expectations
340-
assert(instance.consumed_count == 1);
341-
assert(instance.created_count == 1);
342-
343-
// Verify the outputted nullifier and commitment match our pre-computed ones
344-
assert(instance.consumed_publics[0].resource_nullifier == consumed_nullifier);
345-
assert(instance.created_publics[0].resource_commitment == created_res.commitment());
372+
assert(compliance_digest == 0x2425aec31290a2a809aba31ee04d0dcd8c2a2b7fdbea39b4ce399a571c428282);
346373
}

0 commit comments

Comments
 (0)