risc0_zkvm/host/api/
convert.rs

1// Copyright 2025 RISC Zero, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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            // we use usize because that's the type returned by len() for vectors
344            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
687// NOTE: InnerReceipt and InnerAssumptionReceipt are the same type in protobuf.
688// In Rust, they are distinct types because Rust needs to size everything on the
689// stack and e.g. SuccinctReceipt<ReceiptClaim> and SuccinctReceipt<Unknown>
690// have different sizes. Protobuf handles this without issue.
691impl 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            // Translate MaybePruned<Option<Input>>> to Option<MaybePruned<Input>>.
1008            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            // Translate MaybePruned<Option<Output>>> to Option<MaybePruned<Output>>.
1017            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            // Translate Option<MaybePruned<Input>> to MaybePruned<Option<Input>>.
1047            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            // Translate Option<MaybePruned<Output>> to MaybePruned<Option<Output>>.
1056            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 { /* unreachable  */ }
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
1282// Specialized implementation for Vec<u8> for work around challenges getting the
1283// generic implementation above to work for Vec<u8>.
1284impl 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 { /* unreachable */ }
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    // Create a buffer with extra padding at the start to test different alignments
1390    let padding = 16; // We should hit all alignments by cycling through offsets 0-15
1391    let keccak_states = 3; // Test multiple KeccakStates
1392    let state_size = std::mem::size_of::<KeccakState>();
1393    let mut test_buffer = vec![0u8; padding + (keccak_states * state_size)];
1394
1395    // Fill with recognizable pattern
1396    for (i, byte) in test_buffer.iter_mut().enumerate() {
1397        *byte = (i % 256) as u8;
1398    }
1399
1400    // Test each offset
1401    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        // Verify roundtrip
1414        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    // Test with a buffer that's not a multiple of KeccakState size
1422    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}