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