1use std::path::PathBuf;
16
17use anyhow::{anyhow, bail, Context, Result};
18use prost::{Message, Name};
19use risc0_binfmt::SystemState;
20use risc0_circuit_keccak::KeccakState;
21use risc0_zkp::core::digest::Digest;
22use serde::Serialize;
23
24use super::{malformed_err, path_to_string, pb, Asset, AssetRequest, RedisParams};
25use crate::{
26 claim::{receipt::UnionClaim, Unknown},
27 host::client::env::ProveKeccakRequest,
28 receipt::{
29 merkle::MerkleProof, CompositeReceipt, FakeReceipt, InnerAssumptionReceipt, InnerReceipt,
30 ReceiptMetadata, SegmentReceipt, SuccinctReceipt,
31 },
32 Assumption, Assumptions, ExitCode, GenericReceipt, Groth16Receipt, Input, Journal, MaybePruned,
33 Output, ProveInfo, ProverOpts, Receipt, ReceiptClaim, ReceiptKind, SessionStats, TraceEvent,
34 Work, WorkClaim,
35};
36
37mod ver {
38 use super::pb::base::CompatVersion;
39
40 pub const RECEIPT: CompatVersion = CompatVersion { value: 1 };
41 pub const SEGMENT_RECEIPT: CompatVersion = CompatVersion { value: 1 };
42 pub const SUCCINCT_RECEIPT: CompatVersion = CompatVersion { value: 1 };
43 pub const GROTH16_RECEIPT: CompatVersion = CompatVersion { value: 1 };
44}
45
46impl TryFrom<AssetRequest> for pb::api::AssetRequest {
47 type Error = anyhow::Error;
48
49 fn try_from(value: AssetRequest) -> Result<Self> {
50 Ok(Self {
51 kind: Some(match value {
52 AssetRequest::Inline => pb::api::asset_request::Kind::Inline(()),
53 AssetRequest::Path(path) => {
54 pb::api::asset_request::Kind::Path(path_to_string(path)?)
55 }
56 AssetRequest::Redis(params) => {
57 pb::api::asset_request::Kind::Redis(pb::api::RedisParams {
58 url: params.url,
59 key: params.key,
60 ttl: params.ttl,
61 })
62 }
63 }),
64 })
65 }
66}
67
68impl TryFrom<Asset> for pb::api::Asset {
69 type Error = anyhow::Error;
70
71 fn try_from(value: Asset) -> Result<Self> {
72 Ok(Self {
73 kind: match value {
74 Asset::Inline(bytes) => Some(pb::api::asset::Kind::Inline(bytes.into())),
75 Asset::Path(path) => Some(pb::api::asset::Kind::Path(path_to_string(path)?)),
76 Asset::Redis(key) => Some(pb::api::asset::Kind::Redis(key)),
77 },
78 })
79 }
80}
81
82impl<Claim> TryFrom<SuccinctReceipt<Claim>> for Asset
83where
84 Claim: Serialize,
85{
86 type Error = anyhow::Error;
87
88 fn try_from(succinct_receipt: SuccinctReceipt<Claim>) -> Result<Self> {
89 Ok(Asset::Inline(bincode::serialize(&succinct_receipt)?.into()))
90 }
91}
92
93impl TryFrom<SegmentReceipt> for Asset {
94 type Error = anyhow::Error;
95
96 fn try_from(segment_receipt: SegmentReceipt) -> Result<Self> {
97 Ok(Asset::Inline(bincode::serialize(&segment_receipt)?.into()))
98 }
99}
100
101impl TryFrom<Receipt> for Asset {
102 type Error = anyhow::Error;
103
104 fn try_from(receipt: Receipt) -> Result<Self> {
105 Ok(Asset::Inline(bincode::serialize(&receipt)?.into()))
106 }
107}
108
109impl TryFrom<pb::api::Asset> for Asset {
110 type Error = anyhow::Error;
111
112 fn try_from(value: pb::api::Asset) -> Result<Self> {
113 Ok(
114 match value.kind.ok_or_else(|| malformed_err("Asset.kind"))? {
115 pb::api::asset::Kind::Inline(bytes) => Asset::Inline(bytes.into()),
116 pb::api::asset::Kind::Path(path) => Asset::Path(PathBuf::from(path)),
117 pb::api::asset::Kind::Redis(key) => Asset::Redis(key),
118 },
119 )
120 }
121}
122
123impl TryFrom<pb::api::AssetRequest> for AssetRequest {
124 type Error = anyhow::Error;
125
126 fn try_from(value: pb::api::AssetRequest) -> Result<Self> {
127 Ok(
128 match value
129 .kind
130 .ok_or_else(|| malformed_err("AssetRequest.kind"))?
131 {
132 pb::api::asset_request::Kind::Inline(()) => AssetRequest::Inline,
133 pb::api::asset_request::Kind::Path(path) => {
134 AssetRequest::Path(std::path::PathBuf::from(path))
135 }
136 pb::api::asset_request::Kind::Redis(params) => AssetRequest::Redis(RedisParams {
137 url: params.url,
138 key: params.key,
139 ttl: params.ttl,
140 }),
141 },
142 )
143 }
144}
145
146impl TryFrom<TraceEvent> for pb::api::TraceEvent {
147 type Error = anyhow::Error;
148
149 fn try_from(event: TraceEvent) -> Result<Self> {
150 match event {
151 TraceEvent::InstructionStart { cycle, pc, insn } => Ok(Self {
152 kind: Some(pb::api::trace_event::Kind::InsnStart(
153 pb::api::trace_event::InstructionStart { cycle, pc, insn },
154 )),
155 }),
156 TraceEvent::RegisterSet { idx, value } => Ok(Self {
157 kind: Some(pb::api::trace_event::Kind::RegisterSet(
158 pb::api::trace_event::RegisterSet {
159 idx: idx as u32,
160 value,
161 },
162 )),
163 }),
164 TraceEvent::MemorySet { addr, region } => Ok(Self {
165 kind: Some(pb::api::trace_event::Kind::MemorySet(
166 pb::api::trace_event::MemorySet {
167 addr,
168 value: 0,
169 region,
170 },
171 )),
172 }),
173 TraceEvent::PageIn { cycles } => Ok(Self {
174 kind: Some(pb::api::trace_event::Kind::PageIn(
175 pb::api::trace_event::PageIn { cycles },
176 )),
177 }),
178 TraceEvent::PageOut { cycles } => Ok(Self {
179 kind: Some(pb::api::trace_event::Kind::PageOut(
180 pb::api::trace_event::PageOut { cycles },
181 )),
182 }),
183 _ => Err(anyhow!("unknown TraceEvent kind")),
184 }
185 }
186}
187
188impl TryFrom<pb::api::TraceEvent> for TraceEvent {
189 type Error = anyhow::Error;
190
191 fn try_from(event: pb::api::TraceEvent) -> Result<Self> {
192 Ok(
193 match event.kind.ok_or_else(|| malformed_err("TraceEvent.kind"))? {
194 pb::api::trace_event::Kind::InsnStart(event) => TraceEvent::InstructionStart {
195 cycle: event.cycle,
196 pc: event.pc,
197 insn: event.insn,
198 },
199 pb::api::trace_event::Kind::RegisterSet(event) => TraceEvent::RegisterSet {
200 idx: event.idx as usize,
201 value: event.value,
202 },
203 pb::api::trace_event::Kind::MemorySet(event) => TraceEvent::MemorySet {
204 addr: event.addr,
205 region: event.region,
206 },
207 pb::api::trace_event::Kind::PageIn(event) => TraceEvent::PageIn {
208 cycles: event.cycles,
209 },
210 pb::api::trace_event::Kind::PageOut(event) => TraceEvent::PageOut {
211 cycles: event.cycles,
212 },
213 },
214 )
215 }
216}
217
218impl TryFrom<ExitCode> for pb::base::ExitCode {
219 type Error = anyhow::Error;
220
221 fn try_from(value: ExitCode) -> Result<Self> {
222 Ok(Self {
223 kind: Some(match value {
224 ExitCode::SystemSplit => pb::base::exit_code::Kind::SystemSplit(()),
225 ExitCode::SessionLimit => pb::base::exit_code::Kind::SessionLimit(()),
226 ExitCode::Paused(code) => pb::base::exit_code::Kind::Paused(code),
227 ExitCode::Halted(code) => pb::base::exit_code::Kind::Halted(code),
228 e => bail!("unsupported exit code: {e:?}"),
229 }),
230 })
231 }
232}
233
234impl TryFrom<pb::base::ExitCode> for ExitCode {
235 type Error = anyhow::Error;
236
237 fn try_from(value: pb::base::ExitCode) -> Result<Self> {
238 Ok(
239 match value.kind.ok_or_else(|| malformed_err("ExitCode.kind"))? {
240 pb::base::exit_code::Kind::Halted(code) => Self::Halted(code),
241 pb::base::exit_code::Kind::Paused(code) => Self::Paused(code),
242 pb::base::exit_code::Kind::SystemSplit(_) => Self::SystemSplit,
243 pb::base::exit_code::Kind::SessionLimit(_) => Self::SessionLimit,
244 },
245 )
246 }
247}
248
249impl From<Result<(), anyhow::Error>> for pb::api::GenericReply {
250 fn from(result: Result<(), anyhow::Error>) -> Self {
251 Self {
252 kind: Some(match result {
253 Ok(_) => pb::api::generic_reply::Kind::Ok(()),
254 Err(err) => pb::api::generic_reply::Kind::Error(err.into()),
255 }),
256 }
257 }
258}
259
260impl From<anyhow::Error> for pb::api::GenericError {
261 fn from(err: anyhow::Error) -> Self {
262 Self {
263 reason: err.to_string(),
264 }
265 }
266}
267
268impl From<pb::api::GenericError> for anyhow::Error {
269 fn from(err: pb::api::GenericError) -> Self {
270 anyhow::Error::msg(err.reason)
271 }
272}
273
274impl TryFrom<pb::api::ProverOpts> for ProverOpts {
275 type Error = anyhow::Error;
276
277 fn try_from(opts: pb::api::ProverOpts) -> Result<Self> {
278 Ok(Self {
279 hashfn: opts.hashfn,
280 prove_guest_errors: opts.prove_guest_errors,
281 receipt_kind: match opts.receipt_kind {
282 0 => ReceiptKind::Composite,
283 1 => ReceiptKind::Succinct,
284 2 => ReceiptKind::Groth16,
285 value => panic!("Unknown receipt kind number: {value}"),
286 },
287 control_ids: opts
288 .control_ids
289 .into_iter()
290 .map(TryInto::try_into)
291 .collect::<Result<_>>()?,
292 max_segment_po2: opts
293 .max_segment_po2
294 .try_into()
295 .with_context(|| malformed_err("ProverOpts.max_segment_po2"))?,
296 dev_mode: opts.is_dev_mode,
297 })
298 }
299}
300
301impl From<ProverOpts> for pb::api::ProverOpts {
302 fn from(opts: ProverOpts) -> Self {
303 Self {
304 hashfn: opts.hashfn,
305 prove_guest_errors: opts.prove_guest_errors,
306 receipt_kind: opts.receipt_kind as i32,
307 control_ids: opts.control_ids.into_iter().map(Into::into).collect(),
308 max_segment_po2: opts.max_segment_po2 as u64,
309 is_dev_mode: opts.dev_mode,
310 }
311 }
312}
313
314impl From<semver::Version> for pb::base::SemanticVersion {
315 fn from(value: semver::Version) -> Self {
316 Self {
317 major: value.major,
318 minor: value.minor,
319 patch: value.patch,
320 pre: value.pre.to_string(),
321 build: value.build.to_string(),
322 }
323 }
324}
325
326impl TryFrom<pb::base::SemanticVersion> for semver::Version {
327 type Error = semver::Error;
328
329 fn try_from(value: pb::base::SemanticVersion) -> Result<Self, Self::Error> {
330 Ok(Self {
331 major: value.major,
332 minor: value.minor,
333 patch: value.patch,
334 pre: semver::Prerelease::new(&value.pre)?,
335 build: semver::BuildMetadata::new(&value.build)?,
336 })
337 }
338}
339
340impl From<SessionStats> for pb::core::SessionStats {
341 fn from(value: SessionStats) -> Self {
342 Self {
343 segments: value.segments.try_into().unwrap(),
345 total_cycles: value.total_cycles,
346 user_cycles: value.user_cycles,
347 paging_cycles: value.paging_cycles,
348 reserved_cycles: value.reserved_cycles,
349 }
350 }
351}
352
353impl TryFrom<pb::core::SessionStats> for SessionStats {
354 type Error = anyhow::Error;
355
356 fn try_from(value: pb::core::SessionStats) -> Result<Self> {
357 Ok(Self {
358 segments: value.segments.try_into()?,
359 total_cycles: value.total_cycles,
360 user_cycles: value.user_cycles,
361 paging_cycles: value.paging_cycles,
362 reserved_cycles: value.reserved_cycles,
363 })
364 }
365}
366
367impl TryFrom<ProveInfo> for pb::core::ProveInfo {
368 type Error = anyhow::Error;
369
370 fn try_from(value: ProveInfo) -> Result<Self> {
371 Ok(Self {
372 receipt: Some(value.receipt.try_into().context("ProveInfo.receipt")?),
373 work_receipt: value
374 .work_receipt
375 .map(|r| r.try_into())
376 .transpose()
377 .context("ProveInfo.work_receipt")?,
378 stats: Some(value.stats.into()),
379 })
380 }
381}
382
383impl TryFrom<pb::core::ProveInfo> for ProveInfo {
384 type Error = anyhow::Error;
385
386 fn try_from(value: pb::core::ProveInfo) -> Result<Self> {
387 Ok(Self {
388 receipt: value
389 .receipt
390 .ok_or_else(|| malformed_err("ProveInfo.receipt"))?
391 .try_into()?,
392 work_receipt: value
393 .work_receipt
394 .map(|r| r.try_into())
395 .transpose()
396 .context("ProveInfo.work_receipt")?,
397 stats: value
398 .stats
399 .ok_or_else(|| malformed_err("ProveInfo.stats"))?
400 .try_into()?,
401 })
402 }
403}
404
405impl TryFrom<Receipt> for pb::core::Receipt {
406 type Error = anyhow::Error;
407
408 fn try_from(value: Receipt) -> Result<Self> {
409 Ok(Self {
410 version: Some(ver::RECEIPT),
411 inner: Some(value.inner.try_into()?),
412 journal: value.journal.bytes,
413 metadata: Some(value.metadata.into()),
414 })
415 }
416}
417
418impl TryFrom<pb::core::Receipt> for Receipt {
419 type Error = anyhow::Error;
420
421 fn try_from(value: pb::core::Receipt) -> Result<Self> {
422 let version = value
423 .version
424 .ok_or_else(|| malformed_err("Receipt.version"))?
425 .value;
426 if version > ver::RECEIPT.value {
427 bail!("Incompatible Receipt version: {version}");
428 }
429 Ok(Self {
430 inner: value
431 .inner
432 .ok_or_else(|| malformed_err("Receipt.inner"))?
433 .try_into()?,
434 journal: Journal::new(value.journal),
435 metadata: value
436 .metadata
437 .ok_or_else(|| malformed_err("Receipt.metadata"))?
438 .try_into()?,
439 })
440 }
441}
442
443impl From<ReceiptMetadata> for pb::core::ReceiptMetadata {
444 fn from(value: ReceiptMetadata) -> Self {
445 Self {
446 verifier_parameters: Some(value.verifier_parameters.into()),
447 }
448 }
449}
450
451impl TryFrom<pb::core::ReceiptMetadata> for ReceiptMetadata {
452 type Error = anyhow::Error;
453
454 fn try_from(value: pb::core::ReceiptMetadata) -> Result<Self> {
455 Ok(Self {
456 verifier_parameters: value
457 .verifier_parameters
458 .ok_or_else(|| malformed_err("ReceiptMetadata.verifier_parameters"))?
459 .try_into()?,
460 })
461 }
462}
463
464impl TryFrom<SegmentReceipt> for pb::core::SegmentReceipt {
465 type Error = anyhow::Error;
466
467 fn try_from(value: SegmentReceipt) -> Result<Self> {
468 Ok(Self {
469 version: Some(ver::SEGMENT_RECEIPT),
470 seal: value.get_seal_bytes(),
471 index: value.index,
472 hashfn: value.hashfn,
473 claim: Some(value.claim.try_into()?),
474 verifier_parameters: Some(value.verifier_parameters.into()),
475 })
476 }
477}
478
479impl TryFrom<pb::core::SegmentReceipt> for SegmentReceipt {
480 type Error = anyhow::Error;
481
482 fn try_from(value: pb::core::SegmentReceipt) -> Result<Self> {
483 const WORD_SIZE: usize = std::mem::size_of::<u32>();
484
485 let mut seal = Vec::with_capacity(value.seal.len() / WORD_SIZE);
486 for chunk in value.seal.chunks_exact(WORD_SIZE) {
487 let word = u32::from_le_bytes(chunk.try_into()?);
488 seal.push(word);
489 }
490
491 let claim = value
492 .claim
493 .ok_or_else(|| malformed_err("SegmentReceipt.claim"))?
494 .try_into()?;
495
496 Ok(Self {
497 claim,
498 seal,
499 index: value.index,
500 hashfn: value.hashfn,
501 verifier_parameters: value
502 .verifier_parameters
503 .ok_or_else(|| malformed_err("SegmentReceipt.verifier_parameters"))?
504 .try_into()?,
505 })
506 }
507}
508
509impl<Claim> TryFrom<SuccinctReceipt<Claim>> for pb::core::SuccinctReceipt
510where
511 MaybePruned<Claim>: TryInto<pb::core::MaybePruned, Error = anyhow::Error>,
512{
513 type Error = anyhow::Error;
514
515 fn try_from(value: SuccinctReceipt<Claim>) -> Result<Self> {
516 Ok(Self {
517 version: Some(ver::SUCCINCT_RECEIPT),
518 seal: value.get_seal_bytes(),
519 control_id: Some(value.control_id.into()),
520 control_inclusion_proof: Some(value.control_inclusion_proof.into()),
521 claim: Some(value.claim.try_into()?),
522 hashfn: value.hashfn,
523 verifier_parameters: Some(value.verifier_parameters.into()),
524 })
525 }
526}
527
528impl<Claim> TryFrom<pb::core::SuccinctReceipt> for SuccinctReceipt<Claim>
529where
530 MaybePruned<Claim>: TryFrom<pb::core::MaybePruned, Error = anyhow::Error>,
531{
532 type Error = anyhow::Error;
533
534 fn try_from(value: pb::core::SuccinctReceipt) -> Result<Self> {
535 const WORD_SIZE: usize = std::mem::size_of::<u32>();
536
537 let version = value
538 .version
539 .ok_or_else(|| malformed_err("SuccinctReceipt.version"))?
540 .value;
541 if version > ver::SUCCINCT_RECEIPT.value {
542 bail!("Incompatible SuccinctReceipt version: {version}");
543 }
544
545 let mut seal = Vec::with_capacity(value.seal.len() / WORD_SIZE);
546 for chunk in value.seal.chunks_exact(WORD_SIZE) {
547 let word = u32::from_le_bytes(chunk.try_into()?);
548 seal.push(word);
549 }
550 Ok(Self {
551 seal,
552 control_id: value
553 .control_id
554 .ok_or_else(|| malformed_err("SuccinctReceipt.control_id"))?
555 .try_into()?,
556 control_inclusion_proof: value
557 .control_inclusion_proof
558 .ok_or_else(|| malformed_err("SuccinctReceipt.control_inclusion_proof"))?
559 .try_into()?,
560 claim: value
561 .claim
562 .ok_or_else(|| malformed_err("SuccinctReceipt.claim"))?
563 .try_into()?,
564 hashfn: value.hashfn,
565 verifier_parameters: value
566 .verifier_parameters
567 .ok_or_else(|| malformed_err("SuccinctReceipt.verifier_parameters"))?
568 .try_into()?,
569 })
570 }
571}
572
573impl From<MerkleProof> for pb::core::MerkleProof {
574 fn from(value: MerkleProof) -> Self {
575 Self {
576 index: value.index,
577 digests: value.digests.into_iter().map(Into::into).collect(),
578 }
579 }
580}
581
582impl TryFrom<pb::core::MerkleProof> for MerkleProof {
583 type Error = anyhow::Error;
584
585 fn try_from(value: pb::core::MerkleProof) -> Result<Self> {
586 Ok(Self {
587 index: value.index,
588 digests: value
589 .digests
590 .into_iter()
591 .map(TryInto::try_into)
592 .collect::<Result<_>>()?,
593 })
594 }
595}
596
597impl<Claim> TryFrom<Groth16Receipt<Claim>> for pb::core::Groth16Receipt
598where
599 MaybePruned<Claim>: TryInto<pb::core::MaybePruned, Error = anyhow::Error>,
600{
601 type Error = anyhow::Error;
602
603 fn try_from(value: Groth16Receipt<Claim>) -> Result<Self> {
604 Ok(Self {
605 version: Some(ver::GROTH16_RECEIPT),
606 seal: value.seal,
607 claim: Some(value.claim.try_into()?),
608 verifier_parameters: Some(value.verifier_parameters.into()),
609 })
610 }
611}
612
613impl<Claim> TryFrom<pb::core::Groth16Receipt> for Groth16Receipt<Claim>
614where
615 MaybePruned<Claim>: TryFrom<pb::core::MaybePruned, Error = anyhow::Error>,
616{
617 type Error = anyhow::Error;
618
619 fn try_from(value: pb::core::Groth16Receipt) -> Result<Self> {
620 let version = value
621 .version
622 .ok_or_else(|| malformed_err("Groth16Receipt.version"))?
623 .value;
624 if version > ver::GROTH16_RECEIPT.value {
625 bail!("Incompatible Groth16Receipt version: {version}");
626 }
627
628 Ok(Self {
629 seal: value.seal,
630 claim: value
631 .claim
632 .ok_or_else(|| malformed_err("Groth16Receipt.claim"))?
633 .try_into()?,
634 verifier_parameters: value
635 .verifier_parameters
636 .ok_or_else(|| malformed_err("Groth16Receipt.verifier_parameters"))?
637 .try_into()?,
638 })
639 }
640}
641
642impl TryFrom<InnerReceipt> for pb::core::InnerReceipt {
643 type Error = anyhow::Error;
644
645 fn try_from(value: InnerReceipt) -> Result<Self> {
646 Ok(Self {
647 kind: Some(match value {
648 InnerReceipt::Composite(inner) => {
649 pb::core::inner_receipt::Kind::Composite(inner.try_into()?)
650 }
651 InnerReceipt::Succinct(inner) => {
652 pb::core::inner_receipt::Kind::Succinct(inner.try_into()?)
653 }
654 InnerReceipt::Fake(inner) => {
655 pb::core::inner_receipt::Kind::Fake(pb::core::FakeReceipt {
656 claim: Some(inner.claim.try_into()?),
657 })
658 }
659 InnerReceipt::Groth16(inner) => {
660 pb::core::inner_receipt::Kind::Groth16(inner.try_into()?)
661 }
662 }),
663 })
664 }
665}
666
667impl TryFrom<pb::core::InnerReceipt> for InnerReceipt {
668 type Error = anyhow::Error;
669
670 fn try_from(value: pb::core::InnerReceipt) -> Result<Self> {
671 Ok(
672 match value
673 .kind
674 .ok_or_else(|| malformed_err("InnerReceipt.kind"))?
675 {
676 pb::core::inner_receipt::Kind::Composite(inner) => {
677 Self::Composite(inner.try_into()?)
678 }
679 pb::core::inner_receipt::Kind::Groth16(inner) => Self::Groth16(inner.try_into()?),
680 pb::core::inner_receipt::Kind::Succinct(inner) => Self::Succinct(inner.try_into()?),
681 pb::core::inner_receipt::Kind::Fake(inner) => Self::Fake(inner.try_into()?),
682 },
683 )
684 }
685}
686
687impl TryFrom<InnerAssumptionReceipt> for pb::core::InnerReceipt {
692 type Error = anyhow::Error;
693
694 fn try_from(value: InnerAssumptionReceipt) -> Result<Self> {
695 Ok(Self {
696 kind: Some(match value {
697 InnerAssumptionReceipt::Composite(inner) => {
698 pb::core::inner_receipt::Kind::Composite(inner.try_into()?)
699 }
700 InnerAssumptionReceipt::Succinct(inner) => {
701 pb::core::inner_receipt::Kind::Succinct(inner.try_into()?)
702 }
703 InnerAssumptionReceipt::Fake(inner) => {
704 pb::core::inner_receipt::Kind::Fake(pb::core::FakeReceipt {
705 claim: Some(inner.claim.try_into()?),
706 })
707 }
708 InnerAssumptionReceipt::Groth16(inner) => {
709 pb::core::inner_receipt::Kind::Groth16(inner.try_into()?)
710 }
711 }),
712 })
713 }
714}
715
716impl TryFrom<pb::core::InnerReceipt> for InnerAssumptionReceipt {
717 type Error = anyhow::Error;
718
719 fn try_from(value: pb::core::InnerReceipt) -> Result<Self> {
720 Ok(
721 match value
722 .kind
723 .ok_or_else(|| malformed_err("InnerReceipt.kind"))?
724 {
725 pb::core::inner_receipt::Kind::Composite(inner) => {
726 Self::Composite(inner.try_into()?)
727 }
728 pb::core::inner_receipt::Kind::Groth16(inner) => Self::Groth16(inner.try_into()?),
729 pb::core::inner_receipt::Kind::Succinct(inner) => Self::Succinct(inner.try_into()?),
730 pb::core::inner_receipt::Kind::Fake(inner) => Self::Fake(inner.try_into()?),
731 },
732 )
733 }
734}
735
736impl<Claim> TryFrom<GenericReceipt<Claim>> for pb::core::InnerReceipt
737where
738 MaybePruned<Claim>: TryInto<pb::core::MaybePruned, Error = anyhow::Error>,
739{
740 type Error = anyhow::Error;
741
742 fn try_from(value: GenericReceipt<Claim>) -> Result<Self> {
743 Ok(Self {
744 kind: Some(match value {
745 GenericReceipt::Succinct(inner) => {
746 pb::core::inner_receipt::Kind::Succinct(inner.try_into()?)
747 }
748 GenericReceipt::Fake(inner) => {
749 pb::core::inner_receipt::Kind::Fake(pb::core::FakeReceipt {
750 claim: Some(inner.claim.try_into()?),
751 })
752 }
753 GenericReceipt::Groth16(inner) => {
754 pb::core::inner_receipt::Kind::Groth16(inner.try_into()?)
755 }
756 }),
757 })
758 }
759}
760
761impl<Claim> TryFrom<pb::core::InnerReceipt> for GenericReceipt<Claim>
762where
763 MaybePruned<Claim>: TryFrom<pb::core::MaybePruned, Error = anyhow::Error>,
764{
765 type Error = anyhow::Error;
766
767 fn try_from(value: pb::core::InnerReceipt) -> Result<Self> {
768 Ok(
769 match value
770 .kind
771 .ok_or_else(|| malformed_err("InnerReceipt.kind"))?
772 {
773 pb::core::inner_receipt::Kind::Groth16(inner) => Self::Groth16(inner.try_into()?),
774 pb::core::inner_receipt::Kind::Succinct(inner) => Self::Succinct(inner.try_into()?),
775 pb::core::inner_receipt::Kind::Fake(inner) => Self::Fake(inner.try_into()?),
776 pb::core::inner_receipt::Kind::Composite(_) => return Err(malformed_err(
777 "cannot deserialize GenericReceipt from pb::InnerReceipt with composite kind",
778 )),
779 },
780 )
781 }
782}
783
784impl<Claim> TryFrom<FakeReceipt<Claim>> for pb::core::FakeReceipt
785where
786 MaybePruned<Claim>: TryInto<pb::core::MaybePruned, Error = anyhow::Error>,
787{
788 type Error = anyhow::Error;
789
790 fn try_from(value: FakeReceipt<Claim>) -> Result<Self> {
791 Ok(Self {
792 claim: Some(value.claim.try_into()?),
793 })
794 }
795}
796
797impl<Claim> TryFrom<pb::core::FakeReceipt> for FakeReceipt<Claim>
798where
799 MaybePruned<Claim>: TryFrom<pb::core::MaybePruned, Error = anyhow::Error>,
800{
801 type Error = anyhow::Error;
802
803 fn try_from(value: pb::core::FakeReceipt) -> Result<Self> {
804 Ok(Self {
805 claim: value
806 .claim
807 .ok_or_else(|| malformed_err("FakeReceipt.claim"))?
808 .try_into()?,
809 })
810 }
811}
812
813impl TryFrom<CompositeReceipt> for pb::core::CompositeReceipt {
814 type Error = anyhow::Error;
815
816 fn try_from(value: CompositeReceipt) -> Result<Self> {
817 Ok(Self {
818 segments: value
819 .segments
820 .into_iter()
821 .map(TryInto::try_into)
822 .collect::<Result<_>>()?,
823 assumption_receipts: value
824 .assumption_receipts
825 .into_iter()
826 .map(TryInto::try_into)
827 .collect::<Result<_>>()?,
828 verifier_parameters: Some(value.verifier_parameters.into()),
829 })
830 }
831}
832
833impl TryFrom<pb::core::CompositeReceipt> for CompositeReceipt {
834 type Error = anyhow::Error;
835
836 fn try_from(value: pb::core::CompositeReceipt) -> Result<Self> {
837 Ok(Self {
838 segments: value
839 .segments
840 .into_iter()
841 .map(|s| s.try_into())
842 .collect::<Result<Vec<_>>>()?,
843 assumption_receipts: value
844 .assumption_receipts
845 .into_iter()
846 .map(|a| a.try_into())
847 .collect::<Result<Vec<_>>>()?,
848 verifier_parameters: value
849 .verifier_parameters
850 .ok_or_else(|| malformed_err("CompositeReceipt.verifier_parameters"))?
851 .try_into()?,
852 })
853 }
854}
855
856impl From<Digest> for pb::base::Digest {
857 fn from(value: Digest) -> Self {
858 Self {
859 words: value.as_words().to_vec(),
860 }
861 }
862}
863
864impl TryFrom<pb::base::Digest> for Digest {
865 type Error = anyhow::Error;
866
867 fn try_from(value: pb::base::Digest) -> Result<Self> {
868 value
869 .words
870 .try_into()
871 .map_err(|_| anyhow!("invalid digest"))
872 }
873}
874
875impl Name for pb::core::UnionClaim {
876 const PACKAGE: &'static str = "risc0.protos.core";
877 const NAME: &'static str = "UnionClaim";
878}
879
880impl AssociatedMessage for UnionClaim {
881 type Message = pb::core::UnionClaim;
882}
883
884impl TryFrom<UnionClaim> for pb::core::UnionClaim {
885 type Error = anyhow::Error;
886
887 fn try_from(value: UnionClaim) -> Result<Self> {
888 Ok(Self {
889 left: Some(value.left.into()),
890 right: Some(value.right.into()),
891 })
892 }
893}
894
895impl TryFrom<pb::core::UnionClaim> for UnionClaim {
896 type Error = anyhow::Error;
897
898 fn try_from(value: pb::core::UnionClaim) -> Result<Self> {
899 Ok(Self {
900 left: value
901 .left
902 .ok_or_else(|| malformed_err("UnionClaim.left"))?
903 .try_into()?,
904 right: value
905 .right
906 .ok_or_else(|| malformed_err("UnionClaim.right"))?
907 .try_into()?,
908 })
909 }
910}
911
912impl Name for pb::core::WorkClaim {
913 const PACKAGE: &'static str = "risc0.protos.core";
914 const NAME: &'static str = "WorkClaim";
915}
916
917impl<Claim> AssociatedMessage for WorkClaim<Claim> {
918 type Message = pb::core::WorkClaim;
919}
920
921impl<Claim> TryFrom<WorkClaim<Claim>> for pb::core::WorkClaim
922where
923 MaybePruned<Claim>: TryInto<pb::core::MaybePruned, Error = anyhow::Error>,
924{
925 type Error = anyhow::Error;
926
927 fn try_from(value: WorkClaim<Claim>) -> Result<Self> {
928 Ok(Self {
929 claim: Some(value.claim.try_into().context("WorkClaim.claim")?),
930 work: Some(value.work.try_into().context("WorkClaim.work")?),
931 })
932 }
933}
934
935impl<Claim> TryFrom<pb::core::WorkClaim> for WorkClaim<Claim>
936where
937 MaybePruned<Claim>: TryFrom<pb::core::MaybePruned, Error = anyhow::Error>,
938{
939 type Error = anyhow::Error;
940
941 fn try_from(value: pb::core::WorkClaim) -> Result<Self> {
942 Ok(Self {
943 claim: value
944 .claim
945 .ok_or_else(|| malformed_err("WorkClaim.claim"))?
946 .try_into()
947 .context("WorkClaim.claim")?,
948 work: value
949 .work
950 .ok_or_else(|| malformed_err("WorkClaim.claim"))?
951 .try_into()
952 .context("WorkClaim.work")?,
953 })
954 }
955}
956
957impl Name for pb::core::Work {
958 const PACKAGE: &'static str = "risc0.protos.core";
959 const NAME: &'static str = "Work";
960}
961
962impl AssociatedMessage for Work {
963 type Message = pb::core::Work;
964}
965
966impl TryFrom<Work> for pb::core::Work {
967 type Error = anyhow::Error;
968
969 fn try_from(value: Work) -> Result<Self> {
970 Ok(Self {
971 nonce_min: value.nonce_min.to_bytes().to_vec(),
972 nonce_max: value.nonce_max.to_bytes().to_vec(),
973 value: value.value,
974 })
975 }
976}
977
978impl TryFrom<pb::core::Work> for Work {
979 type Error = anyhow::Error;
980
981 fn try_from(value: pb::core::Work) -> Result<Self> {
982 Ok(Self {
983 nonce_min: value.nonce_min.try_into().context("Work.nonce_min")?,
984 nonce_max: value.nonce_max.try_into().context("Work.nonce_max")?,
985 value: value.value,
986 })
987 }
988}
989
990impl Name for pb::core::ReceiptClaim {
991 const PACKAGE: &'static str = "risc0.protos.core";
992 const NAME: &'static str = "ReceiptClaim";
993}
994
995impl AssociatedMessage for ReceiptClaim {
996 type Message = pb::core::ReceiptClaim;
997}
998
999impl TryFrom<ReceiptClaim> for pb::core::ReceiptClaim {
1000 type Error = anyhow::Error;
1001
1002 fn try_from(value: ReceiptClaim) -> Result<Self> {
1003 Ok(Self {
1004 pre: Some(value.pre.try_into()?),
1005 post: Some(value.post.try_into()?),
1006 exit_code: Some(value.exit_code.try_into()?),
1007 input: match value.input {
1009 MaybePruned::Value(optional) => optional
1010 .map(|input| MaybePruned::Value(input).try_into())
1011 .transpose()?,
1012 MaybePruned::Pruned(digest) => {
1013 Some(MaybePruned::<Input>::Pruned(digest).try_into()?)
1014 }
1015 },
1016 output: match value.output {
1018 MaybePruned::Value(optional) => optional
1019 .map(|output| MaybePruned::Value(output).try_into())
1020 .transpose()?,
1021 MaybePruned::Pruned(digest) => {
1022 Some(MaybePruned::<Output>::Pruned(digest).try_into()?)
1023 }
1024 },
1025 })
1026 }
1027}
1028
1029impl TryFrom<pb::core::ReceiptClaim> for ReceiptClaim {
1030 type Error = anyhow::Error;
1031
1032 fn try_from(value: pb::core::ReceiptClaim) -> Result<Self> {
1033 Ok(Self {
1034 pre: value
1035 .pre
1036 .ok_or_else(|| malformed_err("ReceiptClaim.pre"))?
1037 .try_into()?,
1038 post: value
1039 .post
1040 .ok_or_else(|| malformed_err("ReceiptClaim.post"))?
1041 .try_into()?,
1042 exit_code: value
1043 .exit_code
1044 .ok_or_else(|| malformed_err("ReceiptClaim.exit_code"))?
1045 .try_into()?,
1046 input: match value.input {
1048 None => MaybePruned::Value(None),
1049 Some(x) => match MaybePruned::<Input>::try_from(x)? {
1050 #[allow(unreachable_patterns)]
1051 MaybePruned::Value(input) => MaybePruned::Value(Some(input)),
1052 MaybePruned::Pruned(digest) => MaybePruned::Pruned(digest),
1053 },
1054 },
1055 output: match value.output {
1057 None => MaybePruned::Value(None),
1058 Some(x) => match MaybePruned::<Output>::try_from(x)? {
1059 MaybePruned::Value(output) => MaybePruned::Value(Some(output)),
1060 MaybePruned::Pruned(digest) => MaybePruned::Pruned(digest),
1061 },
1062 },
1063 })
1064 }
1065}
1066
1067impl Name for pb::core::SystemState {
1068 const PACKAGE: &'static str = "risc0.protos.core";
1069 const NAME: &'static str = "SystemState";
1070}
1071
1072impl AssociatedMessage for SystemState {
1073 type Message = pb::core::SystemState;
1074}
1075
1076impl TryFrom<SystemState> for pb::core::SystemState {
1077 type Error = anyhow::Error;
1078
1079 fn try_from(value: SystemState) -> Result<Self> {
1080 Ok(Self {
1081 pc: value.pc,
1082 merkle_root: Some(value.merkle_root.into()),
1083 })
1084 }
1085}
1086
1087impl TryFrom<pb::core::SystemState> for SystemState {
1088 type Error = anyhow::Error;
1089
1090 fn try_from(value: pb::core::SystemState) -> Result<Self> {
1091 Ok(Self {
1092 pc: value.pc,
1093 merkle_root: value
1094 .merkle_root
1095 .ok_or_else(|| malformed_err("SystemState.merkle_root"))?
1096 .try_into()?,
1097 })
1098 }
1099}
1100
1101impl Name for pb::core::Input {
1102 const PACKAGE: &'static str = "risc0.protos.core";
1103 const NAME: &'static str = "Input";
1104}
1105
1106impl AssociatedMessage for Input {
1107 type Message = pb::core::Input;
1108}
1109
1110impl TryFrom<Input> for pb::core::Input {
1111 type Error = anyhow::Error;
1112
1113 fn try_from(value: Input) -> Result<Self> {
1114 match value.x { }
1115 }
1116}
1117
1118impl TryFrom<pb::core::Input> for Input {
1119 type Error = anyhow::Error;
1120
1121 fn try_from(_value: pb::core::Input) -> Result<Self> {
1122 Err(malformed_err("Input"))
1123 }
1124}
1125
1126impl Name for pb::core::Output {
1127 const PACKAGE: &'static str = "risc0.protos.core";
1128 const NAME: &'static str = "Output";
1129}
1130
1131impl AssociatedMessage for Output {
1132 type Message = pb::core::Output;
1133}
1134
1135impl TryFrom<Output> for pb::core::Output {
1136 type Error = anyhow::Error;
1137
1138 fn try_from(value: Output) -> Result<Self> {
1139 Ok(Self {
1140 journal: Some(value.journal.try_into()?),
1141 assumptions: Some(value.assumptions.try_into()?),
1142 })
1143 }
1144}
1145
1146impl TryFrom<pb::core::Output> for Output {
1147 type Error = anyhow::Error;
1148
1149 fn try_from(value: pb::core::Output) -> Result<Self> {
1150 Ok(Self {
1151 journal: value
1152 .journal
1153 .ok_or_else(|| malformed_err("Output.journal"))?
1154 .try_into()?,
1155 assumptions: value
1156 .assumptions
1157 .ok_or_else(|| malformed_err("Output.assumptions"))?
1158 .try_into()?,
1159 })
1160 }
1161}
1162
1163impl Name for pb::core::Assumption {
1164 const PACKAGE: &'static str = "risc0.protos.core";
1165 const NAME: &'static str = "Assumption";
1166}
1167
1168impl AssociatedMessage for Assumption {
1169 type Message = pb::core::Assumption;
1170}
1171
1172impl TryFrom<Assumption> for pb::core::Assumption {
1173 type Error = anyhow::Error;
1174
1175 fn try_from(value: Assumption) -> Result<Self> {
1176 Ok(Self {
1177 claim: Some(value.claim.into()),
1178 control_root: Some(value.control_root.into()),
1179 })
1180 }
1181}
1182
1183impl TryFrom<pb::core::Assumption> for Assumption {
1184 type Error = anyhow::Error;
1185
1186 fn try_from(value: pb::core::Assumption) -> Result<Self> {
1187 Ok(Self {
1188 claim: value
1189 .claim
1190 .ok_or_else(|| malformed_err("Assumption.claim"))?
1191 .try_into()?,
1192 control_root: value
1193 .control_root
1194 .ok_or_else(|| malformed_err("Assumption.control_root"))?
1195 .try_into()?,
1196 })
1197 }
1198}
1199
1200impl Name for pb::core::Assumptions {
1201 const PACKAGE: &'static str = "risc0.protos.core";
1202 const NAME: &'static str = "Assumptions";
1203}
1204
1205impl AssociatedMessage for Assumptions {
1206 type Message = pb::core::Assumptions;
1207}
1208
1209impl TryFrom<Assumptions> for pb::core::Assumptions {
1210 type Error = anyhow::Error;
1211
1212 fn try_from(value: Assumptions) -> Result<Self> {
1213 Ok(Self {
1214 inner: value
1215 .0
1216 .into_iter()
1217 .map(|a| a.try_into())
1218 .collect::<Result<_>>()?,
1219 })
1220 }
1221}
1222
1223impl TryFrom<pb::core::Assumptions> for Assumptions {
1224 type Error = anyhow::Error;
1225
1226 fn try_from(value: pb::core::Assumptions) -> Result<Self> {
1227 Ok(Self(
1228 value
1229 .inner
1230 .into_iter()
1231 .map(|a| a.try_into())
1232 .collect::<Result<Vec<_>>>()?,
1233 ))
1234 }
1235}
1236
1237trait AssociatedMessage {
1238 type Message: Message;
1239}
1240
1241impl<T> TryFrom<MaybePruned<T>> for pb::core::MaybePruned
1242where
1243 T: AssociatedMessage + serde::Serialize + Clone,
1244 T::Message: TryFrom<T, Error = anyhow::Error> + Sized,
1245{
1246 type Error = anyhow::Error;
1247
1248 fn try_from(value: MaybePruned<T>) -> Result<Self> {
1249 Ok(Self {
1250 kind: Some(match value {
1251 MaybePruned::Value(inner) => pb::core::maybe_pruned::Kind::Value(
1252 T::Message::try_from(inner)?.encode_to_vec(),
1253 ),
1254 MaybePruned::Pruned(digest) => pb::core::maybe_pruned::Kind::Pruned(digest.into()),
1255 }),
1256 })
1257 }
1258}
1259
1260impl<T> TryFrom<pb::core::MaybePruned> for MaybePruned<T>
1261where
1262 T: AssociatedMessage + serde::Serialize + Clone,
1263 T::Message: TryInto<T, Error = anyhow::Error> + Default,
1264{
1265 type Error = anyhow::Error;
1266
1267 fn try_from(value: pb::core::MaybePruned) -> Result<Self> {
1268 Ok(
1269 match value
1270 .kind
1271 .ok_or_else(|| malformed_err("MaybePruned<T>.kind"))?
1272 {
1273 pb::core::maybe_pruned::Kind::Value(inner) => {
1274 Self::Value(T::Message::decode(inner.as_slice())?.try_into()?)
1275 }
1276 pb::core::maybe_pruned::Kind::Pruned(digest) => Self::Pruned(digest.try_into()?),
1277 },
1278 )
1279 }
1280}
1281
1282impl TryFrom<MaybePruned<Vec<u8>>> for pb::core::MaybePruned {
1285 type Error = anyhow::Error;
1286
1287 fn try_from(value: MaybePruned<Vec<u8>>) -> Result<Self> {
1288 Ok(Self {
1289 kind: Some(match value {
1290 MaybePruned::Value(inner) => {
1291 pb::core::maybe_pruned::Kind::Value(inner.encode_to_vec())
1292 }
1293 MaybePruned::Pruned(digest) => pb::core::maybe_pruned::Kind::Pruned(digest.into()),
1294 }),
1295 })
1296 }
1297}
1298
1299impl TryFrom<pb::core::MaybePruned> for MaybePruned<Vec<u8>> {
1300 type Error = anyhow::Error;
1301
1302 fn try_from(value: pb::core::MaybePruned) -> Result<Self> {
1303 Ok(
1304 match value
1305 .kind
1306 .ok_or_else(|| malformed_err("MaybePruned<Vec<u8>>.kind"))?
1307 {
1308 pb::core::maybe_pruned::Kind::Value(inner) => {
1309 Self::Value(<Vec<u8> as Message>::decode(inner.as_slice())?)
1310 }
1311 pb::core::maybe_pruned::Kind::Pruned(digest) => Self::Pruned(digest.try_into()?),
1312 },
1313 )
1314 }
1315}
1316
1317impl TryFrom<MaybePruned<Unknown>> for pb::core::MaybePruned {
1318 type Error = anyhow::Error;
1319
1320 fn try_from(value: MaybePruned<Unknown>) -> Result<Self> {
1321 Ok(Self {
1322 kind: Some(match value {
1323 #[allow(unreachable_patterns)]
1324 MaybePruned::Value(inner) => {
1325 match inner { }
1326 }
1327 MaybePruned::Pruned(digest) => pb::core::maybe_pruned::Kind::Pruned(digest.into()),
1328 }),
1329 })
1330 }
1331}
1332
1333impl TryFrom<pb::core::MaybePruned> for MaybePruned<Unknown> {
1334 type Error = anyhow::Error;
1335
1336 fn try_from(value: pb::core::MaybePruned) -> Result<Self> {
1337 Ok(
1338 match value
1339 .kind
1340 .ok_or_else(|| malformed_err("MaybePruned<Unknown>.kind"))?
1341 {
1342 pb::core::maybe_pruned::Kind::Value(_) => {
1343 Err(malformed_err("MaybePruned<Unknown>.value"))?
1344 }
1345 pb::core::maybe_pruned::Kind::Pruned(digest) => Self::Pruned(digest.try_into()?),
1346 },
1347 )
1348 }
1349}
1350
1351impl TryFrom<pb::api::ProveKeccakRequest> for ProveKeccakRequest {
1352 type Error = anyhow::Error;
1353
1354 fn try_from(value: pb::api::ProveKeccakRequest) -> Result<Self> {
1355 let input = try_keccak_bytes_to_input(&value.input)?;
1356
1357 Ok(Self {
1358 claim_digest: value
1359 .claim_digest
1360 .ok_or_else(|| malformed_err("ProveKeccakRequest.claim_digest"))?
1361 .try_into()?,
1362 po2: value.po2 as usize,
1363 control_root: value
1364 .control_root
1365 .ok_or_else(|| malformed_err("ProveKeccakRequest.control_root"))?
1366 .try_into()?,
1367 input,
1368 })
1369 }
1370}
1371
1372pub(crate) fn keccak_input_to_bytes(input: &[KeccakState]) -> Vec<u8> {
1373 bytemuck::cast_slice(input).to_vec()
1374}
1375
1376pub(crate) fn try_keccak_bytes_to_input(input: &[u8]) -> Result<Vec<KeccakState>> {
1377 let chunks = input.chunks_exact(std::mem::size_of::<KeccakState>());
1378 if !chunks.remainder().is_empty() {
1379 bail!("Input length must be a multiple of KeccakState size");
1380 }
1381 chunks
1382 .map(bytemuck::try_pod_read_unaligned)
1383 .collect::<Result<_, _>>()
1384 .map_err(|e| anyhow!("Failed to convert input bytes to KeccakState: {}", e))
1385}
1386
1387#[test]
1388fn test_keccak_bytes_to_input_alignment() {
1389 let padding = 16; let keccak_states = 3; let state_size = std::mem::size_of::<KeccakState>();
1393 let mut test_buffer = vec![0u8; padding + (keccak_states * state_size)];
1394
1395 for (i, byte) in test_buffer.iter_mut().enumerate() {
1397 *byte = (i % 256) as u8;
1398 }
1399
1400 for offset in 0..padding {
1402 let aligned_slice = &test_buffer[offset..][..(keccak_states * state_size)];
1403 let result = try_keccak_bytes_to_input(aligned_slice);
1404
1405 assert!(result.is_ok(), "Failed to parse at offset {offset}");
1406 let states = result.unwrap();
1407 assert_eq!(
1408 states.len(),
1409 keccak_states,
1410 "Wrong number of states at offset {offset}",
1411 );
1412
1413 let bytes = keccak_input_to_bytes(&states);
1415 assert_eq!(bytes, aligned_slice, "Roundtrip failed at offset {offset}",);
1416 }
1417}
1418
1419#[test]
1420fn test_keccak_bytes_to_input_invalid_size() {
1421 let invalid_size = std::mem::size_of::<KeccakState>() + 1;
1423 let buffer = vec![0u8; invalid_size];
1424 let result = try_keccak_bytes_to_input(&buffer);
1425 assert!(result.is_err(), "Should fail with invalid size");
1426}