Skip to main content

sp1_hypercube/air/
public_values.rs

1use core::{fmt::Debug, mem::size_of};
2use std::{
3    borrow::{Borrow, BorrowMut},
4    ops::Range,
5};
6
7use deepsize2::DeepSizeOf;
8use itertools::Itertools;
9use serde::{Deserialize, Serialize};
10use slop_algebra::{AbstractField, PrimeField32};
11use sp1_primitives::consts::split_page_idx;
12
13use crate::{septic_curve::SepticCurve, septic_digest::SepticDigest, PROOF_MAX_NUM_PVS};
14
15#[cfg(feature = "mprotect")]
16use crate::addr_to_limbs;
17
18/// The number of non padded elements in the SP1 proofs public values vec.
19pub const SP1_PROOF_NUM_PV_ELTS: usize = size_of::<PublicValues<[u8; 4], [u8; 3], [u8; 4], u8>>();
20
21/// The number of 32 bit words in the SP1 proof's committed value digest.
22pub const PV_DIGEST_NUM_WORDS: usize = 8;
23
24/// The number of field elements in the poseidon2 digest.
25pub const POSEIDON_NUM_WORDS: usize = 8;
26
27/// The number of 32 bit words in the SP1 proof's proof nonce.
28pub const PROOF_NONCE_NUM_WORDS: usize = 4;
29
30/// Stores all of a shard proof's public values.
31#[derive(Serialize, Deserialize, Clone, Copy, Default, Debug, PartialEq, Eq, DeepSizeOf)]
32#[repr(C)]
33pub struct PublicValues<W1, W2, W3, T> {
34    /// The `committed_value_digest` value before this shard.
35    pub prev_committed_value_digest: [W1; PV_DIGEST_NUM_WORDS],
36
37    /// The hash of all the bytes that the guest program has written to public values.
38    pub committed_value_digest: [W1; PV_DIGEST_NUM_WORDS],
39
40    /// The `deferred_proof_digest` value before this shard.
41    pub prev_deferred_proofs_digest: [T; POSEIDON_NUM_WORDS],
42
43    /// The hash of all deferred proofs that have been witnessed in the VM. It will be rebuilt in
44    /// recursive verification as the proofs get verified. The hash itself is a rolling poseidon2
45    /// hash of each proof+vkey hash and the previous hash which is initially zero.
46    pub deferred_proofs_digest: [T; POSEIDON_NUM_WORDS],
47
48    /// The shard's start program counter.
49    pub pc_start: W2,
50
51    /// The expected start program counter for the next shard.
52    pub next_pc: W2,
53
54    /// The expected exit code of the program before this shard.
55    pub prev_exit_code: T,
56
57    /// The expected exit code code of the program up to this shard.
58    /// This value is only valid if halt has been executed.
59    pub exit_code: T,
60
61    /// Whether or not the current shard is an execution shard.
62    pub is_execution_shard: T,
63
64    /// The largest address that is witnessed for initialization in the previous shard.
65    pub previous_init_addr: W2,
66
67    /// The largest address that is witnessed for initialization in the current shard.
68    pub last_init_addr: W2,
69
70    /// The largest address that is witnessed for finalization in the previous shard.
71    pub previous_finalize_addr: W2,
72
73    /// The largest address that is witnessed for finalization in the current shard.
74    pub last_finalize_addr: W2,
75
76    /// The largest page idx that is witnessed for initialization in the previous shard.
77    pub previous_init_page_idx: W2,
78
79    /// The largest page idx that is witnessed for initialization in the current shard.
80    pub last_init_page_idx: W2,
81
82    /// The largest page idx that is witnessed for finalization in the previous shard.
83    pub previous_finalize_page_idx: W2,
84
85    /// The largest page idx that is witnessed for finalization in the current shard.
86    pub last_finalize_page_idx: W2,
87
88    /// The initial timestamp of the shard.
89    pub initial_timestamp: W3,
90
91    /// The last timestamp of the shard.
92    pub last_timestamp: W3,
93
94    /// If the high bits of timestamp is equal in this shard.
95    pub is_timestamp_high_eq: T,
96
97    /// The inverse of the difference of the high bits of timestamp.
98    pub inv_timestamp_high: T,
99
100    /// If the low bits of timestamp is equal in this shard.
101    pub is_timestamp_low_eq: T,
102
103    /// The inverse of the difference of the low bits of timestamp.
104    pub inv_timestamp_low: T,
105
106    /// The number of global memory initializations in the shard.
107    pub global_init_count: T,
108
109    /// The number of global memory finalizations in the shard.
110    pub global_finalize_count: T,
111
112    /// The number of global page prot initializations in the shard.
113    pub global_page_prot_init_count: T,
114
115    /// The number of global page prot finalizations in the shard.
116    pub global_page_prot_finalize_count: T,
117
118    /// The number of global interactions in the shard.
119    pub global_count: T,
120
121    /// The global cumulative sum of the shard.
122    pub global_cumulative_sum: SepticDigest<T>,
123
124    /// The `commit_syscall` value of the previous shard.
125    pub prev_commit_syscall: T,
126
127    /// Whether `COMMIT` syscall has been called up to this shard.
128    pub commit_syscall: T,
129
130    /// The `commit_deferred_syscall` value of the previous shard.
131    pub prev_commit_deferred_syscall: T,
132
133    /// Whether `COMMIT_DEFERRED` syscall has been called up to this shard.
134    pub commit_deferred_syscall: T,
135
136    /// The inverse to show that `initial_timestamp != 1` in the shards that aren't the first one.
137    pub initial_timestamp_inv: T,
138
139    /// The inverse to show that `last_timestamp != 1` in all shards.
140    pub last_timestamp_inv: T,
141
142    /// Whether or not this shard is the first shard of the proof.
143    pub is_first_execution_shard: T,
144
145    /// Whether untrusted program support is enabled.  This specifically will enable fetching
146    /// instructions from memory during runtime and checking/setting page permissions.
147    pub is_untrusted_programs_enabled: T,
148
149    /// Whether or not a trap handler exists.
150    #[cfg(feature = "mprotect")]
151    pub enable_trap_handler: T,
152
153    /// The trap context addresses (addr, addr+8, addr+16), each as 3 limbs.
154    #[cfg(feature = "mprotect")]
155    pub trap_context: [W2; 3],
156
157    /// The untrusted memory region (start, end), each as 3 limbs.
158    #[cfg(feature = "mprotect")]
159    pub untrusted_memory: [W2; 2],
160
161    /// The nonce used for this proof.
162    pub proof_nonce: [T; PROOF_NONCE_NUM_WORDS],
163
164    /// This field is here to ensure that the size of the public values struct is a multiple of 8.
165    pub empty: [T; 4],
166}
167
168impl PublicValues<u32, u64, u64, u32> {
169    /// Convert the public values into a vector of field elements.  This function will pad the
170    /// vector to the maximum number of public values.
171    #[must_use]
172    pub fn to_vec<F: AbstractField>(&self) -> Vec<F> {
173        let mut ret = vec![F::zero(); PROOF_MAX_NUM_PVS];
174
175        let field_values = PublicValues::<[F; 4], [F; 3], [F; 4], F>::from(*self);
176        let ret_ref_mut: &mut PublicValues<[F; 4], [F; 3], [F; 4], F> =
177            ret.as_mut_slice().borrow_mut();
178        *ret_ref_mut = field_values;
179        ret
180    }
181
182    /// Get the range of the shard.
183    ///
184    /// TODO: deprecate this once recursion is fully streaming.
185    #[must_use]
186    pub fn range(&self) -> ShardRange {
187        ShardRange {
188            timestamp_range: (self.initial_timestamp, self.last_timestamp),
189            initialized_address_range: (self.previous_init_addr, self.last_init_addr),
190            finalized_address_range: (self.previous_finalize_addr, self.last_finalize_addr),
191            initialized_page_index_range: (self.previous_init_page_idx, self.last_init_page_idx),
192            finalized_page_index_range: (
193                self.previous_finalize_page_idx,
194                self.last_finalize_page_idx,
195            ),
196            deferred_proof_range: (0, 0),
197        }
198    }
199
200    /// Resets the public values to zero.
201    #[must_use]
202    pub fn reset(&self) -> Self {
203        let mut copy = *self;
204        copy.pc_start = 0;
205        copy.next_pc = 0;
206        copy.previous_init_addr = 0;
207        copy.last_init_addr = 0;
208        copy.previous_finalize_addr = 0;
209        copy.last_finalize_addr = 0;
210        copy.previous_init_page_idx = 0;
211        copy.last_init_page_idx = 0;
212        copy.previous_finalize_page_idx = 0;
213        copy.last_finalize_page_idx = 0;
214        copy
215    }
216
217    /// Get the public values corresponding to initial state of the program for a non-execution
218    /// shard.
219    #[must_use]
220    #[cfg_attr(not(feature = "mprotect"), allow(unused_variables))]
221    pub fn initialize(
222        &self,
223        pc_start_abs: u64,
224        enable_untrusted_programs: bool,
225        trap_context: Option<u64>,
226        untrusted_memory: Option<(u64, u64)>,
227    ) -> Self {
228        let mut state = *self;
229        state.pc_start = pc_start_abs;
230        state.next_pc = pc_start_abs;
231        state.initial_timestamp = 1;
232        state.last_timestamp = 1;
233        state.is_timestamp_high_eq = 1;
234        state.is_timestamp_low_eq = 1;
235        state.is_first_execution_shard = 0;
236        state.is_execution_shard = 0;
237        state.initial_timestamp_inv = 0;
238        state.last_timestamp_inv = 0;
239        state.is_untrusted_programs_enabled = enable_untrusted_programs as u32;
240        #[cfg(feature = "mprotect")]
241        {
242            state.enable_trap_handler = trap_context.is_some() as u32;
243            state.trap_context = trap_context.map_or([0, 0, 0], |addr| [addr, addr + 8, addr + 16]);
244            state.untrusted_memory = untrusted_memory.map_or([0, 0], |(start, end)| [start, end]);
245        }
246        state
247    }
248
249    /// Update the public values to the state.
250    pub fn update_state(&mut self, state: &PublicValues<u32, u64, u64, u32>) {
251        self.pc_start = state.pc_start;
252        self.next_pc = state.next_pc;
253        self.exit_code = state.exit_code;
254        self.initial_timestamp = state.initial_timestamp;
255        self.last_timestamp = state.last_timestamp;
256        self.is_timestamp_high_eq = state.is_timestamp_high_eq;
257        self.is_timestamp_low_eq = state.is_timestamp_low_eq;
258        self.last_timestamp_inv = state.last_timestamp_inv;
259        self.initial_timestamp_inv = state.initial_timestamp_inv;
260        self.is_first_execution_shard = state.is_first_execution_shard;
261        self.is_execution_shard = state.is_execution_shard;
262        self.is_untrusted_programs_enabled = state.is_untrusted_programs_enabled;
263        #[cfg(feature = "mprotect")]
264        {
265            self.enable_trap_handler = state.enable_trap_handler;
266            self.trap_context = state.trap_context;
267            self.untrusted_memory = state.untrusted_memory;
268        }
269    }
270
271    /// Update the public values to the state, as a non-execution shard in the initial state of the
272    /// program's execution.
273    #[cfg_attr(not(feature = "mprotect"), allow(unused_variables))]
274    pub fn update_initialized_state(
275        &mut self,
276        pc_start_abs: u64,
277        enable_untrusted_programs: bool,
278        trap_context: Option<u64>,
279        untrusted_memory: Option<(u64, u64)>,
280    ) {
281        self.pc_start = pc_start_abs;
282        self.next_pc = pc_start_abs;
283        self.exit_code = 0;
284        self.initial_timestamp = 1;
285        self.last_timestamp = 1;
286        self.is_timestamp_high_eq = 1;
287        self.is_timestamp_low_eq = 1;
288        self.is_first_execution_shard = 0;
289        self.is_execution_shard = 0;
290        self.initial_timestamp_inv = 0;
291        self.last_timestamp_inv = 0;
292        self.is_untrusted_programs_enabled = enable_untrusted_programs as u32;
293        #[cfg(feature = "mprotect")]
294        {
295            self.enable_trap_handler = trap_context.is_some() as u32;
296            self.trap_context = trap_context.map_or([0, 0, 0], |addr| [addr, addr + 8, addr + 16]);
297            self.untrusted_memory = untrusted_memory.map_or([0, 0], |(start, end)| [start, end]);
298        }
299    }
300
301    /// Update the public values to the state, as a non-execution shard in the final state of the
302    /// program's execution.
303    #[allow(clippy::too_many_arguments)]
304    #[cfg_attr(not(feature = "mprotect"), allow(unused_variables))]
305    pub fn update_finalized_state(
306        &mut self,
307        timestamp: u64,
308        pc: u64,
309        exit_code: u32,
310        is_untrusted_programs_enabled: u32,
311        enable_trap_handler: u32,
312        trap_context: [u64; 3],
313        untrusted_memory: [u64; 2],
314        committed_value_digest: [u32; PV_DIGEST_NUM_WORDS],
315        deferred_proofs_digest: [u32; POSEIDON_NUM_WORDS],
316        nonce: [u32; PROOF_NONCE_NUM_WORDS],
317    ) {
318        self.pc_start = pc;
319        self.next_pc = pc;
320        self.exit_code = exit_code;
321        self.initial_timestamp = timestamp;
322        self.last_timestamp = timestamp;
323        self.is_timestamp_high_eq = 1;
324        self.is_timestamp_low_eq = 1;
325        self.is_first_execution_shard = 0;
326        self.is_execution_shard = 0;
327        self.initial_timestamp_inv = 0;
328        self.last_timestamp_inv = 0;
329        self.prev_committed_value_digest = committed_value_digest;
330        self.committed_value_digest = committed_value_digest;
331        self.prev_deferred_proofs_digest = deferred_proofs_digest;
332        self.deferred_proofs_digest = deferred_proofs_digest;
333        self.is_untrusted_programs_enabled = is_untrusted_programs_enabled;
334        #[cfg(feature = "mprotect")]
335        {
336            self.enable_trap_handler = enable_trap_handler;
337            self.trap_context = trap_context;
338            self.untrusted_memory = untrusted_memory;
339        }
340        self.prev_exit_code = exit_code;
341        self.prev_commit_syscall = 1;
342        self.commit_syscall = 1;
343        self.prev_commit_deferred_syscall = 1;
344        self.commit_deferred_syscall = 1;
345        self.proof_nonce = nonce;
346    }
347
348    /// Similar to [`update_finalized_state`], but takes all the values from an existing public
349    /// values struct for convenience.
350    pub fn update_finalized_state_from_public_values(
351        &mut self,
352        public_values: &PublicValues<u32, u64, u64, u32>,
353    ) {
354        #[cfg(feature = "mprotect")]
355        let (enable_trap_handler, trap_context, untrusted_memory) = (
356            public_values.enable_trap_handler,
357            public_values.trap_context,
358            public_values.untrusted_memory,
359        );
360        #[cfg(not(feature = "mprotect"))]
361        let (enable_trap_handler, trap_context, untrusted_memory) = (0, [0, 0, 0], [0, 0]);
362
363        self.update_finalized_state(
364            public_values.last_timestamp,
365            public_values.next_pc,
366            public_values.exit_code,
367            public_values.is_untrusted_programs_enabled,
368            enable_trap_handler,
369            trap_context,
370            untrusted_memory,
371            public_values.committed_value_digest,
372            public_values.deferred_proofs_digest,
373            public_values.proof_nonce,
374        );
375    }
376}
377
378/// Returns a timestamp from a limbs array.
379///
380/// The representation of the timestamp is given in big endian by bit decomposition of bits
381/// (16, 8, 8, 16)
382#[inline]
383pub fn timestamp_from_limbs<F: PrimeField32>(limbs: &[F; 4]) -> u64 {
384    let mut timestamp = (limbs[0].as_canonical_u32() as u64) << 32;
385    timestamp += (limbs[1].as_canonical_u32() as u64) << 24;
386    timestamp += (limbs[2].as_canonical_u32() as u64) << 16;
387    timestamp += limbs[3].as_canonical_u32() as u64;
388    timestamp
389}
390
391/// A type alias for the public values of the SP1 core proof.
392pub type SP1CorePublicValues<F> = PublicValues<[F; 4], [F; 3], [F; 4], F>;
393
394impl<F: PrimeField32> PublicValues<[F; 4], [F; 3], [F; 4], F> {
395    /// Returns the commit digest as a vector of little-endian bytes.
396    pub fn commit_digest_bytes(&self) -> Vec<u8> {
397        self.committed_value_digest
398            .iter()
399            .flat_map(|w| w.iter().map(|f| f.as_canonical_u32() as u8))
400            .collect_vec()
401    }
402
403    /// Returns the initial timestamp.
404    pub fn initial_timestamp(&self) -> u64 {
405        timestamp_from_limbs(&self.initial_timestamp)
406    }
407
408    /// Returns the last timestamp.
409    pub fn last_timestamp(&self) -> u64 {
410        timestamp_from_limbs(&self.last_timestamp)
411    }
412
413    /// Returns the previous initialization address.
414    pub fn previous_init_addr(&self) -> u64 {
415        self.previous_init_addr
416            .iter()
417            .rev()
418            .fold(0, |acc, x| acc * (1 << 16) + x.as_canonical_u32() as u64)
419    }
420
421    /// Returns the last initialization address.
422    pub fn last_init_addr(&self) -> u64 {
423        self.last_init_addr
424            .iter()
425            .rev()
426            .fold(0, |acc, x| acc * (1 << 16) + x.as_canonical_u32() as u64)
427    }
428
429    /// Returns the previous finalize address.
430    pub fn previous_finalize_addr(&self) -> u64 {
431        self.previous_finalize_addr
432            .iter()
433            .rev()
434            .fold(0, |acc, x| acc * (1 << 16) + x.as_canonical_u32() as u64)
435    }
436
437    /// Returns the last finalize address.
438    pub fn last_finalize_addr(&self) -> u64 {
439        self.last_finalize_addr
440            .iter()
441            .rev()
442            .fold(0, |acc, x| acc * (1 << 16) + x.as_canonical_u32() as u64)
443    }
444
445    /// Returns the previous initialization page index.
446    pub fn previous_init_page_idx(&self) -> u64 {
447        self.previous_init_page_idx
448            .iter()
449            .rev()
450            .fold(0, |acc, x| acc * (1 << 16) + x.as_canonical_u32() as u64)
451    }
452
453    /// Returns the last initialization page index.
454    pub fn last_init_page_idx(&self) -> u64 {
455        self.last_init_page_idx
456            .iter()
457            .rev()
458            .fold(0, |acc, x| acc * (1 << 16) + x.as_canonical_u32() as u64)
459    }
460
461    /// Returns the previous finalize page index.
462    pub fn previous_finalize_page_idx(&self) -> u64 {
463        self.previous_finalize_page_idx
464            .iter()
465            .rev()
466            .fold(0, |acc, x| acc * (1 << 16) + x.as_canonical_u32() as u64)
467    }
468
469    /// Returns the last finalize page index.
470    pub fn last_finalize_page_idx(&self) -> u64 {
471        self.last_finalize_page_idx
472            .iter()
473            .rev()
474            .fold(0, |acc, x| acc * (1 << 16) + x.as_canonical_u32() as u64)
475    }
476
477    /// Returns the range of the shard.
478    #[must_use]
479    pub fn range(&self) -> ShardRange {
480        let timestamp_range = (self.initial_timestamp(), self.last_timestamp());
481        let initialized_address_range = (self.previous_init_addr(), self.last_init_addr());
482        let finalized_address_range = (self.previous_finalize_addr(), self.last_finalize_addr());
483        let initialized_page_index_range =
484            (self.previous_init_page_idx(), self.last_init_page_idx());
485        let finalized_page_index_range =
486            (self.previous_finalize_page_idx(), self.last_finalize_page_idx());
487        let deferred_proof_range = (0, 0);
488
489        ShardRange {
490            timestamp_range,
491            initialized_address_range,
492            finalized_address_range,
493            initialized_page_index_range,
494            finalized_page_index_range,
495            deferred_proof_range,
496        }
497    }
498}
499
500impl<T: Clone> Borrow<PublicValues<[T; 4], [T; 3], [T; 4], T>> for [T] {
501    fn borrow(&self) -> &PublicValues<[T; 4], [T; 3], [T; 4], T> {
502        let size = std::mem::size_of::<PublicValues<[u8; 4], [u8; 3], [u8; 4], u8>>();
503        debug_assert!(self.len() >= size);
504        let slice = &self[0..size];
505        let (prefix, shorts, _suffix) =
506            unsafe { slice.align_to::<PublicValues<[T; 4], [T; 3], [T; 4], T>>() };
507        debug_assert!(prefix.is_empty(), "Alignment should match");
508        debug_assert_eq!(shorts.len(), 1);
509        &shorts[0]
510    }
511}
512
513impl<T: Clone> BorrowMut<PublicValues<[T; 4], [T; 3], [T; 4], T>> for [T] {
514    fn borrow_mut(&mut self) -> &mut PublicValues<[T; 4], [T; 3], [T; 4], T> {
515        let size = std::mem::size_of::<PublicValues<[u8; 4], [u8; 3], [u8; 4], u8>>();
516        debug_assert!(self.len() >= size);
517        let slice = &mut self[0..size];
518        let (prefix, shorts, _suffix) =
519            unsafe { slice.align_to_mut::<PublicValues<[T; 4], [T; 3], [T; 4], T>>() };
520        debug_assert!(prefix.is_empty(), "Alignment should match");
521        debug_assert_eq!(shorts.len(), 1);
522        &mut shorts[0]
523    }
524}
525
526impl<F: AbstractField> From<PublicValues<u32, u64, u64, u32>>
527    for PublicValues<[F; 4], [F; 3], [F; 4], F>
528{
529    #[allow(clippy::too_many_lines)]
530    fn from(value: PublicValues<u32, u64, u64, u32>) -> Self {
531        let PublicValues {
532            prev_committed_value_digest,
533            committed_value_digest,
534            prev_deferred_proofs_digest,
535            deferred_proofs_digest,
536            pc_start,
537            next_pc,
538            prev_exit_code,
539            exit_code,
540            is_execution_shard,
541            previous_init_addr,
542            last_init_addr,
543            previous_finalize_addr,
544            last_finalize_addr,
545            previous_init_page_idx,
546            last_init_page_idx,
547            previous_finalize_page_idx,
548            last_finalize_page_idx,
549            initial_timestamp,
550            last_timestamp,
551            is_timestamp_high_eq,
552            inv_timestamp_high,
553            is_timestamp_low_eq,
554            inv_timestamp_low,
555            global_init_count,
556            global_finalize_count,
557            global_page_prot_init_count,
558            global_page_prot_finalize_count,
559            global_count,
560            global_cumulative_sum,
561            prev_commit_syscall,
562            commit_syscall,
563            prev_commit_deferred_syscall,
564            commit_deferred_syscall,
565            is_untrusted_programs_enabled,
566            #[cfg(feature = "mprotect")]
567            enable_trap_handler,
568            #[cfg(feature = "mprotect")]
569            trap_context,
570            #[cfg(feature = "mprotect")]
571            untrusted_memory,
572            proof_nonce,
573            initial_timestamp_inv,
574            last_timestamp_inv,
575            is_first_execution_shard,
576            ..
577        } = value;
578
579        let prev_committed_value_digest: [_; PV_DIGEST_NUM_WORDS] = core::array::from_fn(|i| {
580            [
581                F::from_canonical_u32(prev_committed_value_digest[i] & 0xFF),
582                F::from_canonical_u32((prev_committed_value_digest[i] >> 8) & 0xFF),
583                F::from_canonical_u32((prev_committed_value_digest[i] >> 16) & 0xFF),
584                F::from_canonical_u32((prev_committed_value_digest[i] >> 24) & 0xFF),
585            ]
586        });
587
588        let committed_value_digest: [_; PV_DIGEST_NUM_WORDS] = core::array::from_fn(|i| {
589            [
590                F::from_canonical_u32(committed_value_digest[i] & 0xFF),
591                F::from_canonical_u32((committed_value_digest[i] >> 8) & 0xFF),
592                F::from_canonical_u32((committed_value_digest[i] >> 16) & 0xFF),
593                F::from_canonical_u32((committed_value_digest[i] >> 24) & 0xFF),
594            ]
595        });
596
597        let prev_deferred_proofs_digest: [_; POSEIDON_NUM_WORDS] =
598            core::array::from_fn(|i| F::from_canonical_u32(prev_deferred_proofs_digest[i]));
599
600        let deferred_proofs_digest: [_; POSEIDON_NUM_WORDS] =
601            core::array::from_fn(|i| F::from_canonical_u32(deferred_proofs_digest[i]));
602
603        let pc_start = [
604            F::from_canonical_u16((pc_start & 0xFFFF) as u16),
605            F::from_canonical_u16(((pc_start >> 16) & 0xFFFF) as u16),
606            F::from_canonical_u16(((pc_start >> 32) & 0xFFFF) as u16),
607        ];
608        let next_pc = [
609            F::from_canonical_u16((next_pc & 0xFFFF) as u16),
610            F::from_canonical_u16(((next_pc >> 16) & 0xFFFF) as u16),
611            F::from_canonical_u16(((next_pc >> 32) & 0xFFFF) as u16),
612        ];
613        let exit_code = F::from_canonical_u32(exit_code);
614        let prev_exit_code = F::from_canonical_u32(prev_exit_code);
615        let is_execution_shard = F::from_canonical_u32(is_execution_shard);
616        let previous_init_addr = [
617            F::from_canonical_u16((previous_init_addr & 0xFFFF) as u16),
618            F::from_canonical_u16(((previous_init_addr >> 16) & 0xFFFF) as u16),
619            F::from_canonical_u16(((previous_init_addr >> 32) & 0xFFFF) as u16),
620        ];
621        let last_init_addr = [
622            F::from_canonical_u16((last_init_addr & 0xFFFF) as u16),
623            F::from_canonical_u16(((last_init_addr >> 16) & 0xFFFF) as u16),
624            F::from_canonical_u16(((last_init_addr >> 32) & 0xFFFF) as u16),
625        ];
626        let previous_finalize_addr = [
627            F::from_canonical_u16((previous_finalize_addr & 0xFFFF) as u16),
628            F::from_canonical_u16(((previous_finalize_addr >> 16) & 0xFFFF) as u16),
629            F::from_canonical_u16(((previous_finalize_addr >> 32) & 0xFFFF) as u16),
630        ];
631        let last_finalize_addr = [
632            F::from_canonical_u16((last_finalize_addr & 0xFFFF) as u16),
633            F::from_canonical_u16(((last_finalize_addr >> 16) & 0xFFFF) as u16),
634            F::from_canonical_u16(((last_finalize_addr >> 32) & 0xFFFF) as u16),
635        ];
636        let previous_init_page_idx: [F; 3] = core::array::from_fn(|i| {
637            F::from_canonical_u16(split_page_idx(previous_init_page_idx)[i])
638        });
639        let last_init_page_idx: [F; 3] =
640            core::array::from_fn(|i| F::from_canonical_u16(split_page_idx(last_init_page_idx)[i]));
641        let previous_finalize_page_idx: [F; 3] = core::array::from_fn(|i| {
642            F::from_canonical_u16(split_page_idx(previous_finalize_page_idx)[i])
643        });
644        let last_finalize_page_idx: [F; 3] = core::array::from_fn(|i| {
645            F::from_canonical_u16(split_page_idx(last_finalize_page_idx)[i])
646        });
647        let initial_timestamp = [
648            F::from_canonical_u16((initial_timestamp >> 32) as u16),
649            F::from_canonical_u8(((initial_timestamp >> 24) & 0xFF) as u8),
650            F::from_canonical_u8(((initial_timestamp >> 16) & 0xFF) as u8),
651            F::from_canonical_u16((initial_timestamp & 0xFFFF) as u16),
652        ];
653        let last_timestamp = [
654            F::from_canonical_u16((last_timestamp >> 32) as u16),
655            F::from_canonical_u8(((last_timestamp >> 24) & 0xFF) as u8),
656            F::from_canonical_u8(((last_timestamp >> 16) & 0xFF) as u8),
657            F::from_canonical_u16((last_timestamp & 0xFFFF) as u16),
658        ];
659
660        let is_timestamp_high_eq = F::from_canonical_u32(is_timestamp_high_eq);
661        let inv_timestamp_high = F::from_canonical_u32(inv_timestamp_high);
662        let is_timestamp_low_eq = F::from_canonical_u32(is_timestamp_low_eq);
663        let inv_timestamp_low = F::from_canonical_u32(inv_timestamp_low);
664
665        let global_init_count = F::from_canonical_u32(global_init_count);
666        let global_finalize_count = F::from_canonical_u32(global_finalize_count);
667        let global_page_prot_init_count = F::from_canonical_u32(global_page_prot_init_count);
668        let global_page_prot_finalize_count =
669            F::from_canonical_u32(global_page_prot_finalize_count);
670        let global_count = F::from_canonical_u32(global_count);
671        let global_cumulative_sum =
672            SepticDigest(SepticCurve::convert(global_cumulative_sum.0, F::from_canonical_u32));
673
674        let prev_commit_syscall = F::from_canonical_u32(prev_commit_syscall);
675        let commit_syscall = F::from_canonical_u32(commit_syscall);
676        let prev_commit_deferred_syscall = F::from_canonical_u32(prev_commit_deferred_syscall);
677        let commit_deferred_syscall = F::from_canonical_u32(commit_deferred_syscall);
678
679        let initial_timestamp_inv = F::from_canonical_u32(initial_timestamp_inv);
680        let last_timestamp_inv = F::from_canonical_u32(last_timestamp_inv);
681        let is_first_execution_shard = F::from_canonical_u32(is_first_execution_shard);
682        let is_untrusted_programs_enabled = F::from_canonical_u32(is_untrusted_programs_enabled);
683
684        #[cfg(feature = "mprotect")]
685        let enable_trap_handler = F::from_canonical_u32(enable_trap_handler);
686        #[cfg(feature = "mprotect")]
687        let trap_context = [
688            addr_to_limbs::<F>(trap_context[0]),
689            addr_to_limbs::<F>(trap_context[1]),
690            addr_to_limbs::<F>(trap_context[2]),
691        ];
692        #[cfg(feature = "mprotect")]
693        let untrusted_memory =
694            [addr_to_limbs::<F>(untrusted_memory[0]), addr_to_limbs::<F>(untrusted_memory[1])];
695
696        let proof_nonce: [_; PROOF_NONCE_NUM_WORDS] =
697            core::array::from_fn(|i| F::from_canonical_u32(proof_nonce[i]));
698
699        Self {
700            prev_committed_value_digest,
701            committed_value_digest,
702            prev_deferred_proofs_digest,
703            deferred_proofs_digest,
704            pc_start,
705            next_pc,
706            prev_exit_code,
707            exit_code,
708            is_execution_shard,
709            previous_init_addr,
710            last_init_addr,
711            previous_finalize_addr,
712            last_finalize_addr,
713            previous_init_page_idx,
714            last_init_page_idx,
715            previous_finalize_page_idx,
716            last_finalize_page_idx,
717            initial_timestamp,
718            last_timestamp,
719            is_timestamp_high_eq,
720            inv_timestamp_high,
721            is_timestamp_low_eq,
722            inv_timestamp_low,
723            global_init_count,
724            global_finalize_count,
725            global_page_prot_init_count,
726            global_page_prot_finalize_count,
727            global_count,
728            global_cumulative_sum,
729            prev_commit_syscall,
730            commit_syscall,
731            prev_commit_deferred_syscall,
732            commit_deferred_syscall,
733            is_untrusted_programs_enabled,
734            #[cfg(feature = "mprotect")]
735            enable_trap_handler,
736            #[cfg(feature = "mprotect")]
737            trap_context,
738            #[cfg(feature = "mprotect")]
739            untrusted_memory,
740            initial_timestamp_inv,
741            last_timestamp_inv,
742            is_first_execution_shard,
743            proof_nonce,
744            empty: core::array::from_fn(|_| F::zero()),
745        }
746    }
747}
748
749/// A shard boundary is a single shard that is being proven.
750#[derive(
751    Serialize, Deserialize, Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash,
752)]
753#[repr(C)]
754pub struct ShardBoundary {
755    /// The timestamp.
756    pub timestamp: u64,
757    /// The initialized address.
758    pub initialized_address: u64,
759    /// The finalized address.
760    pub finalized_address: u64,
761    /// The initialized page index.
762    pub initialized_page_index: u64,
763    /// The finalized page index.
764    pub finalized_page_index: u64,
765    /// The deferred proof index
766    pub deferred_proof: u64,
767}
768
769impl ShardBoundary {
770    /// Returns the initial shard boundary.
771    ///
772    /// The initial shard boundary has timestamp set to 1, the other values are set to 0.
773    #[inline]
774    #[must_use]
775    pub fn initial() -> Self {
776        Self {
777            timestamp: 1,
778            initialized_address: 0,
779            finalized_address: 0,
780            initialized_page_index: 0,
781            finalized_page_index: 0,
782            deferred_proof: 0,
783        }
784    }
785}
786
787/// The range of the shard with respect to the program execution ordering.
788#[derive(
789    Serialize, Deserialize, Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash,
790)]
791#[repr(C)]
792pub struct ShardRange {
793    /// The timestamp range of the shard
794    pub timestamp_range: (u64, u64),
795    /// The initialized address range of the shard,
796    pub initialized_address_range: (u64, u64),
797    /// The finalized address range of the shard
798    pub finalized_address_range: (u64, u64),
799    /// The initialized page index range of the shard
800    pub initialized_page_index_range: (u64, u64),
801    /// The finalized page index range of the shard
802    pub finalized_page_index_range: (u64, u64),
803    /// The deferred proof index range of the shard
804    pub deferred_proof_range: (u64, u64),
805}
806
807impl From<Range<ShardBoundary>> for ShardRange {
808    fn from(value: Range<ShardBoundary>) -> Self {
809        Self {
810            timestamp_range: (value.start.timestamp, value.end.timestamp),
811            initialized_address_range: (
812                value.start.initialized_address,
813                value.end.initialized_address,
814            ),
815            finalized_address_range: (value.start.finalized_address, value.end.finalized_address),
816            initialized_page_index_range: (
817                value.start.initialized_page_index,
818                value.end.initialized_page_index,
819            ),
820            finalized_page_index_range: (
821                value.start.finalized_page_index,
822                value.end.finalized_page_index,
823            ),
824            deferred_proof_range: (value.start.deferred_proof, value.end.deferred_proof),
825        }
826    }
827}
828
829impl ShardRange {
830    /// Returns the start boundary of the shard.
831    #[must_use]
832    #[inline]
833    pub fn start(&self) -> ShardBoundary {
834        ShardBoundary {
835            timestamp: self.timestamp_range.0,
836            initialized_address: self.initialized_address_range.0,
837            finalized_address: self.finalized_address_range.0,
838            initialized_page_index: self.initialized_page_index_range.0,
839            finalized_page_index: self.finalized_page_index_range.0,
840            deferred_proof: self.deferred_proof_range.0,
841        }
842    }
843
844    /// Returns the end boundary of the shard.
845    #[must_use]
846    #[inline]
847    pub fn end(&self) -> ShardBoundary {
848        ShardBoundary {
849            timestamp: self.timestamp_range.1,
850            initialized_address: self.initialized_address_range.1,
851            finalized_address: self.finalized_address_range.1,
852            initialized_page_index: self.initialized_page_index_range.1,
853            finalized_page_index: self.finalized_page_index_range.1,
854            deferred_proof: self.deferred_proof_range.1,
855        }
856    }
857
858    /// Returns the shard range for precompile shards.
859    ///
860    /// Precompile shards are ordered before all other shards in the compress tree. They have the
861    /// `timestamp_range: (1, 1)` and all other ranges set to (0, 0).
862    #[must_use]
863    #[inline]
864    pub fn precompile() -> Self {
865        Self {
866            timestamp_range: (1, 1),
867            initialized_address_range: (0, 0),
868            finalized_address_range: (0, 0),
869            initialized_page_index_range: (0, 0),
870            finalized_page_index_range: (0, 0),
871            deferred_proof_range: (0, 0),
872        }
873    }
874
875    /// Returns the shard range for deferred proof shards.
876    ///
877    /// These have `timestamp_range: (1, 1)` and `deferred_proof_range` set according to the input.
878    #[must_use]
879    #[inline]
880    pub fn deferred(prev_deferred_proof: u64, deferred_proof: u64) -> Self {
881        ShardRange {
882            timestamp_range: (1, 1),
883            initialized_address_range: (0, 0),
884            finalized_address_range: (0, 0),
885            initialized_page_index_range: (0, 0),
886            finalized_page_index_range: (0, 0),
887            deferred_proof_range: (prev_deferred_proof, deferred_proof),
888        }
889    }
890}
891
892impl core::fmt::Display for ShardRange {
893    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
894        write!(f, "ShardRange:")?;
895        write!(f, "timestamp_range: {}..{}", self.timestamp_range.0, self.timestamp_range.1)?;
896        write!(
897            f,
898            "initialized_address_range: {}..{}",
899            self.initialized_address_range.0, self.initialized_address_range.1
900        )?;
901        write!(
902            f,
903            "finalized_address_range: {}..{}",
904            self.finalized_address_range.0, self.finalized_address_range.1
905        )?;
906        write!(
907            f,
908            "initialized_page_index_range: {}..{}",
909            self.initialized_page_index_range.0, self.initialized_page_index_range.1
910        )?;
911        write!(
912            f,
913            "finalized_page_index_range: {}..{}",
914            self.finalized_page_index_range.0, self.finalized_page_index_range.1
915        )?;
916        Ok(())
917    }
918}
919
920#[cfg(test)]
921mod tests {
922    use crate::air::public_values;
923
924    /// Check that the [`PI_DIGEST_NUM_WORDS`] number match the zkVM crate's.
925    #[test]
926    fn test_public_values_digest_num_words_consistency_zkvm() {
927        assert_eq!(public_values::PV_DIGEST_NUM_WORDS, sp1_zkvm::PV_DIGEST_NUM_WORDS);
928    }
929}