core_utils/circuit/
circuit_id.rs1use primitives::algebra::elliptic_curve::Curve;
16use serde::{Deserialize, Serialize};
17
18use crate::circuit::v2::Circuit;
19
20#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
23pub struct CircuitId([u8; 32]);
24
25impl CircuitId {
26 pub fn of<C: Curve>(circuit: &Circuit<C>) -> Self {
32 let bytes =
33 bincode::serialize(circuit).expect("circuit bincode serialization is infallible");
34 Self(*blake3::hash(&bytes).as_bytes())
35 }
36
37 pub fn as_bytes(&self) -> &[u8; 32] {
39 &self.0
40 }
41}
42
43#[cfg(test)]
44mod tests {
45 use primitives::algebra::elliptic_curve::Curve25519Ristretto as C;
46
47 use super::*;
48 use crate::circuit::v2::{AlgebraicType, FieldShareBinaryOp, Gate, Input};
49
50 fn input<C: Curve>() -> Gate<C> {
51 Gate::Input(Input::SecretPlaintext {
52 inputer: 0,
53 algebraic_type: AlgebraicType::ScalarField,
54 batch_size: 1,
55 })
56 }
57
58 fn sample_circuit() -> Circuit<C> {
60 let mut circuit = Circuit::new();
61 let i0 = circuit.add_gate(input()).unwrap();
62 let i1 = circuit.add_gate(input()).unwrap();
63 let i2 = circuit.add_gate(input()).unwrap();
64 let i3 = circuit.add_gate(input()).unwrap();
65 let l = circuit
66 .add_gate(Gate::FieldShareBinaryOp {
67 x: i0,
68 y: i1,
69 op: FieldShareBinaryOp::Add,
70 })
71 .unwrap();
72 let r = circuit
73 .add_gate(Gate::FieldShareBinaryOp {
74 x: i2,
75 y: i3,
76 op: FieldShareBinaryOp::Add,
77 })
78 .unwrap();
79 let top = circuit
80 .add_gate(Gate::FieldShareBinaryOp {
81 x: l,
82 y: r,
83 op: FieldShareBinaryOp::Add,
84 })
85 .unwrap();
86 circuit.add_output(top).unwrap();
87 circuit
88 }
89
90 #[test]
92 fn deterministic() {
93 assert_eq!(
94 CircuitId::of(&sample_circuit()),
95 CircuitId::of(&sample_circuit())
96 );
97 }
98
99 #[test]
101 fn stable_across_serialization_roundtrip() {
102 let circuit = sample_circuit();
103 let bytes = bincode::serialize(&circuit).unwrap();
104 let restored: Circuit<C> = bincode::deserialize(&bytes).unwrap();
105 assert_eq!(CircuitId::of(&circuit), CircuitId::of(&restored));
106 }
107
108 #[test]
110 fn order_sensitive() {
111 let mut a = Circuit::<C>::new();
114 let a0 = a.add_gate(input()).unwrap();
115 let a1 = a
116 .add_gate(Gate::Input(Input::SecretPlaintext {
117 inputer: 1,
118 algebraic_type: AlgebraicType::ScalarField,
119 batch_size: 1,
120 }))
121 .unwrap();
122 let a2 = a
123 .add_gate(Gate::FieldShareBinaryOp {
124 x: a0,
125 y: a1,
126 op: FieldShareBinaryOp::Add,
127 })
128 .unwrap();
129 a.add_output(a2).unwrap();
130
131 let mut b = Circuit::<C>::new();
132 let b0 = b
133 .add_gate(Gate::Input(Input::SecretPlaintext {
134 inputer: 1,
135 algebraic_type: AlgebraicType::ScalarField,
136 batch_size: 1,
137 }))
138 .unwrap();
139 let b1 = b.add_gate(input()).unwrap();
140 let b2 = b
141 .add_gate(Gate::FieldShareBinaryOp {
142 x: b0,
143 y: b1,
144 op: FieldShareBinaryOp::Add,
145 })
146 .unwrap();
147 b.add_output(b2).unwrap();
148
149 assert_ne!(CircuitId::of(&a), CircuitId::of(&b));
150 }
151
152 #[test]
154 fn gate_sensitive() {
155 let mut add = Circuit::<C>::new();
156 let x = add.add_gate(input()).unwrap();
157 let y = add.add_gate(input()).unwrap();
158 let z = add
159 .add_gate(Gate::FieldShareBinaryOp {
160 x,
161 y,
162 op: FieldShareBinaryOp::Add,
163 })
164 .unwrap();
165 add.add_output(z).unwrap();
166
167 let mut mul = Circuit::<C>::new();
168 let x = mul.add_gate(input()).unwrap();
169 let y = mul.add_gate(input()).unwrap();
170 let z = mul
171 .add_gate(Gate::FieldShareBinaryOp {
172 x,
173 y,
174 op: FieldShareBinaryOp::Mul,
175 })
176 .unwrap();
177 mul.add_output(z).unwrap();
178
179 assert_ne!(CircuitId::of(&add), CircuitId::of(&mul));
180 }
181
182 #[test]
184 fn output_sensitive() {
185 let mut base = Circuit::<C>::new();
186 let x = base.add_gate(input()).unwrap();
187 let y = base.add_gate(input()).unwrap();
188 let z = base
189 .add_gate(Gate::FieldShareBinaryOp {
190 x,
191 y,
192 op: FieldShareBinaryOp::Add,
193 })
194 .unwrap();
195
196 let mut out_z = base.clone();
197 out_z.add_output(z).unwrap();
198
199 let mut out_x = base.clone();
200 out_x.add_output(x).unwrap();
201
202 assert_ne!(CircuitId::of(&out_z), CircuitId::of(&out_x));
203 }
204}