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