@@ -2,12 +2,17 @@ use core::{MAX_CONSUMED, write_bytes, NullifierKey, Resource};
22use bigcurve:: {Secp256k1 , BigCurve , Secp256k1Scalar };
33use std::embedded_curve_ops:: {fixed_base_scalar_mul , multi_scalar_mul , EmbeddedCurvePoint , EmbeddedCurveScalar };
44use poseidon::poseidon2 ;
5+ use std::static_assert ;
56
67// --- Circuit Globals ---
78global DIGEST_BYTES : u32 = 32 ;
89global MAX_CREATED : u32 = 4 ;
910global MAX_KINDS : u32 = 8 ;
1011global 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.
106138struct 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