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