Skip to main content

sp1_hypercube/verifier/
hashable_key.rs

1use std::borrow::Borrow;
2
3use serde::{Deserialize, Serialize};
4pub use slop_algebra::PrimeField32;
5use slop_algebra::{AbstractField, PrimeField};
6use slop_bn254::Bn254Fr;
7use slop_challenger::IopCtx;
8use sp1_primitives::{poseidon2_hash, SP1Field, SP1GlobalContext};
9
10use crate::{MachineVerifyingKey, DIGEST_SIZE};
11
12/// The information necessary to verify a proof for a given RISC-V program.
13#[derive(Clone, Serialize, Deserialize)]
14pub struct SP1VerifyingKey {
15    /// The underlying verifying key, where the underlying field is `SP1Field` and the digest type
16    /// is an array of `DIGEST_SIZE` `SP1Field` elements.
17    pub vk: MachineVerifyingKey<SP1GlobalContext>,
18}
19
20/// Convert 8 `SP1Field` words into a `Bn254Fr` field element by shifting by 31 bits each time. The last
21/// word becomes the least significant bits.
22#[must_use]
23pub fn koalabears_to_bn254(digest: &[SP1Field; 8]) -> Bn254Fr {
24    let mut result = Bn254Fr::zero();
25    for word in digest.iter() {
26        // Since SP1Field prime is less than 2^31, we can shift by 31 bits each time and still be
27        // within the Bn254Fr field, so we don't have to truncate the top 3 bits.
28        result *= Bn254Fr::from_canonical_u64(1 << 31);
29        result += Bn254Fr::from_canonical_u32(word.as_canonical_u32());
30    }
31    result
32}
33
34/// Utility method for converting u32 words to bytes in big endian.
35#[must_use]
36pub fn words_to_bytes_be(words: &[u32; 8]) -> [u8; 32] {
37    let mut bytes = [0u8; 32];
38    for i in 0..8 {
39        let word_bytes = words[i].to_be_bytes();
40        bytes[i * 4..(i + 1) * 4].copy_from_slice(&word_bytes);
41    }
42    bytes
43}
44
45/// A trait for keys that can be hashed into a digest.
46pub trait HashableKey {
47    /// Hash the key into a digest of `SP1Field` elements.
48    fn hash_koalabear(&self) -> [SP1Field; DIGEST_SIZE];
49
50    /// Hash the key into a digest of u32 elements.
51    fn hash_u32(&self) -> [u32; DIGEST_SIZE];
52
53    /// Hash the key into a `Bn254Fr` element.
54    fn hash_bn254(&self) -> Bn254Fr {
55        koalabears_to_bn254(&self.hash_koalabear())
56    }
57
58    /// Hash the key into a 32 byte hex string, prefixed with "0x".
59    ///
60    /// This is ideal for generating a vkey hash for onchain verification.
61    fn bytes32(&self) -> String {
62        let vkey_digest_bn254 = self.hash_bn254();
63        format!("0x{:0>64}", vkey_digest_bn254.as_canonical_biguint().to_str_radix(16))
64    }
65
66    /// Hash the key into a 32 byte array.
67    ///
68    /// This has the same value as `bytes32`, but as a raw byte array.
69    fn bytes32_raw(&self) -> [u8; 32] {
70        let vkey_digest_bn254 = self.hash_bn254();
71        let vkey_bytes = vkey_digest_bn254.as_canonical_biguint().to_bytes_be();
72        let mut result = [0u8; 32];
73        result[1..].copy_from_slice(&vkey_bytes);
74        result
75    }
76
77    /// Hash the key into a digest of bytes elements.
78    fn hash_bytes(&self) -> [u8; DIGEST_SIZE * 4] {
79        words_to_bytes_be(&self.hash_u32())
80    }
81
82    /// Hash the key into a digest of u64 elements.
83    fn hash_u64(&self) -> [u64; DIGEST_SIZE / 2] {
84        self.hash_u32()
85            .chunks_exact(2)
86            .map(|chunk| chunk[0] as u64 | ((chunk[1] as u64) << 32))
87            .collect::<Vec<_>>()
88            .try_into()
89            .unwrap()
90    }
91}
92
93impl HashableKey for SP1VerifyingKey {
94    fn hash_koalabear(&self) -> [SP1Field; DIGEST_SIZE] {
95        self.vk.hash_koalabear()
96    }
97
98    fn hash_u32(&self) -> [u32; DIGEST_SIZE] {
99        self.vk.hash_u32()
100    }
101}
102
103impl<GC: IopCtx<F = SP1Field>> HashableKey for MachineVerifyingKey<GC>
104where
105    GC::Digest: Borrow<[SP1Field; DIGEST_SIZE]>,
106{
107    fn hash_koalabear(&self) -> [SP1Field; DIGEST_SIZE] {
108        let num_inputs = DIGEST_SIZE + 3 + 14 + 1;
109        let mut inputs = Vec::with_capacity(num_inputs);
110        inputs.extend(self.preprocessed_commit.borrow());
111        inputs.extend(self.pc_start);
112        inputs.extend(self.initial_global_cumulative_sum.0.x.0);
113        inputs.extend(self.initial_global_cumulative_sum.0.y.0);
114        inputs.push(self.enable_untrusted_programs);
115
116        poseidon2_hash(inputs)
117    }
118
119    fn hash_u32(&self) -> [u32; 8] {
120        self.hash_koalabear()
121            .into_iter()
122            .map(|n| n.as_canonical_u32())
123            .collect::<Vec<_>>()
124            .try_into()
125            .unwrap()
126    }
127}