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