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