1use std::array;
6
7use crate::parallel_utils::{flatten_results, transform, transform_parallel};
8use crate::single::group::GroupHasher;
9use crate::single::{
10 self, circuit_degree,
11 group::F,
12 log::{ContributionHash, Hashable},
13 DLogProof, ExtraTransitionInformation, LinkingProof, Phase1CRSElements, Phase1Contribution,
14 Phase1RawCRSElements, Phase1RawContribution, Phase2CRSElements, Phase2Contribution,
15 Phase2RawCRSElements, Phase2RawContribution,
16};
17use anyhow::{anyhow, Result};
18use ark_groth16::ProvingKey;
19use ark_relations::r1cs::ConstraintMatrices;
20use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate};
21use decaf377::Bls12_377;
22use penumbra_sdk_dex::{swap::proof::SwapCircuit, swap_claim::proof::SwapClaimCircuit};
23use penumbra_sdk_governance::DelegatorVoteCircuit;
24use penumbra_sdk_proof_params::generate_constraint_matrices;
25use penumbra_sdk_proto::tools::summoning::v1::{self as pb};
26use penumbra_sdk_shielded_pool::{
27 ConvertCircuit, NullifierDerivationCircuit, OutputCircuit, SpendCircuit,
28};
29
30use rand_core::OsRng;
31
32const SERIALIZATION_COMPRESSION: Compress = Compress::No;
35
36fn to_bytes<T: CanonicalSerialize>(t: &T) -> Result<Vec<u8>> {
37 let mut out = Vec::new();
38 t.serialize_with_mode(&mut out, SERIALIZATION_COMPRESSION)?;
39 Ok(out)
40}
41
42fn from_bytes<T: CanonicalDeserialize>(data: &[u8]) -> Result<T> {
43 Ok(T::deserialize_with_mode(
44 data,
45 SERIALIZATION_COMPRESSION,
46 Validate::Yes,
47 )?)
48}
49
50fn from_bytes_unchecked<T: CanonicalDeserialize>(data: &[u8]) -> Result<T> {
51 Ok(T::deserialize_with_mode(
52 data,
53 SERIALIZATION_COMPRESSION,
54 Validate::No,
55 )?)
56}
57
58pub const NUM_CIRCUITS: usize = 7;
59
60fn circuits() -> [ConstraintMatrices<F>; NUM_CIRCUITS] {
62 [
63 generate_constraint_matrices::<SpendCircuit>(),
64 generate_constraint_matrices::<OutputCircuit>(),
65 generate_constraint_matrices::<DelegatorVoteCircuit>(),
66 generate_constraint_matrices::<ConvertCircuit>(),
67 generate_constraint_matrices::<SwapCircuit>(),
68 generate_constraint_matrices::<SwapClaimCircuit>(),
69 generate_constraint_matrices::<NullifierDerivationCircuit>(),
70 ]
71}
72
73#[derive(Clone, Debug)]
75pub struct Phase2RawCeremonyCRS([Phase2RawCRSElements; NUM_CIRCUITS]);
76
77impl Phase2RawCeremonyCRS {
78 pub fn assume_valid(self) -> Phase2CeremonyCRS {
82 match self.0 {
83 [x0, x1, x2, x3, x4, x5, x6] => Phase2CeremonyCRS([
84 x0.assume_valid(),
85 x1.assume_valid(),
86 x2.assume_valid(),
87 x3.assume_valid(),
88 x4.assume_valid(),
89 x5.assume_valid(),
90 x6.assume_valid(),
91 ]),
92 }
93 }
94
95 pub fn unchecked_from_protobuf(value: pb::CeremonyCrs) -> anyhow::Result<Self> {
96 Ok(Self([
97 from_bytes_unchecked::<Phase2RawCRSElements>(value.spend.as_slice())?,
98 from_bytes_unchecked::<Phase2RawCRSElements>(value.output.as_slice())?,
99 from_bytes_unchecked::<Phase2RawCRSElements>(value.delegator_vote.as_slice())?,
100 from_bytes_unchecked::<Phase2RawCRSElements>(value.undelegate_claim.as_slice())?,
101 from_bytes_unchecked::<Phase2RawCRSElements>(value.swap.as_slice())?,
102 from_bytes_unchecked::<Phase2RawCRSElements>(value.swap_claim.as_slice())?,
103 from_bytes_unchecked::<Phase2RawCRSElements>(value.nullifer_derivation_crs.as_slice())?,
104 ]))
105 }
106}
107
108impl TryInto<pb::CeremonyCrs> for Phase2RawCeremonyCRS {
109 type Error = anyhow::Error;
110
111 fn try_into(self) -> Result<pb::CeremonyCrs> {
112 Ok(pb::CeremonyCrs {
113 spend: to_bytes(&self.0[0])?,
114 output: to_bytes(&self.0[1])?,
115 delegator_vote: to_bytes(&self.0[2])?,
116 undelegate_claim: to_bytes(&self.0[3])?,
117 swap: to_bytes(&self.0[4])?,
118 swap_claim: to_bytes(&self.0[5])?,
119 nullifer_derivation_crs: to_bytes(&self.0[6])?,
120 })
121 }
122}
123
124impl TryFrom<pb::CeremonyCrs> for Phase2RawCeremonyCRS {
125 type Error = anyhow::Error;
126
127 fn try_from(value: pb::CeremonyCrs) -> std::result::Result<Self, Self::Error> {
128 Ok(Self([
129 from_bytes::<Phase2RawCRSElements>(value.spend.as_slice())?,
130 from_bytes::<Phase2RawCRSElements>(value.output.as_slice())?,
131 from_bytes::<Phase2RawCRSElements>(value.delegator_vote.as_slice())?,
132 from_bytes::<Phase2RawCRSElements>(value.undelegate_claim.as_slice())?,
133 from_bytes::<Phase2RawCRSElements>(value.swap.as_slice())?,
134 from_bytes::<Phase2RawCRSElements>(value.swap_claim.as_slice())?,
135 from_bytes::<Phase2RawCRSElements>(value.nullifer_derivation_crs.as_slice())?,
136 ]))
137 }
138}
139
140#[derive(Clone, Debug)]
142pub struct Phase2CeremonyCRS([Phase2CRSElements; NUM_CIRCUITS]);
143
144impl From<Phase2CeremonyCRS> for Phase2RawCeremonyCRS {
145 fn from(value: Phase2CeremonyCRS) -> Self {
146 Self(array::from_fn(|i| value.0[i].raw.clone()))
147 }
148}
149
150impl TryFrom<Phase2CeremonyCRS> for pb::CeremonyCrs {
151 type Error = anyhow::Error;
152
153 fn try_from(data: Phase2CeremonyCRS) -> Result<pb::CeremonyCrs> {
154 Phase2RawCeremonyCRS::from(data).try_into()
155 }
156}
157
158impl Phase2CeremonyCRS {
159 pub fn root() -> Result<Self> {
160 let [c0, c1, c2, c3, c4, c5, c6] = circuits();
161 Ok(Self([
162 Phase2CRSElements::dummy_root(circuit_degree(&c0)?),
163 Phase2CRSElements::dummy_root(circuit_degree(&c1)?),
164 Phase2CRSElements::dummy_root(circuit_degree(&c2)?),
165 Phase2CRSElements::dummy_root(circuit_degree(&c3)?),
166 Phase2CRSElements::dummy_root(circuit_degree(&c4)?),
167 Phase2CRSElements::dummy_root(circuit_degree(&c5)?),
168 Phase2CRSElements::dummy_root(circuit_degree(&c6)?),
169 ]))
170 }
171}
172
173#[derive(Clone, Debug)]
175pub struct Phase2RawCeremonyContribution([Phase2RawContribution; NUM_CIRCUITS]);
176
177impl TryInto<pb::participate_request::Contribution> for Phase2RawCeremonyContribution {
178 type Error = anyhow::Error;
179
180 fn try_into(self) -> Result<pb::participate_request::Contribution> {
181 Ok(pb::participate_request::Contribution {
182 updated: Some(pb::CeremonyCrs {
183 spend: to_bytes(&self.0[0].new_elements)?,
184 output: to_bytes(&self.0[1].new_elements)?,
185 delegator_vote: to_bytes(&self.0[2].new_elements)?,
186 undelegate_claim: to_bytes(&self.0[3].new_elements)?,
187 swap: to_bytes(&self.0[4].new_elements)?,
188 swap_claim: to_bytes(&self.0[5].new_elements)?,
189 nullifer_derivation_crs: to_bytes(&self.0[6].new_elements)?,
190 }),
191 update_proofs: Some(pb::CeremonyLinkingProof {
192 spend: to_bytes(&self.0[0].linking_proof)?,
193 output: to_bytes(&self.0[1].linking_proof)?,
194 delegator_vote: to_bytes(&self.0[2].linking_proof)?,
195 undelegate_claim: to_bytes(&self.0[3].linking_proof)?,
196 swap: to_bytes(&self.0[4].linking_proof)?,
197 swap_claim: to_bytes(&self.0[5].linking_proof)?,
198 nullifer_derivation_crs: to_bytes(&self.0[6].linking_proof)?,
199 }),
200 parent_hashes: Some(pb::CeremonyParentHashes {
201 spend: self.0[0].parent.0.to_vec(),
202 output: self.0[1].parent.0.to_vec(),
203 delegator_vote: self.0[2].parent.0.to_vec(),
204 undelegate_claim: self.0[3].parent.0.to_vec(),
205 swap: self.0[4].parent.0.to_vec(),
206 swap_claim: self.0[5].parent.0.to_vec(),
207 nullifer_derivation_crs: self.0[6].parent.0.to_vec(),
208 }),
209 })
210 }
211}
212
213impl TryFrom<pb::participate_request::Contribution> for Phase2RawCeremonyContribution {
214 type Error = anyhow::Error;
215
216 fn try_from(value: pb::participate_request::Contribution) -> Result<Self> {
217 let (parent_hashes, updated, update_proofs) = match value {
218 pb::participate_request::Contribution {
219 parent_hashes: Some(x0),
220 updated: Some(x1),
221 update_proofs: Some(x2),
222 } => (x0, x1, x2),
223 _ => anyhow::bail!("missing contribution data"),
224 };
225 let data = [
226 (parent_hashes.spend, updated.spend, update_proofs.spend),
227 (parent_hashes.output, updated.output, update_proofs.output),
228 (
229 parent_hashes.delegator_vote,
230 updated.delegator_vote,
231 update_proofs.delegator_vote,
232 ),
233 (
234 parent_hashes.undelegate_claim,
235 updated.undelegate_claim,
236 update_proofs.undelegate_claim,
237 ),
238 (parent_hashes.swap, updated.swap, update_proofs.swap),
239 (
240 parent_hashes.swap_claim,
241 updated.swap_claim,
242 update_proofs.swap_claim,
243 ),
244 (
245 parent_hashes.nullifer_derivation_crs,
246 updated.nullifer_derivation_crs,
247 update_proofs.nullifer_derivation_crs,
248 ),
249 ];
250 let out = transform_parallel(data, |(parent_hash, updated, update_proof)| {
251 Ok::<_, anyhow::Error>(Phase2RawContribution {
252 parent: ContributionHash::try_from(parent_hash.as_slice())?,
253 new_elements: Phase2RawCRSElements::checked_deserialize_parallel(
254 SERIALIZATION_COMPRESSION,
255 updated.as_slice(),
256 )?,
257 linking_proof: from_bytes::<DLogProof>(update_proof.as_slice())?,
258 })
259 });
260 Ok(Self(flatten_results(out)?))
261 }
262}
263
264impl Phase2RawCeremonyContribution {
265 pub fn validate(self, root: &Phase2CeremonyCRS) -> Option<Phase2CeremonyContribution> {
270 let data: [_; 7] = self
271 .0
272 .into_iter()
273 .zip(root.0.iter())
274 .collect::<Vec<_>>()
275 .try_into()
276 .expect("iterator should have the same size");
277 let out = transform_parallel(data, |(x, root)| {
278 x.validate(&mut OsRng, root)
279 .ok_or(anyhow!("failed to validate"))
280 });
281 Some(Phase2CeremonyContribution(flatten_results(out).ok()?))
282 }
283
284 pub fn assume_valid(self) -> Phase2CeremonyContribution {
288 Phase2CeremonyContribution(transform(self.0, |x| x.assume_valid()))
290 }
291
292 pub fn unchecked_from_protobuf(value: pb::participate_request::Contribution) -> Result<Self> {
293 let (parent_hashes, updated, update_proofs) = match value {
294 pb::participate_request::Contribution {
295 parent_hashes: Some(x0),
296 updated: Some(x1),
297 update_proofs: Some(x2),
298 } => (x0, x1, x2),
299 _ => anyhow::bail!("missing contribution data"),
300 };
301 let data = [
302 (parent_hashes.spend, updated.spend, update_proofs.spend),
303 (parent_hashes.output, updated.output, update_proofs.output),
304 (
305 parent_hashes.delegator_vote,
306 updated.delegator_vote,
307 update_proofs.delegator_vote,
308 ),
309 (
310 parent_hashes.undelegate_claim,
311 updated.undelegate_claim,
312 update_proofs.undelegate_claim,
313 ),
314 (parent_hashes.swap, updated.swap, update_proofs.swap),
315 (
316 parent_hashes.swap_claim,
317 updated.swap_claim,
318 update_proofs.swap_claim,
319 ),
320 (
321 parent_hashes.nullifer_derivation_crs,
322 updated.nullifer_derivation_crs,
323 update_proofs.nullifer_derivation_crs,
324 ),
325 ];
326 let out = transform(data, |(parent_hash, updated, update_proof)| {
327 Ok::<_, anyhow::Error>(Phase2RawContribution {
328 parent: ContributionHash::try_from(parent_hash.as_slice())?,
329 new_elements: from_bytes_unchecked::<Phase2RawCRSElements>(updated.as_slice())?,
330 linking_proof: from_bytes_unchecked::<DLogProof>(update_proof.as_slice())?,
331 })
332 });
333 Ok(Self(flatten_results(out)?))
334 }
335}
336
337#[derive(Clone, Debug)]
339pub struct Phase2CeremonyContribution([Phase2Contribution; NUM_CIRCUITS]);
340
341impl From<Phase2CeremonyContribution> for Phase2RawCeremonyContribution {
342 fn from(value: Phase2CeremonyContribution) -> Self {
343 let out: [Phase2RawContribution; NUM_CIRCUITS] =
344 array::from_fn(|i| Phase2RawContribution::from(value.0[i].clone()));
345 Self(out)
346 }
347}
348
349impl TryFrom<Phase2CeremonyContribution> for pb::participate_request::Contribution {
350 type Error = anyhow::Error;
351
352 fn try_from(data: Phase2CeremonyContribution) -> Result<pb::participate_request::Contribution> {
353 Phase2RawCeremonyContribution::from(data).try_into()
354 }
355}
356
357impl Phase2CeremonyContribution {
358 pub fn new_elements(&self) -> Phase2CeremonyCRS {
360 Phase2CeremonyCRS(array::from_fn(|i| self.0[i].new_elements.clone()))
361 }
362
363 #[must_use]
365 pub fn is_linked_to(&self, parent: &Phase2CeremonyCRS) -> bool {
366 self.0
367 .iter()
368 .zip(parent.0.iter())
369 .all(|(x, y)| x.is_linked_to(y))
370 }
371
372 pub fn make(old: &Phase2CeremonyCRS) -> Self {
373 let data = [
374 &old.0[0], &old.0[1], &old.0[2], &old.0[3], &old.0[4], &old.0[5], &old.0[6],
375 ];
376 Self(transform_parallel(data, |old_i| {
377 Phase2Contribution::make(&mut OsRng, ContributionHash::dummy(), old_i)
378 }))
379 }
380}
381
382impl Hashable for Phase2CeremonyContribution {
383 fn hash(&self) -> ContributionHash {
384 let hashes = transform(self.0.clone(), |x| x.hash());
385 let mut hasher = GroupHasher::new(b"phase2contr");
386 for h in hashes {
387 hasher.eat_bytes(h.as_ref());
388 }
389 ContributionHash(hasher.finalize_bytes())
390 }
391}
392
393#[derive(Clone, Debug)]
397pub struct Phase1RawCeremonyCRS([Phase1RawCRSElements; NUM_CIRCUITS]);
398
399impl Phase1RawCeremonyCRS {
400 pub fn assume_valid(self) -> Phase1CeremonyCRS {
404 match self.0 {
405 [x0, x1, x2, x3, x4, x5, x6] => Phase1CeremonyCRS([
406 x0.assume_valid(),
407 x1.assume_valid(),
408 x2.assume_valid(),
409 x3.assume_valid(),
410 x4.assume_valid(),
411 x5.assume_valid(),
412 x6.assume_valid(),
413 ]),
414 }
415 }
416
417 pub fn unchecked_from_protobuf(value: pb::CeremonyCrs) -> anyhow::Result<Self> {
419 Ok(Self([
420 from_bytes_unchecked::<Phase1RawCRSElements>(value.spend.as_slice())?,
421 from_bytes_unchecked::<Phase1RawCRSElements>(value.output.as_slice())?,
422 from_bytes_unchecked::<Phase1RawCRSElements>(value.delegator_vote.as_slice())?,
423 from_bytes_unchecked::<Phase1RawCRSElements>(value.undelegate_claim.as_slice())?,
424 from_bytes_unchecked::<Phase1RawCRSElements>(value.swap.as_slice())?,
425 from_bytes_unchecked::<Phase1RawCRSElements>(value.swap_claim.as_slice())?,
426 from_bytes_unchecked::<Phase1RawCRSElements>(value.nullifer_derivation_crs.as_slice())?,
427 ]))
428 }
429}
430
431impl TryInto<pb::CeremonyCrs> for Phase1RawCeremonyCRS {
432 type Error = anyhow::Error;
433
434 fn try_into(self) -> Result<pb::CeremonyCrs> {
435 Ok(pb::CeremonyCrs {
436 spend: to_bytes(&self.0[0])?,
437 output: to_bytes(&self.0[1])?,
438 delegator_vote: to_bytes(&self.0[2])?,
439 undelegate_claim: to_bytes(&self.0[3])?,
440 swap: to_bytes(&self.0[4])?,
441 swap_claim: to_bytes(&self.0[5])?,
442 nullifer_derivation_crs: to_bytes(&self.0[6])?,
443 })
444 }
445}
446
447impl TryFrom<pb::CeremonyCrs> for Phase1RawCeremonyCRS {
448 type Error = anyhow::Error;
449
450 fn try_from(value: pb::CeremonyCrs) -> std::result::Result<Self, Self::Error> {
451 Ok(Self([
452 from_bytes::<Phase1RawCRSElements>(value.spend.as_slice())?,
453 from_bytes::<Phase1RawCRSElements>(value.output.as_slice())?,
454 from_bytes::<Phase1RawCRSElements>(value.delegator_vote.as_slice())?,
455 from_bytes::<Phase1RawCRSElements>(value.undelegate_claim.as_slice())?,
456 from_bytes::<Phase1RawCRSElements>(value.swap.as_slice())?,
457 from_bytes::<Phase1RawCRSElements>(value.swap_claim.as_slice())?,
458 from_bytes::<Phase1RawCRSElements>(value.nullifer_derivation_crs.as_slice())?,
459 ]))
460 }
461}
462
463#[derive(Clone, Debug, PartialEq)]
465pub struct Phase1CeremonyCRS([Phase1CRSElements; NUM_CIRCUITS]);
466
467impl From<Phase1CeremonyCRS> for Phase1RawCeremonyCRS {
468 fn from(value: Phase1CeremonyCRS) -> Self {
469 Self(array::from_fn(|i| value.0[i].raw.clone()))
470 }
471}
472
473impl TryFrom<Phase1CeremonyCRS> for pb::CeremonyCrs {
474 type Error = anyhow::Error;
475
476 fn try_from(data: Phase1CeremonyCRS) -> Result<pb::CeremonyCrs> {
477 Phase1RawCeremonyCRS::from(data).try_into()
478 }
479}
480
481impl Phase1CeremonyCRS {
482 pub fn root() -> Result<Self> {
483 let [c0, c1, c2, c3, c4, c5, c6] = circuits();
484 Ok(Self([
485 Phase1CRSElements::root(circuit_degree(&c0)?),
486 Phase1CRSElements::root(circuit_degree(&c1)?),
487 Phase1CRSElements::root(circuit_degree(&c2)?),
488 Phase1CRSElements::root(circuit_degree(&c3)?),
489 Phase1CRSElements::root(circuit_degree(&c4)?),
490 Phase1CRSElements::root(circuit_degree(&c5)?),
491 Phase1CRSElements::root(circuit_degree(&c6)?),
492 ]))
493 }
494}
495
496#[derive(Clone, Debug)]
498pub struct Phase1RawCeremonyContribution([Phase1RawContribution; NUM_CIRCUITS]);
499
500impl TryInto<pb::participate_request::Contribution> for Phase1RawCeremonyContribution {
501 type Error = anyhow::Error;
502
503 fn try_into(self) -> Result<pb::participate_request::Contribution> {
504 Ok(pb::participate_request::Contribution {
505 updated: Some(pb::CeremonyCrs {
506 spend: to_bytes(&self.0[0].new_elements)?,
507 output: to_bytes(&self.0[1].new_elements)?,
508 delegator_vote: to_bytes(&self.0[2].new_elements)?,
509 undelegate_claim: to_bytes(&self.0[3].new_elements)?,
510 swap: to_bytes(&self.0[4].new_elements)?,
511 swap_claim: to_bytes(&self.0[5].new_elements)?,
512 nullifer_derivation_crs: to_bytes(&self.0[6].new_elements)?,
513 }),
514 update_proofs: Some(pb::CeremonyLinkingProof {
515 spend: to_bytes(&self.0[0].linking_proof)?,
516 output: to_bytes(&self.0[1].linking_proof)?,
517 delegator_vote: to_bytes(&self.0[2].linking_proof)?,
518 undelegate_claim: to_bytes(&self.0[3].linking_proof)?,
519 swap: to_bytes(&self.0[4].linking_proof)?,
520 swap_claim: to_bytes(&self.0[5].linking_proof)?,
521 nullifer_derivation_crs: to_bytes(&self.0[6].linking_proof)?,
522 }),
523 parent_hashes: Some(pb::CeremonyParentHashes {
524 spend: self.0[0].parent.0.to_vec(),
525 output: self.0[1].parent.0.to_vec(),
526 delegator_vote: self.0[2].parent.0.to_vec(),
527 undelegate_claim: self.0[3].parent.0.to_vec(),
528 swap: self.0[4].parent.0.to_vec(),
529 swap_claim: self.0[5].parent.0.to_vec(),
530 nullifer_derivation_crs: self.0[6].parent.0.to_vec(),
531 }),
532 })
533 }
534}
535
536impl TryFrom<pb::participate_request::Contribution> for Phase1RawCeremonyContribution {
537 type Error = anyhow::Error;
538
539 fn try_from(value: pb::participate_request::Contribution) -> Result<Self> {
540 let (parent_hashes, updated, update_proofs) = match value {
541 pb::participate_request::Contribution {
542 parent_hashes: Some(x0),
543 updated: Some(x1),
544 update_proofs: Some(x2),
545 } => (x0, x1, x2),
546 _ => anyhow::bail!("missing contribution data"),
547 };
548 let data = [
549 (parent_hashes.spend, updated.spend, update_proofs.spend),
550 (parent_hashes.output, updated.output, update_proofs.output),
551 (
552 parent_hashes.delegator_vote,
553 updated.delegator_vote,
554 update_proofs.delegator_vote,
555 ),
556 (
557 parent_hashes.undelegate_claim,
558 updated.undelegate_claim,
559 update_proofs.undelegate_claim,
560 ),
561 (parent_hashes.swap, updated.swap, update_proofs.swap),
562 (
563 parent_hashes.swap_claim,
564 updated.swap_claim,
565 update_proofs.swap_claim,
566 ),
567 (
568 parent_hashes.nullifer_derivation_crs,
569 updated.nullifer_derivation_crs,
570 update_proofs.nullifer_derivation_crs,
571 ),
572 ];
573 let out = transform_parallel(data, |(parent_hash, updated, update_proof)| {
574 Ok::<_, anyhow::Error>(Phase1RawContribution {
575 parent: ContributionHash::try_from(parent_hash.as_slice())?,
576 new_elements: Phase1RawCRSElements::checked_deserialize_parallel(
577 SERIALIZATION_COMPRESSION,
578 updated.as_slice(),
579 )?,
580 linking_proof: from_bytes::<LinkingProof>(update_proof.as_slice())?,
581 })
582 });
583 Ok(Self(flatten_results(out)?))
584 }
585}
586
587impl Phase1RawCeremonyContribution {
588 pub fn validate(self) -> Option<Phase1CeremonyContribution> {
593 let out = transform_parallel(self.0, |x| {
594 x.validate().ok_or(anyhow!("failed to validate"))
595 });
596 Some(Phase1CeremonyContribution(flatten_results(out).ok()?))
597 }
598
599 pub fn assume_valid(self) -> Phase1CeremonyContribution {
603 match self.0 {
605 [x0, x1, x2, x3, x4, x5, x6] => Phase1CeremonyContribution([
606 x0.assume_valid(),
607 x1.assume_valid(),
608 x2.assume_valid(),
609 x3.assume_valid(),
610 x4.assume_valid(),
611 x5.assume_valid(),
612 x6.assume_valid(),
613 ]),
614 }
615 }
616
617 pub fn unchecked_from_protobuf(value: pb::participate_request::Contribution) -> Result<Self> {
618 let (parent_hashes, updated, update_proofs) = match value {
619 pb::participate_request::Contribution {
620 parent_hashes: Some(x0),
621 updated: Some(x1),
622 update_proofs: Some(x2),
623 } => (x0, x1, x2),
624 _ => anyhow::bail!("missing contribution data"),
625 };
626 let data = [
627 (parent_hashes.spend, updated.spend, update_proofs.spend),
628 (parent_hashes.output, updated.output, update_proofs.output),
629 (
630 parent_hashes.delegator_vote,
631 updated.delegator_vote,
632 update_proofs.delegator_vote,
633 ),
634 (
635 parent_hashes.undelegate_claim,
636 updated.undelegate_claim,
637 update_proofs.undelegate_claim,
638 ),
639 (parent_hashes.swap, updated.swap, update_proofs.swap),
640 (
641 parent_hashes.swap_claim,
642 updated.swap_claim,
643 update_proofs.swap_claim,
644 ),
645 (
646 parent_hashes.nullifer_derivation_crs,
647 updated.nullifer_derivation_crs,
648 update_proofs.nullifer_derivation_crs,
649 ),
650 ];
651 let out = transform(data, |(parent_hash, updated, update_proof)| {
652 Ok::<_, anyhow::Error>(Phase1RawContribution {
653 parent: ContributionHash::try_from(parent_hash.as_slice())?,
654 new_elements: from_bytes_unchecked::<Phase1RawCRSElements>(updated.as_slice())?,
655 linking_proof: from_bytes_unchecked::<LinkingProof>(update_proof.as_slice())?,
656 })
657 });
658 Ok(Self(flatten_results(out)?))
659 }
660}
661
662#[derive(Clone, Debug)]
664pub struct Phase1CeremonyContribution([Phase1Contribution; NUM_CIRCUITS]);
665
666impl From<Phase1CeremonyContribution> for Phase1RawCeremonyContribution {
667 fn from(value: Phase1CeremonyContribution) -> Self {
668 let out: [Phase1RawContribution; NUM_CIRCUITS] =
669 array::from_fn(|i| Phase1RawContribution::from(value.0[i].clone()));
670 Self(out)
671 }
672}
673
674impl TryFrom<Phase1CeremonyContribution> for pb::participate_request::Contribution {
675 type Error = anyhow::Error;
676
677 fn try_from(data: Phase1CeremonyContribution) -> Result<pb::participate_request::Contribution> {
678 Phase1RawCeremonyContribution::from(data).try_into()
679 }
680}
681
682impl Phase1CeremonyContribution {
683 pub fn new_elements(&self) -> Phase1CeremonyCRS {
685 Phase1CeremonyCRS(array::from_fn(|i| self.0[i].new_elements.clone()))
686 }
687
688 #[must_use]
690 pub fn is_linked_to(&self, parent: &Phase1CeremonyCRS) -> bool {
691 self.0
692 .iter()
693 .zip(parent.0.iter())
694 .all(|(x, y)| x.is_linked_to(y))
695 }
696
697 pub fn make(old: &Phase1CeremonyCRS) -> Self {
698 let data = [
699 &old.0[0], &old.0[1], &old.0[2], &old.0[3], &old.0[4], &old.0[5], &old.0[6],
700 ];
701 Self(transform_parallel(data, |old_i| {
702 Phase1Contribution::make(&mut OsRng, ContributionHash::dummy(), old_i)
703 }))
704 }
705}
706
707impl Hashable for Phase1CeremonyContribution {
708 fn hash(&self) -> ContributionHash {
709 let hashes = transform(self.0.clone(), |x| x.hash());
710 let mut hasher = GroupHasher::new(b"phase1contr");
711 for h in hashes {
712 hasher.eat_bytes(h.as_ref());
713 }
714 ContributionHash(hasher.finalize_bytes())
715 }
716}
717
718#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)]
719pub struct AllExtraTransitionInformation([ExtraTransitionInformation; NUM_CIRCUITS]);
720
721impl AllExtraTransitionInformation {
722 pub fn to_bytes(&self) -> Result<Vec<u8>> {
723 to_bytes(self)
724 }
725
726 pub fn from_bytes(data: &[u8]) -> Result<Self> {
727 from_bytes_unchecked::<Self>(data)
728 }
729}
730
731pub fn transition(
733 phase1: &Phase1CeremonyCRS,
734) -> Result<(AllExtraTransitionInformation, Phase2CeremonyCRS)> {
735 let circuits = circuits();
736 let indices = [0, 1, 2, 3, 4, 5, 6];
737 let [(e0, p0), (e1, p1), (e2, p2), (e3, p3), (e4, p4), (e5, p5), (e6, p6)] =
738 flatten_results(transform_parallel(indices, |i| {
739 single::transition(&phase1.0[i], &circuits[i])
740 }))?;
741 Ok((
742 AllExtraTransitionInformation([e0, e1, e2, e3, e4, e5, e6]),
743 Phase2CeremonyCRS([p0, p1, p2, p3, p4, p5, p6]),
744 ))
745}
746
747pub fn combine(
748 phase1out: &Phase1CeremonyCRS,
749 phase2out: &Phase2CeremonyCRS,
750 extra: &AllExtraTransitionInformation,
751) -> [ProvingKey<Bls12_377>; NUM_CIRCUITS] {
752 let circuits = circuits();
753 let indices = [0, 1, 2, 3, 4, 5, 6];
754 transform_parallel(indices, |i| {
755 single::combine(&circuits[i], &phase1out.0[i], &phase2out.0[i], &extra.0[i])
756 })
757}