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