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