1use super::{LabeledPolynomial, PolynomialInfo};
17use crate::{crypto_hash::sha256::sha256, fft::EvaluationDomain, polycommit::kzg10};
18use snarkvm_curves::PairingEngine;
19use snarkvm_fields::{ConstraintFieldError, Field, PrimeField, ToConstraintField};
20use snarkvm_utilities::{FromBytes, ToBytes, error, serialize::*};
21
22use hashbrown::HashMap;
23use std::{
24 borrow::{Borrow, Cow},
25 collections::{BTreeMap, BTreeSet},
26 fmt,
27 ops::{AddAssign, MulAssign, SubAssign},
28};
29
30pub type UniversalParams<E> = kzg10::UniversalParams<E>;
32
33pub type Randomness<E> = kzg10::KZGRandomness<E>;
35
36pub type Commitment<E> = kzg10::KZGCommitment<E>;
38
39#[derive(Debug)]
42pub struct CommitterKey<E: PairingEngine> {
43 pub powers_of_beta_g: Vec<E::G1Affine>,
45
46 pub lagrange_bases_at_beta_g: BTreeMap<usize, Vec<E::G1Affine>>,
48
49 pub powers_of_beta_times_gamma_g: Vec<E::G1Affine>,
51
52 pub shifted_powers_of_beta_g: Option<Vec<E::G1Affine>>,
55
56 pub shifted_powers_of_beta_times_gamma_g: Option<BTreeMap<usize, Vec<E::G1Affine>>>,
59
60 pub enforced_degree_bounds: Option<Vec<usize>>,
64}
65
66impl<E: PairingEngine> FromBytes for CommitterKey<E> {
67 fn read_le<R: Read>(mut reader: R) -> io::Result<Self> {
68 let powers_len: u32 = FromBytes::read_le(&mut reader)?;
70 let mut powers_of_beta_g = Vec::with_capacity(powers_len as usize);
71 for _ in 0..powers_len {
72 let power: E::G1Affine = FromBytes::read_le(&mut reader)?;
73 powers_of_beta_g.push(power);
74 }
75
76 let lagrange_bases_at_beta_len: u32 = FromBytes::read_le(&mut reader)?;
78 let mut lagrange_bases_at_beta_g = BTreeMap::new();
79 for _ in 0..lagrange_bases_at_beta_len {
80 let size: u32 = FromBytes::read_le(&mut reader)?;
81 let mut basis = Vec::with_capacity(size as usize);
82 for _ in 0..size {
83 let power: E::G1Affine = FromBytes::read_le(&mut reader)?;
84 basis.push(power);
85 }
86 lagrange_bases_at_beta_g.insert(size as usize, basis);
87 }
88
89 let powers_of_beta_times_gamma_g_len: u32 = FromBytes::read_le(&mut reader)?;
91 let mut powers_of_beta_times_gamma_g = Vec::with_capacity(powers_of_beta_times_gamma_g_len as usize);
92 for _ in 0..powers_of_beta_times_gamma_g_len {
93 let powers_of_g: E::G1Affine = FromBytes::read_le(&mut reader)?;
94 powers_of_beta_times_gamma_g.push(powers_of_g);
95 }
96
97 let has_shifted_powers_of_beta_g: bool = FromBytes::read_le(&mut reader)?;
99 let shifted_powers_of_beta_g = match has_shifted_powers_of_beta_g {
100 true => {
101 let shifted_powers_len: u32 = FromBytes::read_le(&mut reader)?;
102 let mut shifted_powers_of_beta_g = Vec::with_capacity(shifted_powers_len as usize);
103 for _ in 0..shifted_powers_len {
104 let shifted_power: E::G1Affine = FromBytes::read_le(&mut reader)?;
105 shifted_powers_of_beta_g.push(shifted_power);
106 }
107
108 Some(shifted_powers_of_beta_g)
109 }
110 false => None,
111 };
112
113 let has_shifted_powers_of_beta_times_gamma_g: bool = FromBytes::read_le(&mut reader)?;
115 let shifted_powers_of_beta_times_gamma_g = match has_shifted_powers_of_beta_times_gamma_g {
116 true => {
117 let mut shifted_powers_of_beta_times_gamma_g = BTreeMap::new();
118 let shifted_powers_of_beta_times_gamma_g_num_elements: u32 = FromBytes::read_le(&mut reader)?;
119 for _ in 0..shifted_powers_of_beta_times_gamma_g_num_elements {
120 let key: u32 = FromBytes::read_le(&mut reader)?;
121
122 let value_len: u32 = FromBytes::read_le(&mut reader)?;
123 let mut value = Vec::with_capacity(value_len as usize);
124 for _ in 0..value_len {
125 let val: E::G1Affine = FromBytes::read_le(&mut reader)?;
126 value.push(val);
127 }
128
129 shifted_powers_of_beta_times_gamma_g.insert(key as usize, value);
130 }
131
132 Some(shifted_powers_of_beta_times_gamma_g)
133 }
134 false => None,
135 };
136
137 let has_enforced_degree_bounds: bool = FromBytes::read_le(&mut reader)?;
139 let enforced_degree_bounds = match has_enforced_degree_bounds {
140 true => {
141 let enforced_degree_bounds_len: u32 = FromBytes::read_le(&mut reader)?;
142 let mut enforced_degree_bounds = Vec::with_capacity(enforced_degree_bounds_len as usize);
143 for _ in 0..enforced_degree_bounds_len {
144 let enforced_degree_bound: u32 = FromBytes::read_le(&mut reader)?;
145 enforced_degree_bounds.push(enforced_degree_bound as usize);
146 }
147
148 Some(enforced_degree_bounds)
149 }
150 false => None,
151 };
152
153 let mut hash_input = powers_of_beta_g.to_bytes_le().map_err(|_| error("Could not serialize powers"))?;
155 powers_of_beta_times_gamma_g
156 .write_le(&mut hash_input)
157 .map_err(|_| error("Could not serialize powers_of_beta_times_gamma_g"))?;
158
159 if let Some(shifted_powers_of_beta_g) = &shifted_powers_of_beta_g {
160 shifted_powers_of_beta_g
161 .write_le(&mut hash_input)
162 .map_err(|_| error("Could not serialize shifted_powers_of_beta_g"))?;
163 }
164
165 if let Some(shifted_powers_of_beta_times_gamma_g) = &shifted_powers_of_beta_times_gamma_g {
166 for value in shifted_powers_of_beta_times_gamma_g.values() {
167 value.write_le(&mut hash_input).map_err(|_| error("Could not serialize shifted_power_of_gamma_g"))?;
168 }
169 }
170
171 let hash = sha256(&hash_input);
173 let expected_hash: [u8; 32] = FromBytes::read_le(&mut reader)?;
174
175 if expected_hash != hash {
177 return Err(error("Mismatching group elements"));
178 }
179
180 Ok(Self {
181 powers_of_beta_g,
182 lagrange_bases_at_beta_g,
183 powers_of_beta_times_gamma_g,
184 shifted_powers_of_beta_g,
185 shifted_powers_of_beta_times_gamma_g,
186 enforced_degree_bounds,
187 })
188 }
189}
190
191impl<E: PairingEngine> ToBytes for CommitterKey<E> {
192 fn write_le<W: Write>(&self, mut writer: W) -> io::Result<()> {
193 (self.powers_of_beta_g.len() as u32).write_le(&mut writer)?;
195 for power in &self.powers_of_beta_g {
196 power.write_le(&mut writer)?;
197 }
198
199 (self.lagrange_bases_at_beta_g.len() as u32).write_le(&mut writer)?;
201 for (size, powers) in &self.lagrange_bases_at_beta_g {
202 (*size as u32).write_le(&mut writer)?;
203 for power in powers {
204 power.write_le(&mut writer)?;
205 }
206 }
207
208 (self.powers_of_beta_times_gamma_g.len() as u32).write_le(&mut writer)?;
210 for power_of_gamma_g in &self.powers_of_beta_times_gamma_g {
211 power_of_gamma_g.write_le(&mut writer)?;
212 }
213
214 self.shifted_powers_of_beta_g.is_some().write_le(&mut writer)?;
216 if let Some(shifted_powers_of_beta_g) = &self.shifted_powers_of_beta_g {
217 (shifted_powers_of_beta_g.len() as u32).write_le(&mut writer)?;
218 for shifted_power in shifted_powers_of_beta_g {
219 shifted_power.write_le(&mut writer)?;
220 }
221 }
222
223 self.shifted_powers_of_beta_times_gamma_g.is_some().write_le(&mut writer)?;
225 if let Some(shifted_powers_of_beta_times_gamma_g) = &self.shifted_powers_of_beta_times_gamma_g {
226 (shifted_powers_of_beta_times_gamma_g.len() as u32).write_le(&mut writer)?;
227 for (key, shifted_powers_of_beta_g) in shifted_powers_of_beta_times_gamma_g {
228 (*key as u32).write_le(&mut writer)?;
229 (shifted_powers_of_beta_g.len() as u32).write_le(&mut writer)?;
230 for shifted_power in shifted_powers_of_beta_g {
231 shifted_power.write_le(&mut writer)?;
232 }
233 }
234 }
235
236 self.enforced_degree_bounds.is_some().write_le(&mut writer)?;
238 if let Some(enforced_degree_bounds) = &self.enforced_degree_bounds {
239 (enforced_degree_bounds.len() as u32).write_le(&mut writer)?;
240 for enforced_degree_bound in enforced_degree_bounds {
241 (*enforced_degree_bound as u32).write_le(&mut writer)?;
242 }
243 }
244
245 let mut hash_input = self.powers_of_beta_g.to_bytes_le().map_err(|_| error("Could not serialize powers"))?;
247 self.powers_of_beta_times_gamma_g
248 .write_le(&mut hash_input)
249 .map_err(|_| error("Could not serialize powers_of_beta_times_gamma_g"))?;
250
251 if let Some(shifted_powers_of_beta_g) = &self.shifted_powers_of_beta_g {
252 shifted_powers_of_beta_g
253 .write_le(&mut hash_input)
254 .map_err(|_| error("Could not serialize shifted_powers_of_beta_g"))?;
255 }
256
257 if let Some(shifted_powers_of_beta_times_gamma_g) = &self.shifted_powers_of_beta_times_gamma_g {
258 for value in shifted_powers_of_beta_times_gamma_g.values() {
259 value.write_le(&mut hash_input).map_err(|_| error("Could not serialize shifted_power_of_gamma_g"))?;
260 }
261 }
262
263 let hash = sha256(&hash_input);
265 hash.write_le(&mut writer)
266 }
267}
268
269impl<E: PairingEngine> CommitterKey<E> {
270 fn len(&self) -> usize {
271 if self.shifted_powers_of_beta_g.is_some() { self.shifted_powers_of_beta_g.as_ref().unwrap().len() } else { 0 }
272 }
273}
274
275#[derive(Debug)]
278pub struct CommitterUnionKey<'a, E: PairingEngine> {
279 pub powers_of_beta_g: Option<&'a Vec<E::G1Affine>>,
281
282 pub lagrange_bases_at_beta_g: BTreeMap<usize, &'a Vec<E::G1Affine>>,
284
285 pub powers_of_beta_times_gamma_g: Option<&'a Vec<E::G1Affine>>,
287
288 pub shifted_powers_of_beta_g: Option<&'a Vec<E::G1Affine>>,
291
292 pub shifted_powers_of_beta_times_gamma_g: Option<BTreeMap<usize, &'a Vec<E::G1Affine>>>,
295
296 pub enforced_degree_bounds: Option<Vec<usize>>,
300}
301
302impl<'a, E: PairingEngine> CommitterUnionKey<'a, E> {
303 pub fn powers(&self) -> kzg10::Powers<E> {
305 kzg10::Powers {
306 powers_of_beta_g: self.powers_of_beta_g.unwrap().as_slice().into(),
307 powers_of_beta_times_gamma_g: self.powers_of_beta_times_gamma_g.unwrap().as_slice().into(),
308 }
309 }
310
311 pub fn shifted_powers_of_beta_g(&self, degree_bound: impl Into<Option<usize>>) -> Option<kzg10::Powers<E>> {
313 match (&self.shifted_powers_of_beta_g, &self.shifted_powers_of_beta_times_gamma_g) {
314 (Some(shifted_powers_of_beta_g), Some(shifted_powers_of_beta_times_gamma_g)) => {
315 let max_bound = self.enforced_degree_bounds.as_ref().unwrap().last().unwrap();
316 let (bound, powers_range) = if let Some(degree_bound) = degree_bound.into() {
317 assert!(self.enforced_degree_bounds.as_ref().unwrap().contains(°ree_bound));
318 (degree_bound, (max_bound - degree_bound)..)
319 } else {
320 (*max_bound, 0..)
321 };
322
323 let ck = kzg10::Powers {
324 powers_of_beta_g: shifted_powers_of_beta_g[powers_range].into(),
325 powers_of_beta_times_gamma_g: shifted_powers_of_beta_times_gamma_g[&bound].clone().into(),
326 };
327
328 Some(ck)
329 }
330
331 (_, _) => None,
332 }
333 }
334
335 pub fn lagrange_basis(&self, domain: EvaluationDomain<E::Fr>) -> Option<kzg10::LagrangeBasis<E>> {
338 self.lagrange_bases_at_beta_g.get(&domain.size()).map(|basis| kzg10::LagrangeBasis {
339 lagrange_basis_at_beta_g: Cow::Borrowed(basis),
340 powers_of_beta_times_gamma_g: Cow::Borrowed(self.powers_of_beta_times_gamma_g.unwrap()),
341 domain,
342 })
343 }
344
345 pub fn union<T: IntoIterator<Item = &'a CommitterKey<E>>>(committer_keys: T) -> Self {
346 let mut ck_union = CommitterUnionKey::<E> {
347 powers_of_beta_g: None,
348 lagrange_bases_at_beta_g: BTreeMap::new(),
349 powers_of_beta_times_gamma_g: None,
350 shifted_powers_of_beta_g: None,
351 shifted_powers_of_beta_times_gamma_g: None,
352 enforced_degree_bounds: None,
353 };
354 let mut enforced_degree_bounds = vec![];
355 let mut biggest_ck: Option<&CommitterKey<E>> = None;
356 let mut shifted_powers_of_beta_times_gamma_g = BTreeMap::new();
357 for ck in committer_keys {
358 if biggest_ck.is_none() || biggest_ck.unwrap().len() < ck.len() {
359 biggest_ck = Some(ck);
360 }
361 let lagrange_bases = &ck.lagrange_bases_at_beta_g;
362 for (bound_base, bases) in lagrange_bases.iter() {
363 ck_union.lagrange_bases_at_beta_g.entry(*bound_base).or_insert(bases);
364 }
365 if let Some(shifted_powers) = ck.shifted_powers_of_beta_times_gamma_g.as_ref() {
366 for (bound_power, powers) in shifted_powers.iter() {
367 shifted_powers_of_beta_times_gamma_g.entry(*bound_power).or_insert(powers);
368 }
369 }
370 if let Some(degree_bounds) = &ck.enforced_degree_bounds {
371 enforced_degree_bounds.append(&mut degree_bounds.clone());
372 }
373 }
374
375 let biggest_ck = biggest_ck.unwrap();
376 ck_union.powers_of_beta_g = Some(&biggest_ck.powers_of_beta_g);
377 ck_union.powers_of_beta_times_gamma_g = Some(&biggest_ck.powers_of_beta_times_gamma_g);
378 ck_union.shifted_powers_of_beta_g = biggest_ck.shifted_powers_of_beta_g.as_ref();
379
380 if !enforced_degree_bounds.is_empty() {
381 enforced_degree_bounds.sort();
382 enforced_degree_bounds.dedup();
383 ck_union.enforced_degree_bounds = Some(enforced_degree_bounds);
384 ck_union.shifted_powers_of_beta_times_gamma_g = Some(shifted_powers_of_beta_times_gamma_g);
385 }
386
387 ck_union
388 }
389}
390
391#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, CanonicalSerialize, CanonicalDeserialize)]
393pub struct BatchProof<E: PairingEngine>(pub(crate) Vec<kzg10::KZGProof<E>>);
394
395impl<E: PairingEngine> BatchProof<E> {
396 pub fn is_hiding(&self) -> bool {
397 self.0.iter().any(|c| c.is_hiding())
398 }
399}
400
401pub type PolynomialLabel = String;
403
404#[derive(Clone, Debug, CanonicalSerialize, PartialEq, Eq)]
406pub struct LabeledCommitment<C: CanonicalSerialize + 'static> {
407 label: PolynomialLabel,
408 commitment: C,
409 degree_bound: Option<usize>,
410}
411
412impl<F: Field, C: CanonicalSerialize + ToConstraintField<F>> ToConstraintField<F> for LabeledCommitment<C> {
413 fn to_field_elements(&self) -> Result<Vec<F>, ConstraintFieldError> {
414 self.commitment.to_field_elements()
415 }
416}
417
418impl<C: CanonicalSerialize + ToBytes> ToBytes for LabeledCommitment<C> {
423 fn write_le<W: Write>(&self, mut writer: W) -> io::Result<()> {
424 CanonicalSerialize::serialize_compressed(&self.commitment, &mut writer)
425 .map_err(|_| error("could not serialize struct"))
426 }
427}
428
429impl<C: CanonicalSerialize> LabeledCommitment<C> {
430 pub fn new(label: PolynomialLabel, commitment: C, degree_bound: Option<usize>) -> Self {
432 Self { label, commitment, degree_bound }
433 }
434
435 pub fn new_with_info(info: &PolynomialInfo, commitment: C) -> Self {
436 Self { label: info.label().to_string(), commitment, degree_bound: info.degree_bound() }
437 }
438
439 pub fn label(&self) -> &str {
441 &self.label
442 }
443
444 pub fn commitment(&self) -> &C {
446 &self.commitment
447 }
448
449 pub fn degree_bound(&self) -> Option<usize> {
451 self.degree_bound
452 }
453}
454
455#[derive(Hash, Ord, PartialOrd, Clone, Eq, PartialEq)]
457pub enum LCTerm {
458 One,
460 PolyLabel(String),
462}
463
464impl fmt::Debug for LCTerm {
465 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
466 match self {
467 LCTerm::One => write!(f, "1"),
468 LCTerm::PolyLabel(label) => write!(f, "{label}"),
469 }
470 }
471}
472
473impl LCTerm {
474 #[inline]
476 pub fn is_one(&self) -> bool {
477 matches!(self, LCTerm::One)
478 }
479}
480
481impl From<PolynomialLabel> for LCTerm {
482 fn from(other: PolynomialLabel) -> Self {
483 Self::PolyLabel(other)
484 }
485}
486
487impl From<&'_ str> for LCTerm {
488 fn from(other: &str) -> Self {
489 Self::PolyLabel(other.into())
490 }
491}
492
493impl core::convert::TryInto<PolynomialLabel> for LCTerm {
494 type Error = ();
495
496 fn try_into(self) -> Result<PolynomialLabel, ()> {
497 match self {
498 Self::One => Err(()),
499 Self::PolyLabel(l) => Ok(l),
500 }
501 }
502}
503
504impl<'a> core::convert::TryInto<&'a PolynomialLabel> for &'a LCTerm {
505 type Error = ();
506
507 fn try_into(self) -> Result<&'a PolynomialLabel, ()> {
508 match self {
509 LCTerm::One => Err(()),
510 LCTerm::PolyLabel(l) => Ok(l),
511 }
512 }
513}
514
515impl<B: Borrow<String>> PartialEq<B> for LCTerm {
516 fn eq(&self, other: &B) -> bool {
517 match self {
518 Self::One => false,
519 Self::PolyLabel(l) => l == other.borrow(),
520 }
521 }
522}
523
524#[derive(Clone, Debug)]
526pub struct LinearCombination<F> {
527 pub label: String,
529 pub terms: BTreeMap<LCTerm, F>,
531}
532
533#[allow(clippy::or_fun_call)]
534impl<F: Field> LinearCombination<F> {
535 pub fn empty(label: impl Into<String>) -> Self {
537 Self { label: label.into(), terms: BTreeMap::new() }
538 }
539
540 pub fn new(label: impl Into<String>, _terms: impl IntoIterator<Item = (F, impl Into<LCTerm>)>) -> Self {
543 let mut terms = BTreeMap::new();
544 for (c, l) in _terms.into_iter().map(|(c, t)| (c, t.into())) {
545 *terms.entry(l).or_insert(F::zero()) += c;
546 }
547
548 Self { label: label.into(), terms }
549 }
550
551 pub fn label(&self) -> &str {
553 &self.label
554 }
555
556 pub fn is_empty(&self) -> bool {
558 self.terms.is_empty()
559 }
560
561 pub fn add(&mut self, c: F, t: impl Into<LCTerm>) -> &mut Self {
563 let t = t.into();
564 *self.terms.entry(t.clone()).or_insert(F::zero()) += c;
565 if self.terms[&t].is_zero() {
566 self.terms.remove(&t);
567 }
568 self
569 }
570
571 pub fn len(&self) -> usize {
572 self.terms.len()
573 }
574
575 pub fn iter(&self) -> impl Iterator<Item = (&F, &LCTerm)> {
576 self.terms.iter().map(|(t, c)| (c, t))
577 }
578}
579
580impl<'a, F: Field> AddAssign<(F, &'a LinearCombination<F>)> for LinearCombination<F> {
581 #[allow(clippy::suspicious_op_assign_impl)]
582 fn add_assign(&mut self, (coeff, other): (F, &'a LinearCombination<F>)) {
583 for (t, c) in other.terms.iter() {
584 self.add(coeff * c, t.clone());
585 }
586 }
587}
588
589impl<'a, F: Field> SubAssign<(F, &'a LinearCombination<F>)> for LinearCombination<F> {
590 #[allow(clippy::suspicious_op_assign_impl)]
591 fn sub_assign(&mut self, (coeff, other): (F, &'a LinearCombination<F>)) {
592 for (t, c) in other.terms.iter() {
593 self.add(-coeff * c, t.clone());
594 }
595 }
596}
597
598impl<'a, F: Field> AddAssign<&'a LinearCombination<F>> for LinearCombination<F> {
599 fn add_assign(&mut self, other: &'a LinearCombination<F>) {
600 for (t, c) in other.terms.iter() {
601 self.add(*c, t.clone());
602 }
603 }
604}
605
606impl<'a, F: Field> SubAssign<&'a LinearCombination<F>> for LinearCombination<F> {
607 fn sub_assign(&mut self, other: &'a LinearCombination<F>) {
608 for (t, &c) in other.terms.iter() {
609 self.add(-c, t.clone());
610 }
611 }
612}
613
614impl<F: Field> AddAssign<F> for LinearCombination<F> {
615 fn add_assign(&mut self, coeff: F) {
616 self.add(coeff, LCTerm::One);
617 }
618}
619
620impl<F: Field> SubAssign<F> for LinearCombination<F> {
621 fn sub_assign(&mut self, coeff: F) {
622 self.add(-coeff, LCTerm::One);
623 }
624}
625
626impl<F: Field> MulAssign<F> for LinearCombination<F> {
627 fn mul_assign(&mut self, coeff: F) {
628 self.terms.values_mut().for_each(|c| *c *= &coeff);
629 }
630}
631
632pub type QuerySet<T> = BTreeSet<(String, (String, T))>;
640
641pub type Evaluations<F> = BTreeMap<(String, F), F>;
647
648pub fn evaluate_query_set<'a, F: PrimeField>(
650 polys: impl IntoIterator<Item = &'a LabeledPolynomial<F>>,
651 query_set: &QuerySet<F>,
652) -> Evaluations<F> {
653 let polys: HashMap<_, _> = polys.into_iter().map(|p| (p.label(), p)).collect();
654 let mut evaluations = Evaluations::new();
655 for (label, (_point_name, point)) in query_set {
656 let poly = polys.get(label as &str).expect("polynomial in evaluated lc is not found");
657 let eval = poly.evaluate(*point);
658 evaluations.insert((label.clone(), *point), eval);
659 }
660 evaluations
661}
662
663#[derive(Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)]
665pub struct BatchLCProof<E: PairingEngine> {
666 pub proof: BatchProof<E>,
668}
669
670impl<E: PairingEngine> BatchLCProof<E> {
671 pub fn is_hiding(&self) -> bool {
672 self.proof.is_hiding()
673 }
674}
675
676impl<E: PairingEngine> FromBytes for BatchLCProof<E> {
677 fn read_le<R: Read>(mut reader: R) -> io::Result<Self> {
678 CanonicalDeserialize::deserialize_compressed(&mut reader).map_err(|_| error("could not deserialize struct"))
679 }
680}
681
682impl<E: PairingEngine> ToBytes for BatchLCProof<E> {
683 fn write_le<W: Write>(&self, mut writer: W) -> io::Result<()> {
684 CanonicalSerialize::serialize_compressed(self, &mut writer).map_err(|_| error("could not serialize struct"))
685 }
686}