risc0_bigint2/
lib.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
15#[cfg(test)]
16#[cfg(feature = "num-bigint-dig")]
17extern crate num_bigint_dig as num_bigint;
18
19#[cfg(feature = "unstable")]
20pub mod ec;
21#[cfg(feature = "unstable")]
22pub mod ffi;
23#[cfg(feature = "unstable")]
24pub mod field;
25#[cfg(feature = "unstable")]
26pub mod rsa;
27
28#[allow(dead_code)] // Used by the unstable functions
29pub(crate) const WORD_SIZE: usize = 4;
30
31/// Trait for converting values to a u32 array to be used for bigint2 acceleration.
32#[stability::unstable]
33pub trait ToBigInt2Buffer<const WIDTH: usize> {
34    /// Convert value to a u32 array to be used for bigint2 acceleration.
35    fn to_u32_array(&self) -> [u32; WIDTH];
36
37    /// Convert from array into the original type.
38    fn from_u32_array(array: [u32; WIDTH]) -> Self;
39}
40
41#[cfg(feature = "unstable")]
42#[inline]
43// Checks if two u32 arrays representing big integers with little-endian digit order satisfy lhs < rhs
44fn is_less<const N: usize>(lhs: &[u32; N], rhs: &[u32; N]) -> bool {
45    for i in (0..N).rev() {
46        if lhs[i] < rhs[i] {
47            return true;
48        }
49        if lhs[i] > rhs[i] {
50            return false;
51        }
52    }
53    false
54}
55
56#[cfg(all(target_os = "zkvm", target_arch = "riscv32"))]
57const _: () = {
58    assert!(
59        core::option_env!("RISC0_FEATURE_bigint2").is_some(),
60        r#"
61RISC Zero zkVM feature bigint2 is not available, and is required by this crate.
62
63If you'd like to use bigint2, please upgrade to risc0-zkvm and risc0-build and ensure the required
64feature flags are enabled. See the RISC Zero dev docs for more information.
65https://dev.risczero.com/api/zkvm/acceleration
66"#
67    );
68};
69
70#[cfg(feature = "num-bigint")]
71impl<const WIDTH: usize> ToBigInt2Buffer<WIDTH> for num_bigint::BigUint {
72    fn to_u32_array(&self) -> [u32; WIDTH] {
73        let digits = self.to_u32_digits();
74        assert!(
75            digits.len() <= WIDTH,
76            "Input too large: {} words exceeds width of {} words",
77            digits.len(),
78            WIDTH,
79        );
80
81        let mut result = [0u32; WIDTH];
82        result[..digits.len()].copy_from_slice(&digits);
83        result
84    }
85
86    fn from_u32_array(array: [u32; WIDTH]) -> Self {
87        Self::from_slice(&array)
88    }
89}
90
91#[cfg(feature = "num-bigint-dig")]
92impl<const WIDTH: usize> ToBigInt2Buffer<WIDTH> for num_bigint_dig::BigUint {
93    fn to_u32_array(&self) -> [u32; WIDTH] {
94        let mut result = [0u32; WIDTH];
95        let bytes = self.to_bytes_le();
96        let max_width = WIDTH * 4;
97        assert!(
98            bytes.len() <= max_width,
99            "Input too large: {} bytes exceeds width of {} bytes",
100            bytes.len(),
101            max_width,
102        );
103
104        let mut chunks = bytes.chunks_exact(WORD_SIZE);
105        for (i, chunk) in chunks.by_ref().enumerate() {
106            result[i] = u32::from_le_bytes(chunk.try_into().unwrap());
107        }
108
109        let remainder = chunks.remainder();
110        if !remainder.is_empty() {
111            let idx = bytes.len() / WORD_SIZE;
112            let mut word = 0u32;
113            for (i, &byte) in remainder.iter().enumerate() {
114                word |= (byte as u32) << (i * 8);
115            }
116            result[idx] = word;
117        }
118
119        result
120    }
121
122    fn from_u32_array(array: [u32; WIDTH]) -> Self {
123        Self::from_slice(&array)
124    }
125}
126
127#[cfg(test)]
128#[cfg(feature = "unstable")]
129struct BigUintWrap(num_bigint::BigUint);
130
131#[cfg(test)]
132#[cfg(feature = "unstable")]
133impl std::str::FromStr for BigUintWrap {
134    type Err = num_bigint::ParseBigIntError;
135
136    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
137        use num_traits::Num as _;
138        Ok(BigUintWrap(num_bigint::BigUint::from_str_radix(s, 16)?))
139    }
140}