Skip to main content

sp1_recursion_circuit/
utils.rs

1use slop_algebra::{AbstractField, PrimeField32};
2use slop_bn254::Bn254Fr;
3use sp1_primitives::SP1Field;
4
5use sp1_recursion_compiler::ir::{Builder, Config, Felt, Var};
6use sp1_recursion_executor::DIGEST_SIZE;
7
8pub fn koalabears_proof_nonce_to_bn254(nonce: &[SP1Field; 4]) -> Bn254Fr {
9    let mut result = Bn254Fr::zero();
10    for word in nonce.iter() {
11        // Since SP1Field prime is less than 2^31, we can shift by 31 bits each time and still be
12        // within the Bn254Fr field, so we don't have to truncate the top 3 bits.
13        result *= Bn254Fr::from_canonical_u64(1 << 31);
14        result += Bn254Fr::from_canonical_u32(word.as_canonical_u32());
15    }
16    result
17}
18
19/// Convert 32 SP1Field bytes into a Bn254Fr field element. The first byte's most significant 3 bits
20/// (which would become the 3 most significant bits) are truncated.
21pub fn koalabear_bytes_to_bn254(bytes: &[SP1Field; 32]) -> Bn254Fr {
22    let mut result = Bn254Fr::zero();
23    for (i, byte) in bytes.iter().enumerate() {
24        debug_assert!(byte < &SP1Field::from_canonical_u32(256));
25        if i == 0 {
26            // 32 bytes is more than Bn254 prime, so we need to truncate the top 3 bits.
27            result = Bn254Fr::from_canonical_u32(byte.as_canonical_u32() & 0x1f);
28        } else {
29            result *= Bn254Fr::from_canonical_u32(256);
30            result += Bn254Fr::from_canonical_u32(byte.as_canonical_u32());
31        }
32    }
33    result
34}
35
36pub fn felts_to_bn254_var<C: Config>(
37    builder: &mut Builder<C>,
38    digest: &[Felt<SP1Field>; DIGEST_SIZE],
39) -> Var<C::N> {
40    let var_2_31: Var<_> = builder.constant(C::N::from_canonical_u32(1 << 31));
41    let result = builder.constant(C::N::zero());
42    for (i, word) in digest.iter().enumerate() {
43        let word_var = builder.felt2var_circuit(*word);
44        if i == 0 {
45            builder.assign(result, word_var);
46        } else {
47            builder.assign(result, result * var_2_31 + word_var);
48        }
49    }
50    result
51}
52
53pub fn felt_proof_nonce_to_bn254_var<C: Config>(
54    builder: &mut Builder<C>,
55    nonce: &[Felt<SP1Field>; 4],
56) -> Var<C::N> {
57    let var_2_31: Var<_> = builder.constant(C::N::from_canonical_u32(1 << 31));
58    let result = builder.constant(C::N::zero());
59    for (i, word) in nonce.iter().enumerate() {
60        let word_var = builder.felt2var_circuit(*word);
61        if i == 0 {
62            builder.assign(result, word_var);
63        } else {
64            builder.assign(result, result * var_2_31 + word_var);
65        }
66    }
67    result
68}
69
70pub fn felt_bytes_to_bn254_var<C: Config>(
71    builder: &mut Builder<C>,
72    bytes: &[Felt<SP1Field>; 32],
73) -> Var<C::N> {
74    let var_256: Var<_> = builder.constant(C::N::from_canonical_u32(256));
75    let zero_var: Var<_> = builder.constant(C::N::zero());
76    let result = builder.constant(C::N::zero());
77    for (i, byte) in bytes.iter().enumerate() {
78        let byte_bits = builder.num2bits_f_circuit(*byte);
79        if i == 0 {
80            // Since 32 bytes doesn't fit into Bn254, we need to truncate the top 3 bits.
81            // For first byte, zero out 3 most significant bits.
82            for i in 0..3 {
83                builder.assign(byte_bits[8 - i - 1], zero_var);
84            }
85            let byte_var = builder.bits2num_v_circuit(&byte_bits);
86            builder.assign(result, byte_var);
87        } else {
88            let byte_var = builder.bits2num_v_circuit(&byte_bits);
89            builder.assign(result, result * var_256 + byte_var);
90        }
91    }
92    result
93}
94
95pub fn words_to_bytes<T: Copy>(words: &[[T; 4]]) -> Vec<T> {
96    words.iter().flat_map(|w| w.iter()).copied().collect::<Vec<_>>()
97}