├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── artifacts ├── buffer.bin └── rsp ├── benchmark ├── Cargo.toml └── src │ └── main.rs └── crates ├── curves ├── CHANGELOG.md ├── Cargo.toml └── src │ ├── edwards │ ├── ed25519.rs │ └── mod.rs │ ├── lib.rs │ ├── params.rs │ ├── polynomial.rs │ ├── polynomial2.rs │ ├── scalar_mul.rs │ ├── uint256.rs │ ├── utils.rs │ └── weierstrass │ ├── bls12_381.rs │ ├── bn254.rs │ ├── mod.rs │ └── secp256k1.rs ├── executor ├── CHANGELOG.md ├── Cargo.toml └── src │ ├── context.rs │ ├── disassembler │ ├── elf.rs │ ├── mod.rs │ └── rrs.rs │ ├── events │ ├── memory.rs │ ├── mod.rs │ ├── precompiles │ │ ├── ec.rs │ │ ├── edwards.rs │ │ ├── fptower.rs │ │ ├── keccak256_permute.rs │ │ ├── mod.rs │ │ ├── sha256_compress.rs │ │ ├── sha256_extend.rs │ │ └── uint256.rs │ ├── syscall.rs │ └── utils.rs │ ├── executor.rs │ ├── hook.rs │ ├── instruction.rs │ ├── io.rs │ ├── lib.rs │ ├── opcode.rs │ ├── program.rs │ ├── register.rs │ ├── state.rs │ ├── syscalls │ ├── code.rs │ ├── commit.rs │ ├── context.rs │ ├── deferred.rs │ ├── halt.rs │ ├── hint.rs │ ├── mod.rs │ ├── precompiles │ │ ├── edwards │ │ │ ├── add.rs │ │ │ ├── decompress.rs │ │ │ └── mod.rs │ │ ├── fptower │ │ │ ├── fp.rs │ │ │ ├── fp2_addsub.rs │ │ │ ├── fp2_mul.rs │ │ │ └── mod.rs │ │ ├── keccak256 │ │ │ ├── mod.rs │ │ │ └── permute.rs │ │ ├── mod.rs │ │ ├── sha256 │ │ │ ├── compress.rs │ │ │ ├── extend.rs │ │ │ └── mod.rs │ │ ├── uint256.rs │ │ └── weierstrass │ │ │ ├── add.rs │ │ │ ├── decompress.rs │ │ │ ├── double.rs │ │ │ └── mod.rs │ ├── unconstrained.rs │ ├── verify.rs │ └── write.rs │ └── utils.rs └── primitives ├── CHANGELOG.md ├── Cargo.toml └── src ├── consts.rs ├── io.rs ├── lib.rs └── types.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace.package] 2 | version = "3.0.0" 3 | edition = "2021" 4 | license = "MIT OR Apache-2.0" 5 | repository = "https://github.com/succinctlabs/sp1" 6 | keywords = ["sp1", "succinct", "zero-knowledge", "zkvm"] 7 | categories = ["cryptography"] 8 | 9 | [workspace] 10 | members = [ 11 | "crates/curves", 12 | "crates/executor", 13 | "crates/primitives", 14 | "benchmark", 15 | ] 16 | exclude = ["examples/target"] 17 | resolver = "2" 18 | 19 | [profile.release] 20 | opt-level = 3 21 | 22 | [profile.bench] 23 | opt-level = 3 24 | 25 | [profile.fast] 26 | inherits = "release" 27 | debug = true 28 | debug-assertions = true 29 | 30 | [workspace.dependencies] 31 | # sp1 32 | sp1-core-executor = { path = "crates/executor", version = "3.0.0" } 33 | sp1-curves = { path = "crates/curves", version = "3.0.0" } 34 | sp1-primitives = { path = "crates/primitives", version = "3.0.0" } 35 | 36 | # p3 37 | p3-air = "0.1.4-succinct" 38 | p3-field = "0.1.4-succinct" 39 | p3-commit = "0.1.4-succinct" 40 | p3-matrix = "0.1.4-succinct" 41 | p3-baby-bear = { version = "0.1.4-succinct", features = ["nightly-features"] } 42 | p3-util = "0.1.4-succinct" 43 | p3-challenger = "0.1.4-succinct" 44 | p3-dft = "0.1.4-succinct" 45 | p3-fri = "0.1.4-succinct" 46 | p3-goldilocks = "0.1.4-succinct" 47 | p3-keccak = "0.1.4-succinct" 48 | p3-keccak-air = "0.1.4-succinct" 49 | p3-blake3 = "0.1.4-succinct" 50 | p3-mds = "0.1.4-succinct" 51 | p3-merkle-tree = "0.1.4-succinct" 52 | p3-poseidon2 = "0.1.4-succinct" 53 | p3-symmetric = "0.1.4-succinct" 54 | p3-uni-stark = "0.1.4-succinct" 55 | p3-maybe-rayon = "0.1.4-succinct" 56 | p3-bn254-fr = "0.1.4-succinct" 57 | 58 | [workspace.metadata.typos] 59 | # TODO: Fix in next version since CommitCommitedValuesDigest is retained since it's present in constraints.json 60 | default.extend-ignore-re = ["Jo-Philipp Wich", "SubEIN", "DivEIN", "CommitCommitedValuesDigest"] 61 | default.extend-ignore-words-re = ["(?i)groth", "TRE"] 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Succinct RISC-V Emulator Challenge 2 | 3 | Succinct is building a SP1, a zero-knowledge virtual machine that can prove the execution of RISC-V bytecode. 4 | 5 | **RISC-V emulator performance is critical for proving latency.** Since SP1 distributes proving workloads across a GPU cluster, the primary bottleneck is how quickly we can generate work for these GPUs. This process begins with executing RISC-V bytecode, which is inherently serial and limits overall throughput. Each unit of work, called a "shard," represents 2 million RISC-V cycles of execution. For example, a 100 million cycle execution would be split into 50 shards. Our goal is to optimize the RISC-V emulator’s performance to efficiently feed the GPUs and maximize parallelism. 6 | 7 | ``` 8 | Time ─────────────────────────────────────────────────▶ 9 | 10 | Execution (Serial, Emulator) 11 | +---------+---------+---------+---------+---------+ 12 | | Shard 1 | Shard 2 | Shard 3 | Shard 4 | Shard 5 | 13 | +---------+---------+---------+---------+---------+ 14 | │ │ │ │ │ 15 | ▼ ▼ ▼ ▼ ▼ 16 | Proving (Parallel, GPUs) 17 | ───────▶[GPU 1: Shard 1 Proof]────────▶ 18 | ───────▶[GPU 2: Shard 2 Proof]────────▶ 19 | ───────▶[GPU 3: Shard 3 Proof]────────▶ 20 | ───────▶[GPU 4: Shard 4 Proof]────────▶ 21 | ───────▶[GPU 5: Shard 5 Proof]────────▶ 22 | ``` 23 | 24 | Succinct is seeking for new approaches to this problem and outstanding engineers to work on it. If you have a solution, please email riscv-emulator-challenge@succinct.xyz with a link to your GitHub repository or zip file. 25 | 26 | ## Task 27 | 28 | We’ve created a basic starter RISC-V emulator in Rust [here](https://github.com/succinctlabs/riscv-emulator-challenge) alongside a basic benchmarking script. Your task is to optimize the performance of this implementation on the `rsp` program and maximize the `Average MHz` statistic. 29 | 30 | To benchmark the performance, run the following command: 31 | 32 | ``` 33 | cd benchmark 34 | cargo run —-release 35 | ``` 36 | 37 | Start by exploring the `crates/executor` crate to understand its current implementation and identify performance bottlenecks. Focus on improving the existing implementation while ensuring that any modifications are benchmarked for performance improvements and correctness. 38 | 39 | Note that performance varies based on the hardware being utilized. Submissions will be judged using a m7i.8xlarge instance on AWS. With the existing implementation, the average MHz is around 9.35. 40 | 41 | ## Leaderboard 42 | 43 | Submissions will be continuously evaluated and a leaderboard will be maintained. -------------------------------------------------------------------------------- /artifacts/buffer.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/succinctlabs/riscv-emulator-challenge/c98508153875743522e2f0f2cac93f988b6a7252/artifacts/buffer.bin -------------------------------------------------------------------------------- /artifacts/rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/succinctlabs/riscv-emulator-challenge/c98508153875743522e2f0f2cac93f988b6a7252/artifacts/rsp -------------------------------------------------------------------------------- /benchmark/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "riscv-emulator-challenge" 3 | description = "SP1 is a performant, 100% open-source, contributor-friendly zkVM." 4 | readme = "../../README.md" 5 | version = { workspace = true } 6 | edition = { workspace = true } 7 | license = { workspace = true } 8 | repository = { workspace = true } 9 | keywords = { workspace = true } 10 | categories = { workspace = true } 11 | 12 | [dependencies] 13 | alloy-primitives = "0.8.5" 14 | sp1-core-executor = { workspace = true } -------------------------------------------------------------------------------- /benchmark/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | 3 | use alloy_primitives::{hex::FromHex, B256}; 4 | use sp1_core_executor::{Executor, Program}; 5 | 6 | fn main() { 7 | // Load the program. 8 | let program = include_bytes!("../../artifacts/rsp"); 9 | 10 | // Load the input. 11 | let buffer = include_bytes!("../../artifacts/buffer.bin"); 12 | 13 | // Number of runs for benchmarking. 14 | const NUM_RUNS: usize = 5; 15 | let mut total_elapsed = 0.0; 16 | let mut total_mhz = 0.0; 17 | 18 | // Run the benchmark multiple times. 19 | for i in 0..NUM_RUNS { 20 | println!("Run {}/{}", i + 1, NUM_RUNS); 21 | 22 | // Setup the executor. 23 | let mut executor = Executor::new(Program::from(program).unwrap()); 24 | executor.write_stdin_slice(buffer); 25 | 26 | // Run the executor. 27 | let start = Instant::now(); 28 | executor.run().unwrap(); 29 | let elapsed = start.elapsed().as_secs_f64(); 30 | 31 | // Read the outputs. 32 | let mut first = [0u8; 8]; 33 | executor.read_public_values_slice(&mut first); 34 | 35 | let mut bytes = [0u8; 32]; 36 | executor.read_public_values_slice(&mut bytes); 37 | 38 | let block_hash = B256::from_slice(&bytes); 39 | assert_eq!( 40 | block_hash, 41 | B256::from_hex("dab3111c08b6a9330098afd5bb0f690b20871522a1f21c924a2aabc6dbd6a5b9").unwrap() 42 | ); 43 | 44 | let mhz = (executor.state.global_clk as f64 / elapsed) / 1_000_000.0; 45 | 46 | println!(" block_hash={block_hash}"); 47 | println!(" cycles: {}", executor.state.global_clk); 48 | println!(" elapsed: {:.4} seconds", elapsed); 49 | println!(" mhz: {:.2}", mhz); 50 | 51 | // Accumulate totals. 52 | total_elapsed += elapsed; 53 | total_mhz += mhz; 54 | } 55 | 56 | // Calculate and print averages 57 | let avg_elapsed = total_elapsed / NUM_RUNS as f64; 58 | let avg_mhz = total_mhz / NUM_RUNS as f64; 59 | 60 | println!("\n===== BENCHMARK RESULTS ====="); 61 | println!("Runs: {}", NUM_RUNS); 62 | println!("Average elapsed: {:.4} seconds", avg_elapsed); 63 | println!("Average MHz: {:.2}", avg_mhz); 64 | } 65 | -------------------------------------------------------------------------------- /crates/curves/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [1.2.0-rc1](https://github.com/succinctlabs/sp1/releases/tag/sp1-curves-v1.2.0-rc1) - 2024-08-23 11 | 12 | ### Other 13 | 14 | - runtime optimizations ([#1344](https://github.com/succinctlabs/sp1/pull/1344)) 15 | - resolve merge conflicts between dev and experimental 16 | - refactor + cleanup core crates 17 | -------------------------------------------------------------------------------- /crates/curves/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sp1-curves" 3 | description = "SP1 is a performant, 100% open-source, contributor-friendly zkVM." 4 | readme = "../../README.md" 5 | version = { workspace = true } 6 | edition = { workspace = true } 7 | license = { workspace = true } 8 | repository = { workspace = true } 9 | keywords = { workspace = true } 10 | categories = { workspace = true } 11 | 12 | [dependencies] 13 | num = "0.4.3" 14 | serde = { version = "1.0.207", features = ["derive"] } 15 | typenum = "1.17.0" 16 | curve25519-dalek = { version = "4.1.2" } 17 | k256 = { version = "0.13.3", features = ["expose-field"] } 18 | generic-array = { version = "1.1.0", features = ["alloc", "serde"] } 19 | amcl = { package = "snowbridge-amcl", version = "1.0.2", default-features = false, features = [ 20 | "bls381", 21 | ] } 22 | elliptic-curve = "0.13.8" 23 | dashu = "0.4.2" 24 | 25 | 26 | sp1-primitives = { workspace = true } 27 | p3-field = { workspace = true } 28 | itertools = "0.13.0" 29 | rug = { version = "1.26.1", optional = true } 30 | cfg-if = "1.0.0" 31 | 32 | [dev-dependencies] 33 | rand = "0.8.5" 34 | num = { version = "0.4.3", features = ["rand"] } 35 | 36 | [features] 37 | bigint-rug = ["rug"] 38 | -------------------------------------------------------------------------------- /crates/curves/src/edwards/ed25519.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use curve25519_dalek::edwards::CompressedEdwardsY; 4 | use generic_array::GenericArray; 5 | use num::{BigUint, Num, One}; 6 | use serde::{Deserialize, Serialize}; 7 | use typenum::{U32, U62}; 8 | 9 | use crate::{ 10 | edwards::{EdwardsCurve, EdwardsParameters}, 11 | params::{FieldParameters, NumLimbs}, 12 | AffinePoint, CurveType, EllipticCurveParameters, 13 | }; 14 | 15 | pub type Ed25519 = EdwardsCurve; 16 | 17 | #[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] 18 | pub struct Ed25519Parameters; 19 | 20 | #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] 21 | pub struct Ed25519BaseField; 22 | 23 | impl FieldParameters for Ed25519BaseField { 24 | const MODULUS: &'static [u8] = &[ 25 | 237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 26 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 27 | ]; 28 | 29 | const WITNESS_OFFSET: usize = 1usize << 14; 30 | 31 | fn modulus() -> BigUint { 32 | (BigUint::one() << 255) - BigUint::from(19u32) 33 | } 34 | } 35 | 36 | impl NumLimbs for Ed25519BaseField { 37 | type Limbs = U32; 38 | type Witness = U62; 39 | } 40 | 41 | impl EllipticCurveParameters for Ed25519Parameters { 42 | type BaseField = Ed25519BaseField; 43 | const CURVE_TYPE: CurveType = CurveType::Ed25519; 44 | } 45 | 46 | impl EdwardsParameters for Ed25519Parameters { 47 | const D: GenericArray = GenericArray::from_array([ 48 | 163, 120, 89, 19, 202, 77, 235, 117, 171, 216, 65, 65, 77, 10, 112, 0, 152, 232, 121, 119, 49 | 121, 64, 199, 140, 115, 254, 111, 43, 238, 108, 3, 82, 50 | ]); 51 | 52 | fn prime_group_order() -> BigUint { 53 | BigUint::from(2u32).pow(252) + BigUint::from(27742317777372353535851937790883648493u128) 54 | } 55 | 56 | fn generator() -> (BigUint, BigUint) { 57 | let x = BigUint::from_str_radix( 58 | "15112221349535400772501151409588531511454012693041857206046113283949847762202", 59 | 10, 60 | ) 61 | .unwrap(); 62 | let y = BigUint::from_str_radix( 63 | "46316835694926478169428394003475163141307993866256225615783033603165251855960", 64 | 10, 65 | ) 66 | .unwrap(); 67 | (x, y) 68 | } 69 | } 70 | 71 | /// Computes the square root of a number in the base field of Ed25519. 72 | /// 73 | /// This function always returns the nonnegative square root, in the sense that the least 74 | /// significant bit of the result is always 0. 75 | pub fn ed25519_sqrt(a: &BigUint) -> BigUint { 76 | // Here is a description of how to calculate sqrt in the Curve25519 base field: 77 | // ssh://git@github.com/succinctlabs/curve25519-dalek/blob/ 78 | // e2d1bd10d6d772af07cac5c8161cd7655016af6d/curve25519-dalek/src/field.rs#L256 79 | 80 | let modulus = Ed25519BaseField::modulus(); 81 | // The exponent is (modulus+3)/8; 82 | let mut beta = a.modpow( 83 | &BigUint::from_str( 84 | "7237005577332262213973186563042994240829374041602535252466099000494570602494", 85 | ) 86 | .unwrap(), 87 | &modulus, 88 | ); 89 | 90 | // The square root of -1 in the field. 91 | // Take from here: 92 | // ssh://git@github.com/succinctlabs/curve25519-dalek/blob/ 93 | // e2d1bd10d6d772af07cac5c8161cd7655016af6d/curve25519-dalek/src/backend/serial/u64/constants. 94 | // rs#L89 95 | let sqrt_m1 = BigUint::from_str( 96 | "19681161376707505956807079304988542015446066515923890162744021073123829784752", 97 | ) 98 | .unwrap(); 99 | 100 | let beta_squared = &beta * &beta % &modulus; 101 | let neg_a = &modulus - a; 102 | 103 | if beta_squared == neg_a { 104 | beta = (&beta * &sqrt_m1) % &modulus; 105 | } 106 | 107 | let correct_sign_sqrt = &beta_squared == a; 108 | let flipped_sign_sqrt = beta_squared == neg_a; 109 | 110 | if !correct_sign_sqrt && !flipped_sign_sqrt { 111 | panic!("a is not a square"); 112 | } 113 | 114 | let beta_bytes = beta.to_bytes_le(); 115 | if (beta_bytes[0] & 1) == 1 { 116 | beta = (&modulus - &beta) % &modulus; 117 | } 118 | 119 | beta 120 | } 121 | 122 | pub fn decompress(compressed_point: &CompressedEdwardsY) -> AffinePoint { 123 | let mut point_bytes = *compressed_point.as_bytes(); 124 | let sign = point_bytes[31] >> 7 == 1; 125 | // mask out the sign bit 126 | point_bytes[31] &= 0b0111_1111; 127 | let modulus = &Ed25519BaseField::modulus(); 128 | 129 | let y = &BigUint::from_bytes_le(&point_bytes); 130 | let yy = &((y * y) % modulus); 131 | let u = (yy - BigUint::one()) % modulus; // u = y²-1 132 | let v = &((yy * &Ed25519Parameters::d_biguint()) + &BigUint::one()) % modulus; // v = dy²+1 133 | 134 | let v_inv = v.modpow(&(modulus - BigUint::from(2u64)), modulus); 135 | let u_div_v = (u * &v_inv) % modulus; 136 | 137 | let mut x = ed25519_sqrt(&u_div_v); 138 | 139 | // sqrt always returns the nonnegative square root, 140 | // so we negate according to the supplied sign bit. 141 | if sign { 142 | x = modulus - &x; 143 | } 144 | 145 | AffinePoint::new(x, y.clone()) 146 | } 147 | 148 | #[cfg(test)] 149 | mod tests { 150 | 151 | use super::*; 152 | use num::traits::ToBytes; 153 | 154 | const NUM_TEST_CASES: usize = 100; 155 | 156 | #[test] 157 | fn test_ed25519_decompress() { 158 | // This test checks that decompression of generator, 2x generator, 4x generator, etc. works. 159 | 160 | // Get the generator point. 161 | let mut point = { 162 | let (x, y) = Ed25519Parameters::generator(); 163 | AffinePoint::>::new(x, y) 164 | }; 165 | for _ in 0..NUM_TEST_CASES { 166 | // Compress the point. The first 255 bits of a compressed point is the y-coordinate. The 167 | // high bit of the 32nd byte gives the "sign" of x, which is the parity. 168 | let compressed_point = { 169 | let x = point.x.to_le_bytes(); 170 | let y = point.y.to_le_bytes(); 171 | let mut compressed = [0u8; 32]; 172 | 173 | // Copy y into compressed. 174 | compressed[..y.len()].copy_from_slice(&y); 175 | 176 | // Set the sign bit. 177 | compressed[31] |= (x[0] & 1) << 7; 178 | 179 | CompressedEdwardsY(compressed) 180 | }; 181 | assert_eq!(point, decompress(&compressed_point)); 182 | 183 | // Double the point to create a "random" point for the next iteration. 184 | point = point.clone() + point.clone(); 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /crates/curves/src/edwards/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ed25519; 2 | 3 | use generic_array::GenericArray; 4 | use num::{BigUint, Zero}; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | use super::CurveType; 8 | use crate::{ 9 | params::{FieldParameters, NumLimbs}, 10 | AffinePoint, EllipticCurve, EllipticCurveParameters, 11 | }; 12 | 13 | use crate::{edwards::ed25519::Ed25519BaseField, params::NumWords}; 14 | use typenum::Unsigned; 15 | 16 | pub type Limbs = ::Limbs; 17 | pub const NUM_LIMBS: usize = Limbs::USIZE; 18 | 19 | pub type WordsFieldElement = ::WordsFieldElement; 20 | pub const WORDS_FIELD_ELEMENT: usize = WordsFieldElement::USIZE; 21 | 22 | #[allow(unused)] 23 | pub type WordsCurvePoint = ::WordsCurvePoint; 24 | pub const WORDS_CURVE_POINT: usize = WordsCurvePoint::USIZE; 25 | 26 | pub trait EdwardsParameters: EllipticCurveParameters { 27 | const D: GenericArray::Limbs>; 28 | 29 | fn generator() -> (BigUint, BigUint); 30 | 31 | fn prime_group_order() -> BigUint; 32 | 33 | fn d_biguint() -> BigUint { 34 | let mut modulus = BigUint::zero(); 35 | for (i, limb) in Self::D.iter().enumerate() { 36 | modulus += BigUint::from(*limb) << (8 * i); 37 | } 38 | modulus 39 | } 40 | 41 | fn neutral() -> (BigUint, BigUint) { 42 | (BigUint::from(0u32), BigUint::from(1u32)) 43 | } 44 | } 45 | 46 | #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)] 47 | #[serde(bound = "")] 48 | pub struct EdwardsCurve(pub E); 49 | 50 | impl EdwardsParameters for EdwardsCurve { 51 | const D: GenericArray::Limbs> = E::D; 52 | 53 | fn generator() -> (BigUint, BigUint) { 54 | E::generator() 55 | } 56 | 57 | fn prime_group_order() -> BigUint { 58 | E::prime_group_order() 59 | } 60 | 61 | fn d_biguint() -> BigUint { 62 | E::d_biguint() 63 | } 64 | 65 | fn neutral() -> (BigUint, BigUint) { 66 | E::neutral() 67 | } 68 | } 69 | 70 | impl EllipticCurveParameters for EdwardsCurve { 71 | type BaseField = E::BaseField; 72 | const CURVE_TYPE: CurveType = E::CURVE_TYPE; 73 | } 74 | 75 | impl EdwardsCurve { 76 | pub fn prime_group_order() -> BigUint { 77 | E::prime_group_order() 78 | } 79 | 80 | pub fn neutral() -> AffinePoint { 81 | let (x, y) = E::neutral(); 82 | AffinePoint::new(x, y) 83 | } 84 | } 85 | 86 | impl EllipticCurve for EdwardsCurve { 87 | fn ec_add(p: &AffinePoint, q: &AffinePoint) -> AffinePoint { 88 | p.ed_add(q) 89 | } 90 | 91 | fn ec_double(p: &AffinePoint) -> AffinePoint { 92 | p.ed_double() 93 | } 94 | 95 | fn ec_generator() -> AffinePoint { 96 | let (x, y) = E::generator(); 97 | AffinePoint::new(x, y) 98 | } 99 | 100 | fn ec_neutral() -> Option> { 101 | Some(Self::neutral()) 102 | } 103 | 104 | fn ec_neg(p: &AffinePoint) -> AffinePoint { 105 | let modulus = E::BaseField::modulus(); 106 | AffinePoint::new(&modulus - &p.x, p.y.clone()) 107 | } 108 | } 109 | 110 | impl AffinePoint> { 111 | pub(crate) fn ed_add( 112 | &self, 113 | other: &AffinePoint>, 114 | ) -> AffinePoint> { 115 | let p = ::BaseField::modulus(); 116 | let x_3n = (&self.x * &other.y + &self.y * &other.x) % &p; 117 | let y_3n = (&self.y * &other.y + &self.x * &other.x) % &p; 118 | 119 | let all_xy = (&self.x * &self.y * &other.x * &other.y) % &p; 120 | let d = E::d_biguint(); 121 | let dxy = (d * &all_xy) % &p; 122 | let den_x = ((1u32 + &dxy) % &p).modpow(&(&p - 2u32), &p); 123 | let den_y = ((1u32 + &p - &dxy) % &p).modpow(&(&p - 2u32), &p); 124 | 125 | let x_3 = (&x_3n * &den_x) % &p; 126 | let y_3 = (&y_3n * &den_y) % &p; 127 | 128 | AffinePoint::new(x_3, y_3) 129 | } 130 | 131 | pub(crate) fn ed_double(&self) -> AffinePoint> { 132 | self.ed_add(self) 133 | } 134 | } 135 | 136 | #[cfg(test)] 137 | mod tests { 138 | 139 | use num::bigint::RandBigInt; 140 | use rand::thread_rng; 141 | 142 | use super::*; 143 | use crate::edwards::ed25519::{Ed25519, Ed25519Parameters}; 144 | 145 | #[test] 146 | fn test_bigint_ed_add() { 147 | type E = Ed25519; 148 | let neutral = E::neutral(); 149 | let base = E::ec_generator(); 150 | 151 | assert_eq!(&base + &neutral, base); 152 | assert_eq!(&neutral + &base, base); 153 | assert_eq!(&neutral + &neutral, neutral); 154 | } 155 | 156 | #[test] 157 | fn test_biguint_scalar_mul() { 158 | type E = Ed25519; 159 | let base = E::ec_generator(); 160 | 161 | let d = Ed25519Parameters::d_biguint(); 162 | let p = ::BaseField::modulus(); 163 | assert_eq!((d * 121666u32) % &p, (&p - 121665u32) % &p); 164 | 165 | let mut rng = thread_rng(); 166 | for _ in 0..10 { 167 | let x = rng.gen_biguint(24); 168 | let y = rng.gen_biguint(25); 169 | 170 | let x_base = &base * &x; 171 | let y_x_base = &x_base * &y; 172 | let xy = &x * &y; 173 | let xy_base = &base * &xy; 174 | assert_eq!(y_x_base, xy_base); 175 | } 176 | 177 | let order = BigUint::from(2u32).pow(252) 178 | + BigUint::from(27742317777372353535851937790883648493u128); 179 | assert_eq!(base, &base + &(&base * &order)); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /crates/curves/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod edwards; 2 | pub mod params; 3 | // pub mod polynomial; 4 | pub mod scalar_mul; 5 | pub mod uint256; 6 | pub mod utils; 7 | pub mod weierstrass; 8 | pub mod polynomial2; 9 | 10 | pub mod curve25519_dalek { 11 | pub use curve25519_dalek::edwards::CompressedEdwardsY; 12 | } 13 | 14 | pub mod k256 { 15 | pub use k256::{ 16 | ecdsa::{RecoveryId, Signature, VerifyingKey}, 17 | elliptic_curve::ops::Invert, 18 | }; 19 | } 20 | 21 | use params::{FieldParameters, NumWords}; 22 | use sp1_primitives::consts::WORD_SIZE; 23 | use std::{ 24 | fmt::{Debug, Display, Formatter, Result}, 25 | ops::{Add, Neg}, 26 | }; 27 | use typenum::Unsigned; 28 | 29 | use num::BigUint; 30 | use serde::{de::DeserializeOwned, Serialize}; 31 | 32 | pub const NUM_WORDS_FIELD_ELEMENT: usize = 8; 33 | pub const NUM_BYTES_FIELD_ELEMENT: usize = NUM_WORDS_FIELD_ELEMENT * WORD_SIZE; 34 | pub const COMPRESSED_POINT_BYTES: usize = 32; 35 | 36 | /// Number of words needed to represent a point on an elliptic curve. This is twice the number of 37 | /// words needed to represent a field element as a point consists of the x and y coordinates. 38 | pub const NUM_WORDS_EC_POINT: usize = 2 * NUM_WORDS_FIELD_ELEMENT; 39 | 40 | #[derive(Debug, PartialEq, Eq)] 41 | pub enum CurveType { 42 | Secp256k1, 43 | Bn254, 44 | Ed25519, 45 | Bls12381, 46 | } 47 | 48 | impl Display for CurveType { 49 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 50 | match self { 51 | CurveType::Secp256k1 => write!(f, "Secp256k1"), 52 | CurveType::Bn254 => write!(f, "Bn254"), 53 | CurveType::Ed25519 => write!(f, "Ed25519"), 54 | CurveType::Bls12381 => write!(f, "Bls12381"), 55 | } 56 | } 57 | } 58 | 59 | #[derive(Debug, Clone, PartialEq, Eq)] 60 | pub struct AffinePoint { 61 | pub x: BigUint, 62 | pub y: BigUint, 63 | _marker: std::marker::PhantomData, 64 | } 65 | 66 | impl AffinePoint { 67 | #[allow(dead_code)] 68 | pub const fn new(x: BigUint, y: BigUint) -> Self { 69 | Self { x, y, _marker: std::marker::PhantomData } 70 | } 71 | 72 | pub fn from_words_le(words: &[u32]) -> Self { 73 | let x_bytes = 74 | words[0..words.len() / 2].iter().flat_map(|n| n.to_le_bytes()).collect::>(); 75 | let y_bytes = 76 | &words[words.len() / 2..].iter().flat_map(|n| n.to_le_bytes()).collect::>(); 77 | let x = BigUint::from_bytes_le(x_bytes.as_slice()); 78 | let y = BigUint::from_bytes_le(y_bytes.as_slice()); 79 | Self { x, y, _marker: std::marker::PhantomData } 80 | } 81 | 82 | pub fn to_words_le(&self) -> Vec { 83 | let num_words = ::WordsCurvePoint::USIZE; 84 | let num_bytes = num_words * 4; 85 | let half_words = num_words / 2; 86 | 87 | let mut x_bytes = self.x.to_bytes_le(); 88 | x_bytes.resize(num_bytes / 2, 0u8); 89 | let mut y_bytes = self.y.to_bytes_le(); 90 | y_bytes.resize(num_bytes / 2, 0u8); 91 | 92 | let mut words = vec![0u32; num_words]; 93 | 94 | for i in 0..half_words { 95 | let x = u32::from_le_bytes([ 96 | x_bytes[4 * i], 97 | x_bytes[4 * i + 1], 98 | x_bytes[4 * i + 2], 99 | x_bytes[4 * i + 3], 100 | ]); 101 | let y = u32::from_le_bytes([ 102 | y_bytes[4 * i], 103 | y_bytes[4 * i + 1], 104 | y_bytes[4 * i + 2], 105 | y_bytes[4 * i + 3], 106 | ]); 107 | 108 | words[i] = x; 109 | words[half_words + i] = y; 110 | } 111 | 112 | words 113 | } 114 | } 115 | 116 | pub trait EllipticCurveParameters: 117 | Debug + Send + Sync + Copy + Serialize + DeserializeOwned + 'static 118 | { 119 | type BaseField: FieldParameters + NumWords; 120 | 121 | const CURVE_TYPE: CurveType; 122 | } 123 | 124 | /// An interface for elliptic curve groups. 125 | pub trait EllipticCurve: EllipticCurveParameters { 126 | const NB_LIMBS: usize = Self::BaseField::NB_LIMBS; 127 | 128 | const NB_WITNESS_LIMBS: usize = Self::BaseField::NB_WITNESS_LIMBS; 129 | /// Adds two different points on the curve. 130 | /// 131 | /// Warning: This method assumes that the two points are different. 132 | fn ec_add(p: &AffinePoint, q: &AffinePoint) -> AffinePoint; 133 | 134 | /// Doubles a point on the curve. 135 | fn ec_double(p: &AffinePoint) -> AffinePoint; 136 | 137 | /// Returns the generator of the curve group for a curve/subgroup of prime order. 138 | fn ec_generator() -> AffinePoint; 139 | 140 | /// Returns the neutral element of the curve group, if this element is affine (such as in the 141 | /// case of the Edwards curve group). Otherwise, returns `None`. 142 | fn ec_neutral() -> Option>; 143 | 144 | /// Returns the negative of a point on the curve. 145 | fn ec_neg(p: &AffinePoint) -> AffinePoint; 146 | 147 | /// Returns the number of bits needed to represent a scalar in the group. 148 | fn nb_scalar_bits() -> usize { 149 | Self::BaseField::NB_LIMBS * Self::BaseField::NB_BITS_PER_LIMB 150 | } 151 | } 152 | 153 | impl Add<&AffinePoint> for &AffinePoint { 154 | type Output = AffinePoint; 155 | 156 | fn add(self, other: &AffinePoint) -> AffinePoint { 157 | E::ec_add(self, other) 158 | } 159 | } 160 | 161 | impl Add> for AffinePoint { 162 | type Output = AffinePoint; 163 | 164 | fn add(self, other: AffinePoint) -> AffinePoint { 165 | &self + &other 166 | } 167 | } 168 | 169 | impl Add<&AffinePoint> for AffinePoint { 170 | type Output = AffinePoint; 171 | 172 | fn add(self, other: &AffinePoint) -> AffinePoint { 173 | &self + other 174 | } 175 | } 176 | 177 | impl Neg for &AffinePoint { 178 | type Output = AffinePoint; 179 | 180 | fn neg(self) -> AffinePoint { 181 | E::ec_neg(self) 182 | } 183 | } 184 | 185 | impl Neg for AffinePoint { 186 | type Output = AffinePoint; 187 | 188 | fn neg(self) -> AffinePoint { 189 | -&self 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /crates/curves/src/params.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fmt::Debug, 3 | ops::{Div, Index, IndexMut}, 4 | slice::Iter, 5 | }; 6 | 7 | use serde::{de::DeserializeOwned, Serialize}; 8 | 9 | use typenum::{Unsigned, U2, U4}; 10 | 11 | use generic_array::{sequence::GenericSequence, ArrayLength, GenericArray}; 12 | use num::BigUint; 13 | use crate::polynomial2::Polynomial; 14 | 15 | use p3_field::Field; 16 | 17 | use crate::utils::biguint_from_limbs; 18 | 19 | pub const NB_BITS_PER_LIMB: usize = 8; 20 | 21 | /// An array representing N limbs of T. 22 | /// 23 | /// GenericArray allows us to constrain the correct array lengths so we can have # of limbs and # of 24 | /// witness limbs associated in NumLimbs / FieldParameters. 25 | /// See: https://github.com/RustCrypto/traits/issues/1481 26 | #[derive(Debug, Clone)] 27 | pub struct Limbs(pub GenericArray); 28 | 29 | pub trait FieldParameters: 30 | Send + Sync + Copy + 'static + Debug + Serialize + DeserializeOwned + NumLimbs 31 | { 32 | const NB_BITS_PER_LIMB: usize = NB_BITS_PER_LIMB; 33 | const NB_LIMBS: usize = Self::Limbs::USIZE; 34 | const NB_WITNESS_LIMBS: usize = Self::Witness::USIZE; 35 | const WITNESS_OFFSET: usize; 36 | 37 | /// The bytes of the modulus in little-endian order. 38 | const MODULUS: &'static [u8]; 39 | 40 | fn modulus() -> BigUint { 41 | biguint_from_limbs(Self::MODULUS) 42 | } 43 | 44 | fn nb_bits() -> usize { 45 | Self::NB_BITS_PER_LIMB * Self::NB_LIMBS 46 | } 47 | 48 | fn modulus_field_iter() -> impl Iterator { 49 | Self::MODULUS.iter().map(|x| F::from_canonical_u8(*x)).take(Self::NB_LIMBS) 50 | } 51 | 52 | /// Convert a BigUint to a Vec of u8 limbs (with len NB_LIMBS). 53 | fn to_limbs(x: &BigUint) -> Vec { 54 | let mut bytes = x.to_bytes_le(); 55 | bytes.resize(Self::NB_LIMBS, 0u8); 56 | bytes 57 | } 58 | 59 | /// Convert a BigUint to a Vec of F limbs (with len NB_LIMBS). 60 | fn to_limbs_field_vec, F: Field>(x: &BigUint) -> Vec { 61 | Self::to_limbs(x).into_iter().map(|x| F::from_canonical_u8(x).into()).collect::>() 62 | } 63 | 64 | /// Convert a BigUint to Limbs. 65 | fn to_limbs_field, F: Field>(x: &BigUint) -> Limbs { 66 | limbs_from_vec(Self::to_limbs_field_vec(x)) 67 | } 68 | } 69 | 70 | /// Convert a vec of F limbs to a Limbs of N length. 71 | pub fn limbs_from_vec, N: ArrayLength, F: Field>(limbs: Vec) -> Limbs { 72 | debug_assert_eq!(limbs.len(), N::USIZE); 73 | let mut result = GenericArray::::generate(|_i| F::zero().into()); 74 | for (i, limb) in limbs.into_iter().enumerate() { 75 | result[i] = limb.into(); 76 | } 77 | Limbs(result) 78 | } 79 | 80 | /// Trait that holds the typenum values for # of limbs and # of witness limbs. 81 | pub trait NumLimbs: Clone + Debug { 82 | type Limbs: ArrayLength + Debug; 83 | type Witness: ArrayLength + Debug; 84 | } 85 | 86 | /// Trait that holds number of words needed to represent a field element and a curve point. 87 | pub trait NumWords: Clone + Debug { 88 | /// The number of words needed to represent a field element. 89 | type WordsFieldElement: ArrayLength + Debug; 90 | /// The number of words needed to represent a curve point (two field elements). 91 | type WordsCurvePoint: ArrayLength + Debug; 92 | } 93 | 94 | /// Implement NumWords for NumLimbs where # Limbs is divisible by 4. 95 | /// 96 | /// Using typenum we can do N/4 and N/2 in type-level arithmetic. Having it as a separate trait 97 | /// avoids needing the Div where clauses everywhere. 98 | impl NumWords for N 99 | where 100 | N::Limbs: Div, 101 | N::Limbs: Div, 102 | >::Output: ArrayLength + Debug, 103 | >::Output: ArrayLength + Debug, 104 | { 105 | /// Each word has 4 limbs so we divide by 4. 106 | type WordsFieldElement = >::Output; 107 | /// Curve point has 2 field elements so we divide by 2. 108 | type WordsCurvePoint = >::Output; 109 | } 110 | 111 | impl Copy for Limbs where N::ArrayType: Copy {} 112 | 113 | impl Default for Limbs 114 | where 115 | T: Default + Copy, 116 | { 117 | fn default() -> Self { 118 | Self(GenericArray::default()) 119 | } 120 | } 121 | 122 | impl Index for Limbs { 123 | type Output = T; 124 | 125 | fn index(&self, index: usize) -> &Self::Output { 126 | &self.0[index] 127 | } 128 | } 129 | 130 | impl IndexMut for Limbs { 131 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 132 | &mut self.0[index] 133 | } 134 | } 135 | 136 | impl IntoIterator for Limbs { 137 | type Item = T; 138 | type IntoIter = as IntoIterator>::IntoIter; 139 | 140 | fn into_iter(self) -> Self::IntoIter { 141 | self.0.into_iter() 142 | } 143 | } 144 | 145 | impl + Clone, N: ArrayLength, Expr: Clone> From> 146 | for Polynomial 147 | { 148 | fn from(value: Limbs) -> Self { 149 | Polynomial::from_coefficients(&value.0.into_iter().map(|x| x.into()).collect::>()) 150 | } 151 | } 152 | 153 | impl From> for Limbs { 154 | fn from(value: Polynomial) -> Self { 155 | let inner = value.as_coefficients().try_into().unwrap(); 156 | Self(inner) 157 | } 158 | } 159 | 160 | impl<'a, T: Debug + Default + Clone, N: ArrayLength> From> for Limbs { 161 | fn from(value: Iter<'a, T>) -> Self { 162 | let vec: Vec = value.cloned().collect(); 163 | let inner = vec.try_into().unwrap(); 164 | Self(inner) 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /crates/curves/src/scalar_mul.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Mul; 2 | 3 | use num::{BigUint, One}; 4 | 5 | use super::{utils::biguint_to_bits_le, AffinePoint, EllipticCurve}; 6 | 7 | impl AffinePoint { 8 | pub fn scalar_mul(&self, scalar: &BigUint) -> Self { 9 | let power_two_modulus = BigUint::one() << E::nb_scalar_bits(); 10 | let scalar = scalar % &power_two_modulus; 11 | let mut result = E::ec_neutral(); 12 | let mut temp = self.clone(); 13 | let bits = biguint_to_bits_le(&scalar, E::nb_scalar_bits()); 14 | for bit in bits { 15 | if bit { 16 | result = result.map_or_else(|| Some(temp.clone()), |r| Some(&r + &temp)); 17 | } 18 | temp = &temp + &temp; 19 | } 20 | result.expect("Scalar multiplication failed") 21 | } 22 | } 23 | 24 | impl Mul<&BigUint> for &AffinePoint { 25 | type Output = AffinePoint; 26 | 27 | fn mul(self, scalar: &BigUint) -> AffinePoint { 28 | self.scalar_mul(scalar) 29 | } 30 | } 31 | 32 | impl Mul for &AffinePoint { 33 | type Output = AffinePoint; 34 | 35 | fn mul(self, scalar: BigUint) -> AffinePoint { 36 | self.scalar_mul(&scalar) 37 | } 38 | } 39 | 40 | impl Mul for AffinePoint { 41 | type Output = AffinePoint; 42 | 43 | fn mul(self, scalar: BigUint) -> AffinePoint { 44 | self.scalar_mul(&scalar) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /crates/curves/src/uint256.rs: -------------------------------------------------------------------------------- 1 | use typenum::{U32, U63}; 2 | 3 | use num::{BigUint, One}; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::params::{FieldParameters, NumLimbs}; 7 | 8 | /// Although `U256` is technically not a field, we utilize `FieldParameters` here for compatibility. 9 | /// This approach is specifically for the `FieldOps` multiplication operation, which employs these 10 | /// parameters solely as a modulus, rather than enforcing the requirement of being a proper field. 11 | #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] 12 | pub struct U256Field; 13 | 14 | impl FieldParameters for U256Field { 15 | /// The modulus of the field. It is represented as a little-endian array of 33 bytes. 16 | const MODULUS: &'static [u8] = &[ 17 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18 | 0, 0, 1, 19 | ]; 20 | 21 | /// A rough witness-offset estimate given the size of the limbs and the size of the field. 22 | const WITNESS_OFFSET: usize = 1usize << 14; 23 | 24 | /// The modulus of Uint235 is 2^256. 25 | fn modulus() -> BigUint { 26 | BigUint::one() << 256 27 | } 28 | } 29 | 30 | impl NumLimbs for U256Field { 31 | type Limbs = U32; 32 | // Note we use one more limb than usual because for mulmod with mod 1<<256, we need an extra 33 | // limb. 34 | type Witness = U63; 35 | } 36 | -------------------------------------------------------------------------------- /crates/curves/src/utils.rs: -------------------------------------------------------------------------------- 1 | use num::BigUint; 2 | 3 | pub fn biguint_to_bits_le(integer: &BigUint, num_bits: usize) -> Vec { 4 | let byte_vec = integer.to_bytes_le(); 5 | let mut bits = Vec::new(); 6 | for byte in byte_vec { 7 | for i in 0..8 { 8 | bits.push(byte & (1 << i) != 0); 9 | } 10 | } 11 | debug_assert!(bits.len() <= num_bits, "Number too large to fit in {num_bits} digits"); 12 | bits.resize(num_bits, false); 13 | bits 14 | } 15 | 16 | pub fn biguint_to_limbs(integer: &BigUint) -> [u8; N] { 17 | let mut bytes = integer.to_bytes_le(); 18 | debug_assert!(bytes.len() <= N, "Number too large to fit in {N} limbs"); 19 | bytes.resize(N, 0u8); 20 | let mut limbs = [0u8; N]; 21 | limbs.copy_from_slice(&bytes); 22 | limbs 23 | } 24 | 25 | #[inline] 26 | pub fn biguint_from_limbs(limbs: &[u8]) -> BigUint { 27 | BigUint::from_bytes_le(limbs) 28 | } 29 | 30 | cfg_if::cfg_if! { 31 | if #[cfg(feature = "bigint-rug")] { 32 | pub fn biguint_to_rug(integer: &BigUint) -> rug::Integer { 33 | let mut int = rug::Integer::new(); 34 | unsafe { 35 | int.assign_bytes_radix_unchecked(integer.to_bytes_be().as_slice(), 256, false); 36 | } 37 | int 38 | } 39 | 40 | pub fn rug_to_biguint(integer: &rug::Integer) -> BigUint { 41 | let be_bytes = integer.to_digits::(rug::integer::Order::MsfBe); 42 | BigUint::from_bytes_be(&be_bytes) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /crates/curves/src/weierstrass/bls12_381.rs: -------------------------------------------------------------------------------- 1 | use amcl::bls381::{big::Big, bls381::utils::deserialize_g1, fp::FP}; 2 | use generic_array::GenericArray; 3 | use num::{BigUint, Num, Zero}; 4 | use serde::{Deserialize, Serialize}; 5 | use typenum::{U48, U94}; 6 | 7 | use super::{FieldType, FpOpField, SwCurve, WeierstrassParameters}; 8 | use crate::{ 9 | params::{FieldParameters, NumLimbs}, 10 | CurveType, EllipticCurveParameters, 11 | }; 12 | 13 | /// Bls12-381 curve parameter 14 | use crate::{AffinePoint, EllipticCurve}; 15 | 16 | // Serialization flags 17 | const COMPRESSION_FLAG: u8 = 0b_1000_0000; 18 | const Y_IS_ODD_FLAG: u8 = 0b_0010_0000; 19 | 20 | #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] 21 | /// Bls12381 curve parameter 22 | pub struct Bls12381Parameters; 23 | 24 | pub type Bls12381 = SwCurve; 25 | 26 | #[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)] 27 | /// Bls12381 base field parameter 28 | pub struct Bls12381BaseField; 29 | 30 | impl FieldParameters for Bls12381BaseField { 31 | // The modulus has been taken from py_ecc python library by Ethereum Foundation. 32 | // // https://github.com/ethereum/py_ecc/blob/7b9e1b3/py_ecc/fields/field_properties.py#L30 33 | // The below value is the little-endian representation of the modulus. 34 | const NB_LIMBS: usize = 48; 35 | 36 | const MODULUS: &'static [u8] = &[ 37 | 171, 170, 255, 255, 255, 255, 254, 185, 255, 255, 83, 177, 254, 255, 171, 30, 36, 246, 176, 38 | 246, 160, 210, 48, 103, 191, 18, 133, 243, 132, 75, 119, 100, 215, 172, 75, 67, 182, 167, 39 | 27, 75, 154, 230, 127, 57, 234, 17, 1, 26, 40 | ]; 41 | 42 | // A rough witness-offset estimate given the size of the limbs and the size of the field. 43 | const WITNESS_OFFSET: usize = 1usize << 15; 44 | 45 | fn modulus() -> BigUint { 46 | BigUint::from_str_radix( 47 | "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787", 48 | 10, 49 | ) 50 | .unwrap() 51 | } 52 | } 53 | 54 | impl FpOpField for Bls12381BaseField { 55 | const FIELD_TYPE: FieldType = FieldType::Bls12381; 56 | } 57 | 58 | impl NumLimbs for Bls12381BaseField { 59 | type Limbs = U48; 60 | type Witness = U94; 61 | } 62 | 63 | impl EllipticCurveParameters for Bls12381Parameters { 64 | type BaseField = Bls12381BaseField; 65 | const CURVE_TYPE: CurveType = CurveType::Bls12381; 66 | } 67 | 68 | impl WeierstrassParameters for Bls12381Parameters { 69 | // The values of `A` and `B` has been taken from py_ecc python library by Ethereum Foundation. 70 | // https://github.com/ethereum/py_ecc/blob/7b9e1b3/py_ecc/bls12_381/bls12_381_curve.py#L31 71 | const A: GenericArray = GenericArray::from_array([ 72 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74 | ]); 75 | 76 | const B: GenericArray = GenericArray::from_array([ 77 | 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79 | ]); 80 | 81 | fn generator() -> (BigUint, BigUint) { 82 | let x = BigUint::from_str_radix( 83 | "3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507", 84 | 10, 85 | ) 86 | .unwrap(); 87 | let y = BigUint::from_str_radix( 88 | "1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569", 89 | 10, 90 | ) 91 | .unwrap(); 92 | (x, y) 93 | } 94 | 95 | // The prime group order has been taken from py_ecc python library by Ethereum Foundation. 96 | // https://github.com/ethereum/py_ecc/blob/7b9e1b3/py_ecc/bls12_381/bls12_381_curve.py#L21-L23 97 | fn prime_group_order() -> num::BigUint { 98 | BigUint::from_str_radix( 99 | "52435875175126190479447740508185965837690552500527637822603658699938581184513", 100 | 10, 101 | ) 102 | .unwrap() 103 | } 104 | 105 | fn a_int() -> BigUint { 106 | BigUint::zero() 107 | } 108 | 109 | fn b_int() -> BigUint { 110 | BigUint::from(4u32) 111 | } 112 | } 113 | 114 | pub fn bls12381_decompress(bytes_be: &[u8], sign_bit: u32) -> AffinePoint { 115 | let mut g1_bytes_be: [u8; 48] = bytes_be.try_into().unwrap(); 116 | let mut flags = COMPRESSION_FLAG; 117 | if sign_bit == 1 { 118 | flags |= Y_IS_ODD_FLAG; 119 | }; 120 | 121 | // set sign and compression flag 122 | g1_bytes_be[0] |= flags; 123 | let point = deserialize_g1(&g1_bytes_be).unwrap(); 124 | 125 | let x_str = point.getx().to_string(); 126 | let x = BigUint::from_str_radix(x_str.as_str(), 16).unwrap(); 127 | let y_str = point.gety().to_string(); 128 | let y = BigUint::from_str_radix(y_str.as_str(), 16).unwrap(); 129 | 130 | AffinePoint::new(x, y) 131 | } 132 | 133 | pub fn bls12381_sqrt(a: &BigUint) -> BigUint { 134 | let a_big = Big::from_bytes(a.to_bytes_be().as_slice()); 135 | 136 | let a_sqrt = FP::new_big(a_big).sqrt(); 137 | 138 | BigUint::from_str_radix(a_sqrt.to_string().as_str(), 16).unwrap() 139 | } 140 | 141 | #[cfg(test)] 142 | mod tests { 143 | 144 | use amcl::bls381::bls381::proof_of_possession::G1_BYTES; 145 | 146 | use super::*; 147 | use crate::utils::biguint_from_limbs; 148 | use num::bigint::RandBigInt; 149 | use rand::thread_rng; 150 | 151 | const NUM_TEST_CASES: usize = 10; 152 | 153 | #[test] 154 | fn test_weierstrass_biguint_scalar_mul() { 155 | assert_eq!(biguint_from_limbs(Bls12381BaseField::MODULUS), Bls12381BaseField::modulus()); 156 | } 157 | 158 | #[test] 159 | fn test_bls12381_decompress() { 160 | // This test checks that decompression of generator, 2x generator, 4x generator, etc. works. 161 | 162 | // Get the generator point. 163 | let mut point = { 164 | let (x, y) = Bls12381Parameters::generator(); 165 | AffinePoint::>::new(x, y) 166 | }; 167 | for _ in 0..NUM_TEST_CASES { 168 | let (compressed_point, is_odd) = { 169 | let mut result = [0u8; G1_BYTES]; 170 | let x = point.x.to_bytes_le(); 171 | result[..x.len()].copy_from_slice(&x); 172 | result.reverse(); 173 | 174 | // Evaluate if y > -y 175 | let y = point.y.clone(); 176 | let y_neg = Bls12381BaseField::modulus() - y.clone(); 177 | 178 | // Set flags 179 | let mut is_odd = 0; 180 | if y > y_neg { 181 | result[0] += Y_IS_ODD_FLAG; 182 | is_odd = 1; 183 | } 184 | result[0] += COMPRESSION_FLAG; 185 | 186 | (result, is_odd) 187 | }; 188 | assert_eq!(point, bls12381_decompress(&compressed_point, is_odd)); 189 | 190 | // Double the point to create a "random" point for the next iteration. 191 | point = point.clone().sw_double(); 192 | } 193 | } 194 | 195 | #[test] 196 | fn test_bls12381_sqrt() { 197 | let mut rng = thread_rng(); 198 | for _ in 0..NUM_TEST_CASES { 199 | // Check that sqrt(x^2)^2 == x^2 200 | // We use x^2 since not all field elements have a square root 201 | let x = rng.gen_biguint(256) % Bls12381BaseField::modulus(); 202 | let x_2 = (&x * &x) % Bls12381BaseField::modulus(); 203 | let sqrt = bls12381_sqrt(&x_2); 204 | let sqrt_2 = (&sqrt * &sqrt) % Bls12381BaseField::modulus(); 205 | assert_eq!(sqrt_2, x_2); 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /crates/curves/src/weierstrass/bn254.rs: -------------------------------------------------------------------------------- 1 | use generic_array::GenericArray; 2 | use num::{BigUint, Num, Zero}; 3 | use serde::{Deserialize, Serialize}; 4 | use typenum::{U32, U62}; 5 | 6 | use super::{FieldType, FpOpField, SwCurve, WeierstrassParameters}; 7 | use crate::{ 8 | params::{FieldParameters, NumLimbs}, 9 | CurveType, EllipticCurveParameters, 10 | }; 11 | 12 | #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] 13 | /// Bn254 curve parameter 14 | pub struct Bn254Parameters; 15 | 16 | pub type Bn254 = SwCurve; 17 | 18 | #[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)] 19 | /// Bn254 base field parameter 20 | pub struct Bn254BaseField; 21 | 22 | impl FieldParameters for Bn254BaseField { 23 | const MODULUS: &'static [u8] = &[ 24 | 71, 253, 124, 216, 22, 140, 32, 60, 141, 202, 113, 104, 145, 106, 129, 151, 93, 88, 129, 25 | 129, 182, 69, 80, 184, 41, 160, 49, 225, 114, 78, 100, 48, 26 | ]; 27 | 28 | // A rough witness-offset estimate given the size of the limbs and the size of the field. 29 | const WITNESS_OFFSET: usize = 1usize << 14; 30 | 31 | // The modulus has been taken from py_ecc python library by Ethereum Foundation. 32 | // https://github.com/ethereum/py_pairing/blob/5f609da/py_ecc/bn128/bn128_field_elements.py#L10-L11 33 | fn modulus() -> BigUint { 34 | BigUint::from_str_radix( 35 | "21888242871839275222246405745257275088696311157297823662689037894645226208583", 36 | 10, 37 | ) 38 | .unwrap() 39 | } 40 | } 41 | 42 | impl FpOpField for Bn254BaseField { 43 | const FIELD_TYPE: FieldType = FieldType::Bn254; 44 | } 45 | 46 | impl NumLimbs for Bn254BaseField { 47 | type Limbs = U32; 48 | type Witness = U62; 49 | } 50 | 51 | impl EllipticCurveParameters for Bn254Parameters { 52 | type BaseField = Bn254BaseField; 53 | 54 | const CURVE_TYPE: CurveType = CurveType::Bn254; 55 | } 56 | 57 | impl WeierstrassParameters for Bn254Parameters { 58 | // The values have been taken from py_ecc python library by Ethereum Foundation. 59 | // https://github.com/ethereum/py_pairing/blob/5f609da/py_ecc/bn128/bn128_field_elements.py 60 | const A: GenericArray = GenericArray::from_array([ 61 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62 | 0, 0, 63 | ]); 64 | 65 | const B: GenericArray = GenericArray::from_array([ 66 | 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67 | 0, 0, 68 | ]); 69 | fn generator() -> (BigUint, BigUint) { 70 | let x = BigUint::from(1u32); 71 | let y = BigUint::from(2u32); 72 | (x, y) 73 | } 74 | 75 | fn prime_group_order() -> num::BigUint { 76 | BigUint::from_str_radix( 77 | "21888242871839275222246405745257275088548364400416034343698204186575808495617", 78 | 10, 79 | ) 80 | .unwrap() 81 | } 82 | 83 | fn a_int() -> BigUint { 84 | BigUint::zero() 85 | } 86 | 87 | fn b_int() -> BigUint { 88 | BigUint::from(3u32) 89 | } 90 | } 91 | 92 | #[cfg(test)] 93 | mod tests { 94 | 95 | use super::*; 96 | use crate::utils::biguint_from_limbs; 97 | 98 | #[test] 99 | fn test_weierstrass_biguint_scalar_mul() { 100 | assert_eq!(biguint_from_limbs(Bn254BaseField::MODULUS), Bn254BaseField::modulus()); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /crates/curves/src/weierstrass/secp256k1.rs: -------------------------------------------------------------------------------- 1 | //! Modulo defining the Secp256k1 curve and its base field. The constants are all taken from 2 | //! https://en.bitcoin.it/wiki/Secp256k1. 3 | 4 | use std::str::FromStr; 5 | 6 | use elliptic_curve::{sec1::ToEncodedPoint, subtle::Choice}; 7 | use generic_array::GenericArray; 8 | use k256::{elliptic_curve::point::DecompressPoint, FieldElement}; 9 | use num::{ 10 | traits::{FromBytes, ToBytes}, 11 | BigUint, Zero, 12 | }; 13 | use serde::{Deserialize, Serialize}; 14 | use typenum::{U32, U62}; 15 | 16 | use super::{SwCurve, WeierstrassParameters}; 17 | use crate::{ 18 | params::{FieldParameters, NumLimbs}, 19 | AffinePoint, CurveType, EllipticCurve, EllipticCurveParameters, 20 | }; 21 | 22 | #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] 23 | /// Secp256k1 curve parameter 24 | pub struct Secp256k1Parameters; 25 | 26 | pub type Secp256k1 = SwCurve; 27 | 28 | #[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)] 29 | /// Secp256k1 base field parameter 30 | pub struct Secp256k1BaseField; 31 | 32 | impl FieldParameters for Secp256k1BaseField { 33 | const MODULUS: &'static [u8] = &[ 34 | 0x2f, 0xfc, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 35 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 36 | 0xff, 0xff, 37 | ]; 38 | 39 | /// A rough witness-offset estimate given the size of the limbs and the size of the field. 40 | const WITNESS_OFFSET: usize = 1usize << 14; 41 | 42 | fn modulus() -> BigUint { 43 | BigUint::from_bytes_le(Self::MODULUS) 44 | } 45 | } 46 | 47 | impl NumLimbs for Secp256k1BaseField { 48 | type Limbs = U32; 49 | type Witness = U62; 50 | } 51 | 52 | impl EllipticCurveParameters for Secp256k1Parameters { 53 | type BaseField = Secp256k1BaseField; 54 | const CURVE_TYPE: CurveType = CurveType::Secp256k1; 55 | } 56 | 57 | impl WeierstrassParameters for Secp256k1Parameters { 58 | const A: GenericArray = GenericArray::from_array([ 59 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60 | 0, 0, 61 | ]); 62 | 63 | const B: GenericArray = GenericArray::from_array([ 64 | 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65 | 0, 0, 66 | ]); 67 | fn generator() -> (BigUint, BigUint) { 68 | let x = BigUint::from_str( 69 | "55066263022277343669578718895168534326250603453777594175500187360389116729240", 70 | ) 71 | .unwrap(); 72 | let y = BigUint::from_str( 73 | "32670510020758816978083085130507043184471273380659243275938904335757337482424", 74 | ) 75 | .unwrap(); 76 | (x, y) 77 | } 78 | 79 | fn prime_group_order() -> num::BigUint { 80 | BigUint::from_slice(&[ 81 | 0xD0364141, 0xBFD25E8C, 0xAF48A03B, 0xBAAEDCE6, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 82 | 0xFFFFFFFF, 83 | ]) 84 | } 85 | 86 | fn a_int() -> BigUint { 87 | BigUint::zero() 88 | } 89 | 90 | fn b_int() -> BigUint { 91 | BigUint::from(7u32) 92 | } 93 | } 94 | 95 | pub fn secp256k1_decompress(bytes_be: &[u8], sign: u32) -> AffinePoint { 96 | let computed_point = 97 | k256::AffinePoint::decompress(bytes_be.into(), Choice::from(sign as u8)).unwrap(); 98 | let point = computed_point.to_encoded_point(false); 99 | 100 | let x = BigUint::from_bytes_be(point.x().unwrap()); 101 | let y = BigUint::from_bytes_be(point.y().unwrap()); 102 | AffinePoint::::new(x, y) 103 | } 104 | 105 | pub fn secp256k1_sqrt(n: &BigUint) -> BigUint { 106 | let be_bytes = n.to_be_bytes(); 107 | let mut bytes = [0_u8; 32]; 108 | bytes[32 - be_bytes.len()..].copy_from_slice(&be_bytes); 109 | let fe = FieldElement::from_bytes(&bytes.into()).unwrap(); 110 | let result_bytes = fe.sqrt().unwrap().normalize().to_bytes(); 111 | BigUint::from_be_bytes(&result_bytes as &[u8]) 112 | } 113 | 114 | #[cfg(test)] 115 | mod tests { 116 | 117 | use super::*; 118 | use crate::utils::biguint_from_limbs; 119 | use num::bigint::RandBigInt; 120 | use rand::thread_rng; 121 | 122 | #[test] 123 | fn test_weierstrass_biguint_scalar_mul() { 124 | assert_eq!(biguint_from_limbs(Secp256k1BaseField::MODULUS), Secp256k1BaseField::modulus()); 125 | } 126 | 127 | #[test] 128 | fn test_secp256k_sqrt() { 129 | let mut rng = thread_rng(); 130 | for _ in 0..10 { 131 | // Check that sqrt(x^2)^2 == x^2 132 | // We use x^2 since not all field elements have a square root 133 | let x = rng.gen_biguint(256) % Secp256k1BaseField::modulus(); 134 | let x_2 = (&x * &x) % Secp256k1BaseField::modulus(); 135 | let sqrt = secp256k1_sqrt(&x_2); 136 | 137 | println!("sqrt: {}", sqrt); 138 | 139 | let sqrt_2 = (&sqrt * &sqrt) % Secp256k1BaseField::modulus(); 140 | 141 | assert_eq!(sqrt_2, x_2); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /crates/executor/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [1.2.0-rc1](https://github.com/succinctlabs/sp1/releases/tag/sp1-core-executor-v1.2.0-rc1) - 2024-08-23 11 | 12 | ### Added 13 | 14 | - gas ([#1354](https://github.com/succinctlabs/sp1/pull/1354)) 15 | 16 | ### Fixed 17 | 18 | - fix artifact export test 19 | - fix fptower tests 20 | - fix imports 21 | - cargo check on tests 22 | 23 | ### Other 24 | 25 | - runtime optimizations ([#1344](https://github.com/succinctlabs/sp1/pull/1344)) 26 | - make state pub ([#1345](https://github.com/succinctlabs/sp1/pull/1345)) 27 | - resolve merge conflicts between dev and experimental 28 | - refactor + cleanup core crates 29 | -------------------------------------------------------------------------------- /crates/executor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sp1-core-executor" 3 | description = "SP1 is a performant, 100% open-source, contributor-friendly zkVM." 4 | readme = "../../../README.md" 5 | version = { workspace = true } 6 | edition = { workspace = true } 7 | license = { workspace = true } 8 | repository = { workspace = true } 9 | keywords = { workspace = true } 10 | categories = { workspace = true } 11 | 12 | [dependencies] 13 | # sp1 14 | sp1-primitives = { workspace = true } 15 | sp1-curves = { workspace = true } 16 | # sp1-stark = { workspace = true } 17 | 18 | # p3 19 | p3-field = { workspace = true } 20 | p3-maybe-rayon = { workspace = true, features = ["parallel"] } 21 | 22 | # misc 23 | serde = { version = "1.0.205", features = ["derive", "rc"] } 24 | elf = "0.7.4" 25 | rrs_lib = { package = "rrs-succinct", version = "0.1.0" } 26 | eyre = "0.6.12" 27 | bincode = "1.3.3" 28 | hashbrown = { version = "0.14.5", features = ["serde", "inline-more"] } 29 | itertools = "0.13.0" 30 | rand = "0.8.5" 31 | num = { version = "0.4.3" } 32 | typenum = "1.17.0" 33 | nohash-hasher = "0.2.0" 34 | thiserror = "1.0.63" 35 | tracing = "0.1.40" 36 | strum_macros = "0.26.4" 37 | strum = { version = "0.26.3", features = ["derive"] } 38 | log = "0.4.22" 39 | hex = "0.4.3" 40 | bytemuck = "1.16.3" 41 | tiny-keccak = { version = "2.0.2", features = ["keccak"] } 42 | vec_map = { version = "0.8.2", features = ["serde"] } 43 | enum-map = { version = "2.7.3", features = ["serde"] } 44 | 45 | [dev-dependencies] 46 | alloy-primitives = "0.8.5" 47 | 48 | [features] 49 | programs = [] 50 | bigint-rug = ["sp1-curves/bigint-rug"] 51 | -------------------------------------------------------------------------------- /crates/executor/src/context.rs: -------------------------------------------------------------------------------- 1 | use core::mem::take; 2 | 3 | use hashbrown::HashMap; 4 | 5 | use crate::hook::{hookify, BoxedHook, HookEnv, HookRegistry}; 6 | 7 | /// Context to run a program inside SP1. 8 | #[derive(Clone, Default)] 9 | pub struct SP1Context<'a> { 10 | /// The registry of hooks invocable from inside SP1. 11 | /// 12 | /// Note: `None` denotes the default list of hooks. 13 | pub hook_registry: Option>, 14 | 15 | /// The maximum number of cpu cycles to use for execution. 16 | pub max_cycles: Option, 17 | } 18 | 19 | /// A builder for [`SP1Context`]. 20 | #[derive(Clone, Default)] 21 | pub struct SP1ContextBuilder<'a> { 22 | no_default_hooks: bool, 23 | hook_registry_entries: Vec<(u32, BoxedHook<'a>)>, 24 | max_cycles: Option, 25 | } 26 | 27 | impl<'a> SP1Context<'a> { 28 | /// Create a new context builder. See [`SP1ContextBuilder`] for more details. 29 | #[must_use] 30 | pub fn builder() -> SP1ContextBuilder<'a> { 31 | SP1ContextBuilder::new() 32 | } 33 | } 34 | 35 | impl<'a> SP1ContextBuilder<'a> { 36 | /// Create a new [`SP1ContextBuilder`]. 37 | /// 38 | /// Prefer using [`SP1Context::builder`]. 39 | #[must_use] 40 | pub fn new() -> Self { 41 | SP1ContextBuilder::default() 42 | } 43 | 44 | /// Build and return the [`SP1Context`]. 45 | /// 46 | /// Clears and resets the builder, allowing it to be reused. 47 | pub fn build(&mut self) -> SP1Context<'a> { 48 | // If hook_registry_entries is nonempty or no_default_hooks true, 49 | // indicating a non-default value of hook_registry. 50 | let hook_registry = 51 | (!self.hook_registry_entries.is_empty() || self.no_default_hooks).then(|| { 52 | let mut table = if take(&mut self.no_default_hooks) { 53 | HashMap::default() 54 | } else { 55 | HookRegistry::default().table 56 | }; 57 | // Allows overwriting default hooks. 58 | table.extend(take(&mut self.hook_registry_entries)); 59 | HookRegistry { table } 60 | }); 61 | let cycle_limit = take(&mut self.max_cycles); 62 | SP1Context { 63 | hook_registry, 64 | max_cycles: cycle_limit, 65 | } 66 | } 67 | 68 | /// Add a runtime [Hook](super::Hook) into the context. 69 | /// 70 | /// Hooks may be invoked from within SP1 by writing to the specified file descriptor `fd` 71 | /// with [`sp1_zkvm::io::write`], returning a list of arbitrary data that may be read 72 | /// with successive calls to [`sp1_zkvm::io::read`]. 73 | pub fn hook( 74 | &mut self, 75 | fd: u32, 76 | f: impl FnMut(HookEnv, &[u8]) -> Vec> + Send + Sync + 'a, 77 | ) -> &mut Self { 78 | self.hook_registry_entries.push((fd, hookify(f))); 79 | self 80 | } 81 | 82 | /// Avoid registering the default hooks in the runtime. 83 | /// 84 | /// It is not necessary to call this to override hooks --- instead, simply 85 | /// register a hook with the same value of `fd` by calling [`Self::hook`]. 86 | pub fn without_default_hooks(&mut self) -> &mut Self { 87 | self.no_default_hooks = true; 88 | self 89 | } 90 | 91 | /// Set the maximum number of cpu cycles to use for execution. 92 | pub fn max_cycles(&mut self, max_cycles: u64) -> &mut Self { 93 | self.max_cycles = Some(max_cycles); 94 | self 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /crates/executor/src/disassembler/elf.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::min; 2 | 3 | use elf::{ 4 | abi::{EM_RISCV, ET_EXEC, PF_X, PT_LOAD}, 5 | endian::LittleEndian, 6 | file::Class, 7 | ElfBytes, 8 | }; 9 | use hashbrown::HashMap; 10 | use sp1_primitives::consts::{MAXIMUM_MEMORY_SIZE, WORD_SIZE}; 11 | 12 | /// RISC-V 32IM ELF (Executable and Linkable Format) File. 13 | /// 14 | /// This file represents a binary in the ELF format, specifically the RISC-V 32IM architecture 15 | /// with the following extensions: 16 | /// 17 | /// - Base Integer Instruction Set (I) 18 | /// - Integer Multiplication and Division (M) 19 | /// 20 | /// This format is commonly used in embedded systems and is supported by many compilers. 21 | #[derive(Debug, Clone)] 22 | pub(crate) struct Elf { 23 | /// The instructions of the program encoded as 32-bits. 24 | pub(crate) instructions: Vec, 25 | /// The start address of the program. 26 | pub(crate) pc_start: u32, 27 | /// The base address of the program. 28 | pub(crate) pc_base: u32, 29 | /// The initial memory image, useful for global constants. 30 | pub(crate) memory_image: HashMap, 31 | } 32 | 33 | impl Elf { 34 | /// Create a new [Elf]. 35 | #[must_use] 36 | pub(crate) const fn new( 37 | instructions: Vec, 38 | pc_start: u32, 39 | pc_base: u32, 40 | memory_image: HashMap, 41 | ) -> Self { 42 | Self { 43 | instructions, 44 | pc_start, 45 | pc_base, 46 | memory_image, 47 | } 48 | } 49 | 50 | /// Parse the ELF file into a vector of 32-bit encoded instructions and the first memory 51 | /// address. 52 | /// 53 | /// # Errors 54 | /// 55 | /// This function may return an error if the ELF is not valid. 56 | /// 57 | /// Reference: [Executable and Linkable Format](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) 58 | pub(crate) fn decode(input: &[u8]) -> eyre::Result { 59 | let mut image: HashMap = HashMap::new(); 60 | 61 | // Parse the ELF file assuming that it is little-endian.. 62 | let elf = ElfBytes::::minimal_parse(input)?; 63 | 64 | // Some sanity checks to make sure that the ELF file is valid. 65 | if elf.ehdr.class != Class::ELF32 { 66 | eyre::bail!("must be a 32-bit elf"); 67 | } else if elf.ehdr.e_machine != EM_RISCV { 68 | eyre::bail!("must be a riscv machine"); 69 | } else if elf.ehdr.e_type != ET_EXEC { 70 | eyre::bail!("must be executable"); 71 | } 72 | 73 | // Get the entrypoint of the ELF file as an u32. 74 | let entry: u32 = elf.ehdr.e_entry.try_into()?; 75 | 76 | // Make sure the entrypoint is valid. 77 | if entry == MAXIMUM_MEMORY_SIZE || entry % WORD_SIZE as u32 != 0 { 78 | eyre::bail!("invalid entrypoint"); 79 | } 80 | 81 | // Get the segments of the ELF file. 82 | let segments = elf 83 | .segments() 84 | .ok_or_else(|| eyre::eyre!("failed to get segments"))?; 85 | if segments.len() > 256 { 86 | eyre::bail!("too many program headers"); 87 | } 88 | 89 | let mut instructions: Vec = Vec::new(); 90 | let mut base_address = u32::MAX; 91 | 92 | // Only read segments that are executable instructions that are also PT_LOAD. 93 | for segment in segments.iter().filter(|x| x.p_type == PT_LOAD) { 94 | // Get the file size of the segment as an u32. 95 | let file_size: u32 = segment.p_filesz.try_into()?; 96 | if file_size == MAXIMUM_MEMORY_SIZE { 97 | eyre::bail!("invalid segment file_size"); 98 | } 99 | 100 | // Get the memory size of the segment as an u32. 101 | let mem_size: u32 = segment.p_memsz.try_into()?; 102 | if mem_size == MAXIMUM_MEMORY_SIZE { 103 | eyre::bail!("Invalid segment mem_size"); 104 | } 105 | 106 | // Get the virtual address of the segment as an u32. 107 | let vaddr: u32 = segment.p_vaddr.try_into()?; 108 | if vaddr % WORD_SIZE as u32 != 0 { 109 | eyre::bail!("vaddr {vaddr:08x} is unaligned"); 110 | } 111 | 112 | // If the virtual address is less than the first memory address, then update the first 113 | // memory address. 114 | if (segment.p_flags & PF_X) != 0 && base_address > vaddr { 115 | base_address = vaddr; 116 | } 117 | 118 | // Get the offset to the segment. 119 | let offset: u32 = segment.p_offset.try_into()?; 120 | 121 | // Read the segment and decode each word as an instruction. 122 | for i in (0..mem_size).step_by(WORD_SIZE) { 123 | let addr = vaddr 124 | .checked_add(i) 125 | .ok_or_else(|| eyre::eyre!("vaddr overflow"))?; 126 | if addr == MAXIMUM_MEMORY_SIZE { 127 | eyre::bail!( 128 | "address [0x{addr:08x}] exceeds maximum address for guest programs [0x{MAXIMUM_MEMORY_SIZE:08x}]" 129 | ); 130 | } 131 | 132 | // If we are reading past the end of the file, then break. 133 | if i >= file_size { 134 | image.insert(addr, 0); 135 | continue; 136 | } 137 | 138 | // Get the word as an u32 but make sure we don't read past the end of the file. 139 | let mut word = 0; 140 | let len = min(file_size - i, WORD_SIZE as u32); 141 | for j in 0..len { 142 | let offset = (offset + i + j) as usize; 143 | let byte = input 144 | .get(offset) 145 | .ok_or_else(|| eyre::eyre!("failed to read segment offset"))?; 146 | word |= u32::from(*byte) << (j * 8); 147 | } 148 | image.insert(addr, word); 149 | if (segment.p_flags & PF_X) != 0 { 150 | instructions.push(word); 151 | } 152 | } 153 | } 154 | 155 | Ok(Elf::new(instructions, entry, base_address, image)) 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /crates/executor/src/disassembler/mod.rs: -------------------------------------------------------------------------------- 1 | //! A disassembler for RISC-V ELFs. 2 | 3 | mod elf; 4 | mod rrs; 5 | 6 | pub(crate) use elf::*; 7 | pub(crate) use rrs::*; 8 | -------------------------------------------------------------------------------- /crates/executor/src/events/memory.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Memory Record. 4 | /// 5 | /// This object encapsulates the information needed to prove a memory access operation. This 6 | /// includes the shard, timestamp, and value of the memory address. 7 | #[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] 8 | pub struct MemoryRecord { 9 | /// The shard number. 10 | pub shard: u32, 11 | /// The timestamp. 12 | pub timestamp: u32, 13 | /// The value. 14 | pub value: u32, 15 | } 16 | 17 | /// Memory Access Position. 18 | /// 19 | /// This enum represents the position of a memory access in a register. For example, if a memory 20 | /// access is performed in the C register, it will have a position of C. 21 | /// 22 | /// Note: The register positions require that they be read and written in the following order: 23 | /// C, B, A. 24 | #[derive(Copy, Clone, Debug, PartialEq)] 25 | pub enum MemoryAccessPosition { 26 | /// Memory access position. 27 | Memory = 0, 28 | /// C register access position. 29 | C = 1, 30 | /// B register access position. 31 | B = 2, 32 | /// A register access position. 33 | A = 3, 34 | } 35 | 36 | /// Memory Read Record. 37 | /// 38 | /// This object encapsulates the information needed to prove a memory read operation. This 39 | /// includes the value, shard, timestamp, and previous shard and timestamp. 40 | #[allow(clippy::manual_non_exhaustive)] 41 | #[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] 42 | pub struct MemoryReadRecord { 43 | /// The value. 44 | pub value: u32, 45 | /// The shard number. 46 | pub shard: u32, 47 | /// The timestamp. 48 | pub timestamp: u32, 49 | /// The previous shard number. 50 | pub prev_shard: u32, 51 | /// The previous timestamp. 52 | pub prev_timestamp: u32, 53 | } 54 | 55 | /// Memory Write Record. 56 | /// 57 | /// This object encapsulates the information needed to prove a memory write operation. This 58 | /// includes the value, shard, timestamp, previous value, previous shard, and previous timestamp. 59 | #[allow(clippy::manual_non_exhaustive)] 60 | #[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] 61 | pub struct MemoryWriteRecord { 62 | /// The value. 63 | pub value: u32, 64 | /// The shard number. 65 | pub shard: u32, 66 | /// The timestamp. 67 | pub timestamp: u32, 68 | /// The previous value. 69 | pub prev_value: u32, 70 | /// The previous shard number. 71 | pub prev_shard: u32, 72 | /// The previous timestamp. 73 | pub prev_timestamp: u32, 74 | } 75 | 76 | /// Memory Record Enum. 77 | /// 78 | /// This enum represents the different types of memory records that can be stored in the memory 79 | /// event such as reads and writes. 80 | #[derive(Debug, Copy, Clone, Serialize, Deserialize)] 81 | pub enum MemoryRecordEnum { 82 | /// Read. 83 | Read(MemoryReadRecord), 84 | /// Write. 85 | Write(MemoryWriteRecord), 86 | } 87 | 88 | impl MemoryRecordEnum { 89 | /// Retrieve the current memory record. 90 | #[must_use] 91 | pub fn current_record(&self) -> MemoryRecord { 92 | match self { 93 | MemoryRecordEnum::Read(record) => MemoryRecord { 94 | shard: record.shard, 95 | timestamp: record.timestamp, 96 | value: record.value, 97 | }, 98 | MemoryRecordEnum::Write(record) => MemoryRecord { 99 | shard: record.shard, 100 | timestamp: record.timestamp, 101 | value: record.value, 102 | }, 103 | } 104 | } 105 | 106 | /// Retrieve the previous memory record. 107 | #[must_use] 108 | pub fn previous_record(&self) -> MemoryRecord { 109 | match self { 110 | MemoryRecordEnum::Read(record) => MemoryRecord { 111 | shard: record.prev_shard, 112 | timestamp: record.prev_timestamp, 113 | value: record.value, 114 | }, 115 | MemoryRecordEnum::Write(record) => MemoryRecord { 116 | shard: record.prev_shard, 117 | timestamp: record.prev_timestamp, 118 | value: record.prev_value, 119 | }, 120 | } 121 | } 122 | } 123 | 124 | /// Memory Initialize/Finalize Event. 125 | /// 126 | /// This object encapsulates the information needed to prove a memory initialize or finalize 127 | /// operation. This includes the address, value, shard, timestamp, and whether the memory is 128 | /// initialized or finalized. 129 | #[derive(Debug, Clone, Serialize, Deserialize)] 130 | pub struct MemoryInitializeFinalizeEvent { 131 | /// The address. 132 | pub addr: u32, 133 | /// The value. 134 | pub value: u32, 135 | /// The shard number. 136 | pub shard: u32, 137 | /// The timestamp. 138 | pub timestamp: u32, 139 | /// The used flag. 140 | pub used: u32, 141 | } 142 | 143 | impl MemoryReadRecord { 144 | /// Creates a new [``MemoryReadRecord``]. 145 | #[must_use] 146 | pub const fn new( 147 | value: u32, 148 | shard: u32, 149 | timestamp: u32, 150 | prev_shard: u32, 151 | prev_timestamp: u32, 152 | ) -> Self { 153 | assert!(shard > prev_shard || ((shard == prev_shard) && (timestamp > prev_timestamp))); 154 | Self { 155 | value, 156 | shard, 157 | timestamp, 158 | prev_shard, 159 | prev_timestamp, 160 | } 161 | } 162 | } 163 | 164 | impl MemoryWriteRecord { 165 | /// Creates a new [``MemoryWriteRecord``]. 166 | #[must_use] 167 | pub const fn new( 168 | value: u32, 169 | shard: u32, 170 | timestamp: u32, 171 | prev_value: u32, 172 | prev_shard: u32, 173 | prev_timestamp: u32, 174 | ) -> Self { 175 | assert!(shard > prev_shard || ((shard == prev_shard) && (timestamp > prev_timestamp)),); 176 | Self { 177 | value, 178 | shard, 179 | timestamp, 180 | prev_value, 181 | prev_shard, 182 | prev_timestamp, 183 | } 184 | } 185 | } 186 | 187 | impl MemoryRecordEnum { 188 | /// Returns the value of the memory record. 189 | #[must_use] 190 | pub const fn value(&self) -> u32 { 191 | match self { 192 | MemoryRecordEnum::Read(record) => record.value, 193 | MemoryRecordEnum::Write(record) => record.value, 194 | } 195 | } 196 | } 197 | 198 | impl MemoryInitializeFinalizeEvent { 199 | /// Creates a new [``MemoryInitializeFinalizeEvent``] for an initialization. 200 | #[must_use] 201 | pub const fn initialize(addr: u32, value: u32, used: bool) -> Self { 202 | Self { 203 | addr, 204 | value, 205 | shard: 1, 206 | timestamp: 1, 207 | used: if used { 1 } else { 0 }, 208 | } 209 | } 210 | 211 | /// Creates a new [``MemoryInitializeFinalizeEvent``] for a finalization. 212 | #[must_use] 213 | pub const fn finalize_from_record(addr: u32, record: &MemoryRecord) -> Self { 214 | Self { 215 | addr, 216 | value: record.value, 217 | shard: record.shard, 218 | timestamp: record.timestamp, 219 | used: 1, 220 | } 221 | } 222 | } 223 | 224 | impl From for MemoryRecordEnum { 225 | fn from(read_record: MemoryReadRecord) -> Self { 226 | MemoryRecordEnum::Read(read_record) 227 | } 228 | } 229 | 230 | impl From for MemoryRecordEnum { 231 | fn from(write_record: MemoryWriteRecord) -> Self { 232 | MemoryRecordEnum::Write(write_record) 233 | } 234 | } 235 | 236 | /// Memory Local Event. 237 | /// 238 | /// This object encapsulates the information needed to prove a memory access operation within a 239 | /// shard. This includes the address, initial memory access, and final memory access within a 240 | /// shard. 241 | #[derive(Debug, Clone, Serialize, Deserialize)] 242 | pub struct MemoryLocalEvent { 243 | /// The address. 244 | pub addr: u32, 245 | /// The initial memory access. 246 | pub initial_mem_access: MemoryRecord, 247 | /// The final memory access. 248 | pub final_mem_access: MemoryRecord, 249 | } 250 | -------------------------------------------------------------------------------- /crates/executor/src/events/mod.rs: -------------------------------------------------------------------------------- 1 | //! Type definitions for the events emitted by the [`crate::Executor`] during execution. 2 | 3 | mod memory; 4 | mod precompiles; 5 | mod syscall; 6 | mod utils; 7 | 8 | pub use memory::*; 9 | pub use precompiles::*; 10 | pub use syscall::*; 11 | pub use utils::*; 12 | -------------------------------------------------------------------------------- /crates/executor/src/events/precompiles/ec.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use sp1_curves::{ 4 | params::{NumLimbs, NumWords}, 5 | weierstrass::{bls12_381::bls12381_decompress, secp256k1::secp256k1_decompress}, 6 | AffinePoint, CurveType, EllipticCurve, 7 | }; 8 | use sp1_primitives::consts::{bytes_to_words_le_vec, words_to_bytes_le_vec}; 9 | use typenum::Unsigned; 10 | 11 | use crate::{ 12 | events::{ 13 | memory::{MemoryReadRecord, MemoryWriteRecord}, 14 | LookupId, MemoryLocalEvent, 15 | }, 16 | syscalls::SyscallContext, 17 | }; 18 | 19 | /// Elliptic Curve Add Event. 20 | /// 21 | /// This event is emitted when an elliptic curve addition operation is performed. 22 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 23 | pub struct EllipticCurveAddEvent { 24 | pub(crate) lookup_id: LookupId, 25 | /// The shard number. 26 | pub shard: u32, 27 | /// The clock cycle. 28 | pub clk: u32, 29 | /// The pointer to the first point. 30 | pub p_ptr: u32, 31 | /// The first point as a list of words. 32 | pub p: Vec, 33 | /// The pointer to the second point. 34 | pub q_ptr: u32, 35 | /// The second point as a list of words. 36 | pub q: Vec, 37 | /// The memory records for the first point. 38 | pub p_memory_records: Vec, 39 | /// The memory records for the second point. 40 | pub q_memory_records: Vec, 41 | /// The local memory access records. 42 | pub local_mem_access: Vec, 43 | } 44 | 45 | /// Elliptic Curve Double Event. 46 | /// 47 | /// This event is emitted when an elliptic curve doubling operation is performed. 48 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 49 | pub struct EllipticCurveDoubleEvent { 50 | /// The lookup identifier. 51 | pub lookup_id: LookupId, 52 | /// The shard number. 53 | pub shard: u32, 54 | /// The clock cycle. 55 | pub clk: u32, 56 | /// The pointer to the point. 57 | pub p_ptr: u32, 58 | /// The point as a list of words. 59 | pub p: Vec, 60 | /// The memory records for the point. 61 | pub p_memory_records: Vec, 62 | /// The local memory access records. 63 | pub local_mem_access: Vec, 64 | } 65 | 66 | /// Elliptic Curve Point Decompress Event. 67 | /// 68 | /// This event is emitted when an elliptic curve point decompression operation is performed. 69 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 70 | pub struct EllipticCurveDecompressEvent { 71 | /// The lookup identifier. 72 | pub lookup_id: LookupId, 73 | /// The shard number. 74 | pub shard: u32, 75 | /// The clock cycle. 76 | pub clk: u32, 77 | /// The pointer to the point. 78 | pub ptr: u32, 79 | /// The sign bit of the point. 80 | pub sign_bit: bool, 81 | /// The x coordinate as a list of bytes. 82 | pub x_bytes: Vec, 83 | /// The decompressed y coordinate as a list of bytes. 84 | pub decompressed_y_bytes: Vec, 85 | /// The memory records for the x coordinate. 86 | pub x_memory_records: Vec, 87 | /// The memory records for the y coordinate. 88 | pub y_memory_records: Vec, 89 | /// The local memory access records. 90 | pub local_mem_access: Vec, 91 | } 92 | 93 | /// Create an elliptic curve add event. It takes two pointers to memory locations, reads the points 94 | /// from memory, adds them together, and writes the result back to the first memory location. 95 | /// The generic parameter `N` is the number of u32 words in the point representation. For example, 96 | /// for the secp256k1 curve, `N` would be 16 (64 bytes) because the x and y coordinates are 32 bytes 97 | /// each. 98 | pub fn create_ec_add_event( 99 | rt: &mut SyscallContext, 100 | arg1: u32, 101 | arg2: u32, 102 | ) -> EllipticCurveAddEvent { 103 | let start_clk = rt.clk; 104 | let p_ptr = arg1; 105 | if p_ptr % 4 != 0 { 106 | panic!(); 107 | } 108 | let q_ptr = arg2; 109 | if q_ptr % 4 != 0 { 110 | panic!(); 111 | } 112 | 113 | let num_words = ::WordsCurvePoint::USIZE; 114 | 115 | let p = rt.slice_unsafe(p_ptr, num_words); 116 | 117 | let (q_memory_records, q) = rt.mr_slice(q_ptr, num_words); 118 | 119 | // When we write to p, we want the clk to be incremented because p and q could be the same. 120 | rt.clk += 1; 121 | 122 | let p_affine = AffinePoint::::from_words_le(&p); 123 | let q_affine = AffinePoint::::from_words_le(&q); 124 | let result_affine = p_affine + q_affine; 125 | 126 | let result_words = result_affine.to_words_le(); 127 | 128 | let p_memory_records = rt.mw_slice(p_ptr, &result_words); 129 | 130 | EllipticCurveAddEvent { 131 | lookup_id: rt.syscall_lookup_id, 132 | shard: rt.current_shard(), 133 | clk: start_clk, 134 | p_ptr, 135 | p, 136 | q_ptr, 137 | q, 138 | p_memory_records, 139 | q_memory_records, 140 | local_mem_access: rt.postprocess(), 141 | } 142 | } 143 | 144 | /// Create an elliptic curve double event. 145 | /// 146 | /// It takes a pointer to a memory location, reads the point from memory, doubles it, and writes the 147 | /// result back to the memory location. 148 | pub fn create_ec_double_event( 149 | rt: &mut SyscallContext, 150 | arg1: u32, 151 | _: u32, 152 | ) -> EllipticCurveDoubleEvent { 153 | let start_clk = rt.clk; 154 | let p_ptr = arg1; 155 | if p_ptr % 4 != 0 { 156 | panic!(); 157 | } 158 | 159 | let num_words = ::WordsCurvePoint::USIZE; 160 | 161 | let p = rt.slice_unsafe(p_ptr, num_words); 162 | 163 | let p_affine = AffinePoint::::from_words_le(&p); 164 | 165 | let result_affine = E::ec_double(&p_affine); 166 | 167 | let result_words = result_affine.to_words_le(); 168 | 169 | let p_memory_records = rt.mw_slice(p_ptr, &result_words); 170 | 171 | EllipticCurveDoubleEvent { 172 | lookup_id: rt.syscall_lookup_id, 173 | shard: rt.current_shard(), 174 | clk: start_clk, 175 | p_ptr, 176 | p, 177 | p_memory_records, 178 | local_mem_access: rt.postprocess(), 179 | } 180 | } 181 | 182 | /// Create an elliptic curve decompress event. 183 | /// 184 | /// It takes a pointer to a memory location, reads the point from memory, decompresses it, and 185 | /// writes the result back to the memory location. 186 | pub fn create_ec_decompress_event( 187 | rt: &mut SyscallContext, 188 | slice_ptr: u32, 189 | sign_bit: u32, 190 | ) -> EllipticCurveDecompressEvent { 191 | let start_clk = rt.clk; 192 | assert!(slice_ptr % 4 == 0, "slice_ptr must be 4-byte aligned"); 193 | assert!(sign_bit <= 1, "is_odd must be 0 or 1"); 194 | 195 | let num_limbs = ::Limbs::USIZE; 196 | let num_words_field_element = num_limbs / 4; 197 | 198 | let (x_memory_records, x_vec) = 199 | rt.mr_slice(slice_ptr + (num_limbs as u32), num_words_field_element); 200 | 201 | let x_bytes = words_to_bytes_le_vec(&x_vec); 202 | let mut x_bytes_be = x_bytes.clone(); 203 | x_bytes_be.reverse(); 204 | 205 | let decompress_fn = match E::CURVE_TYPE { 206 | CurveType::Secp256k1 => secp256k1_decompress::, 207 | CurveType::Bls12381 => bls12381_decompress::, 208 | _ => panic!("Unsupported curve"), 209 | }; 210 | 211 | let computed_point: AffinePoint = decompress_fn(&x_bytes_be, sign_bit); 212 | 213 | let mut decompressed_y_bytes = computed_point.y.to_bytes_le(); 214 | decompressed_y_bytes.resize(num_limbs, 0u8); 215 | let y_words = bytes_to_words_le_vec(&decompressed_y_bytes); 216 | 217 | let y_memory_records = rt.mw_slice(slice_ptr, &y_words); 218 | 219 | EllipticCurveDecompressEvent { 220 | lookup_id: rt.syscall_lookup_id, 221 | shard: rt.current_shard(), 222 | clk: start_clk, 223 | ptr: slice_ptr, 224 | sign_bit: sign_bit != 0, 225 | x_bytes: x_bytes.clone(), 226 | decompressed_y_bytes, 227 | x_memory_records, 228 | y_memory_records, 229 | local_mem_access: rt.postprocess(), 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /crates/executor/src/events/precompiles/edwards.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use sp1_curves::{edwards::WORDS_FIELD_ELEMENT, COMPRESSED_POINT_BYTES, NUM_BYTES_FIELD_ELEMENT}; 3 | 4 | use crate::events::{ 5 | memory::{MemoryReadRecord, MemoryWriteRecord}, 6 | LookupId, MemoryLocalEvent, 7 | }; 8 | 9 | /// Edwards Decompress Event. 10 | /// 11 | /// This event is emitted when an edwards decompression operation is performed. 12 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 13 | pub struct EdDecompressEvent { 14 | /// The lookup identifier. 15 | pub lookup_id: LookupId, 16 | /// The shard number. 17 | pub shard: u32, 18 | /// The clock cycle. 19 | pub clk: u32, 20 | /// The pointer to the point. 21 | pub ptr: u32, 22 | /// The sign bit of the point. 23 | pub sign: bool, 24 | /// The comprssed y coordinate as a list of bytes. 25 | pub y_bytes: [u8; COMPRESSED_POINT_BYTES], 26 | /// The decompressed x coordinate as a list of bytes. 27 | pub decompressed_x_bytes: [u8; NUM_BYTES_FIELD_ELEMENT], 28 | /// The memory records for the x coordinate. 29 | pub x_memory_records: [MemoryWriteRecord; WORDS_FIELD_ELEMENT], 30 | /// The memory records for the y coordinate. 31 | pub y_memory_records: [MemoryReadRecord; WORDS_FIELD_ELEMENT], 32 | /// The local memory access events. 33 | pub local_mem_access: Vec, 34 | } 35 | -------------------------------------------------------------------------------- /crates/executor/src/events/precompiles/fptower.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::events::{LookupId, MemoryLocalEvent, MemoryReadRecord, MemoryWriteRecord}; 4 | 5 | /// This is an arithmetic operation for emulating modular arithmetic. 6 | #[derive(Default, PartialEq, Copy, Clone, Debug, Serialize, Deserialize)] 7 | pub enum FieldOperation { 8 | /// Addition. 9 | #[default] 10 | Add, 11 | /// Multiplication. 12 | Mul, 13 | /// Subtraction. 14 | Sub, 15 | /// Division. 16 | Div, 17 | } 18 | 19 | /// Emulated Field Operation Events. 20 | /// 21 | /// This event is emitted when an emulated field operation is performed on the input operands. 22 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 23 | pub struct FpOpEvent { 24 | /// The lookup id. 25 | pub lookup_id: LookupId, 26 | /// The shard number. 27 | pub shard: u32, 28 | /// The clock cycle. 29 | pub clk: u32, 30 | /// The pointer to the x operand. 31 | pub x_ptr: u32, 32 | /// The x operand. 33 | pub x: Vec, 34 | /// The pointer to the y operand. 35 | pub y_ptr: u32, 36 | /// The y operand. 37 | pub y: Vec, 38 | /// The operation to perform. 39 | pub op: FieldOperation, 40 | /// The memory records for the x operand. 41 | pub x_memory_records: Vec, 42 | /// The memory records for the y operand. 43 | pub y_memory_records: Vec, 44 | /// The local memory access records. 45 | pub local_mem_access: Vec, 46 | } 47 | 48 | /// Emulated Degree 2 Field Addition/Subtraction Events. 49 | /// 50 | /// This event is emitted when an emulated degree 2 field operation is performed on the input 51 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 52 | pub struct Fp2AddSubEvent { 53 | /// The lookup id. 54 | pub lookup_id: LookupId, 55 | /// The shard number. 56 | pub shard: u32, 57 | /// The clock cycle. 58 | pub clk: u32, 59 | /// The operation to perform. 60 | pub op: FieldOperation, 61 | /// The pointer to the x operand. 62 | pub x_ptr: u32, 63 | /// The x operand. 64 | pub x: Vec, 65 | /// The pointer to the y operand. 66 | pub y_ptr: u32, 67 | /// The y operand. 68 | pub y: Vec, 69 | /// The memory records for the x operand. 70 | pub x_memory_records: Vec, 71 | /// The memory records for the y operand. 72 | pub y_memory_records: Vec, 73 | /// The local memory access records. 74 | pub local_mem_access: Vec, 75 | } 76 | 77 | /// Emulated Degree 2 Field Multiplication Events. 78 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 79 | pub struct Fp2MulEvent { 80 | /// The lookup id. 81 | pub lookup_id: LookupId, 82 | /// The shard number. 83 | pub shard: u32, 84 | /// The clock cycle. 85 | pub clk: u32, 86 | /// The pointer to the x operand. 87 | pub x_ptr: u32, 88 | /// The x operand. 89 | pub x: Vec, 90 | /// The pointer to the y operand. 91 | pub y_ptr: u32, 92 | /// The y operand. 93 | pub y: Vec, 94 | /// The memory records for the x operand. 95 | pub x_memory_records: Vec, 96 | /// The memory records for the y operand. 97 | pub y_memory_records: Vec, 98 | /// The local memory access records. 99 | pub local_mem_access: Vec, 100 | } 101 | -------------------------------------------------------------------------------- /crates/executor/src/events/precompiles/keccak256_permute.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::events::{ 4 | memory::{MemoryReadRecord, MemoryWriteRecord}, 5 | LookupId, MemoryLocalEvent, 6 | }; 7 | 8 | pub(crate) const STATE_SIZE: usize = 25; 9 | 10 | /// Keccak-256 Permutation Event. 11 | /// 12 | /// This event is emitted when a keccak-256 permutation operation is performed. 13 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 14 | pub struct KeccakPermuteEvent { 15 | /// The lookup identifier. 16 | pub lookup_id: LookupId, 17 | /// The shard number. 18 | pub shard: u32, 19 | /// The clock cycle. 20 | pub clk: u32, 21 | /// The pre-state as a list of u64 words. 22 | pub pre_state: [u64; STATE_SIZE], 23 | /// The post-state as a list of u64 words. 24 | pub post_state: [u64; STATE_SIZE], 25 | /// The memory records for the pre-state. 26 | pub state_read_records: Vec, 27 | /// The memory records for the post-state. 28 | pub state_write_records: Vec, 29 | /// The address of the state. 30 | pub state_addr: u32, 31 | /// The local memory access records. 32 | pub local_mem_access: Vec, 33 | } 34 | -------------------------------------------------------------------------------- /crates/executor/src/events/precompiles/mod.rs: -------------------------------------------------------------------------------- 1 | mod ec; 2 | mod edwards; 3 | mod fptower; 4 | mod keccak256_permute; 5 | mod sha256_compress; 6 | mod sha256_extend; 7 | mod uint256; 8 | 9 | pub use ec::*; 10 | pub use edwards::*; 11 | pub use fptower::*; 12 | use hashbrown::HashMap; 13 | pub use keccak256_permute::*; 14 | use serde::{Deserialize, Serialize}; 15 | pub use sha256_compress::*; 16 | pub use sha256_extend::*; 17 | use strum::{EnumIter, IntoEnumIterator}; 18 | pub use uint256::*; 19 | 20 | use crate::syscalls::SyscallCode; 21 | 22 | use super::{MemoryLocalEvent, SyscallEvent}; 23 | 24 | #[derive(Clone, Debug, Serialize, Deserialize, EnumIter)] 25 | /// Precompile event. There should be one variant for every precompile syscall. 26 | pub enum PrecompileEvent { 27 | /// Sha256 extend precompile event. 28 | ShaExtend(ShaExtendEvent), 29 | /// Sha256 compress precompile event. 30 | ShaCompress(ShaCompressEvent), 31 | /// Keccak256 permute precompile event. 32 | KeccakPermute(KeccakPermuteEvent), 33 | /// Edwards curve add precompile event. 34 | EdAdd(EllipticCurveAddEvent), 35 | /// Edwards curve decompress precompile event. 36 | EdDecompress(EdDecompressEvent), 37 | /// Secp256k1 curve add precompile event. 38 | Secp256k1Add(EllipticCurveAddEvent), 39 | /// Secp256k1 curve double precompile event. 40 | Secp256k1Double(EllipticCurveDoubleEvent), 41 | /// Secp256k1 curve decompress precompile event. 42 | Secp256k1Decompress(EllipticCurveDecompressEvent), 43 | /// K256 curve decompress precompile event. 44 | K256Decompress(EllipticCurveDecompressEvent), 45 | /// Bn254 curve add precompile event. 46 | Bn254Add(EllipticCurveAddEvent), 47 | /// Bn254 curve double precompile event. 48 | Bn254Double(EllipticCurveDoubleEvent), 49 | /// Bn254 base field operation precompile event. 50 | Bn254Fp(FpOpEvent), 51 | /// Bn254 quadratic field add/sub precompile event. 52 | Bn254Fp2AddSub(Fp2AddSubEvent), 53 | /// Bn254 quadratic field mul precompile event. 54 | Bn254Fp2Mul(Fp2MulEvent), 55 | /// Bls12-381 curve add precompile event. 56 | Bls12381Add(EllipticCurveAddEvent), 57 | /// Bls12-381 curve double precompile event. 58 | Bls12381Double(EllipticCurveDoubleEvent), 59 | /// Bls12-381 curve decompress precompile event. 60 | Bls12381Decompress(EllipticCurveDecompressEvent), 61 | /// Bls12-381 base field operation precompile event. 62 | Bls12381Fp(FpOpEvent), 63 | /// Bls12-381 quadratic field add/sub precompile event. 64 | Bls12381Fp2AddSub(Fp2AddSubEvent), 65 | /// Bls12-381 quadratic field mul precompile event. 66 | Bls12381Fp2Mul(Fp2MulEvent), 67 | /// Uint256 mul precompile event. 68 | Uint256Mul(Uint256MulEvent), 69 | } 70 | 71 | /// Trait to retrieve all the local memory events from a vec of precompile events. 72 | pub trait PrecompileLocalMemory { 73 | /// Get an iterator of all the local memory events. 74 | fn get_local_mem_events(&self) -> impl IntoIterator; 75 | } 76 | 77 | impl PrecompileLocalMemory for Vec<(SyscallEvent, PrecompileEvent)> { 78 | fn get_local_mem_events(&self) -> impl IntoIterator { 79 | let mut iterators = Vec::new(); 80 | 81 | for (_, event) in self.iter() { 82 | match event { 83 | PrecompileEvent::ShaExtend(e) => { 84 | iterators.push(e.local_mem_access.iter()); 85 | } 86 | PrecompileEvent::ShaCompress(e) => { 87 | iterators.push(e.local_mem_access.iter()); 88 | } 89 | PrecompileEvent::KeccakPermute(e) => { 90 | iterators.push(e.local_mem_access.iter()); 91 | } 92 | PrecompileEvent::EdDecompress(e) => { 93 | iterators.push(e.local_mem_access.iter()); 94 | } 95 | PrecompileEvent::Secp256k1Add(e) 96 | | PrecompileEvent::EdAdd(e) 97 | | PrecompileEvent::Bn254Add(e) 98 | | PrecompileEvent::Bls12381Add(e) => { 99 | iterators.push(e.local_mem_access.iter()); 100 | } 101 | PrecompileEvent::Secp256k1Double(e) 102 | | PrecompileEvent::Bn254Double(e) 103 | | PrecompileEvent::Bls12381Double(e) => { 104 | iterators.push(e.local_mem_access.iter()); 105 | } 106 | PrecompileEvent::Secp256k1Decompress(e) 107 | | PrecompileEvent::K256Decompress(e) 108 | | PrecompileEvent::Bls12381Decompress(e) => { 109 | iterators.push(e.local_mem_access.iter()); 110 | } 111 | PrecompileEvent::Uint256Mul(e) => { 112 | iterators.push(e.local_mem_access.iter()); 113 | } 114 | PrecompileEvent::Bls12381Fp(e) | PrecompileEvent::Bn254Fp(e) => { 115 | iterators.push(e.local_mem_access.iter()); 116 | } 117 | PrecompileEvent::Bls12381Fp2AddSub(e) | PrecompileEvent::Bn254Fp2AddSub(e) => { 118 | iterators.push(e.local_mem_access.iter()); 119 | } 120 | PrecompileEvent::Bls12381Fp2Mul(e) | PrecompileEvent::Bn254Fp2Mul(e) => { 121 | iterators.push(e.local_mem_access.iter()); 122 | } 123 | } 124 | } 125 | 126 | iterators.into_iter().flatten() 127 | } 128 | } 129 | 130 | /// A record of all the precompile events. 131 | #[derive(Clone, Debug, Serialize, Deserialize)] 132 | pub struct PrecompileEvents { 133 | events: HashMap>, 134 | } 135 | 136 | impl Default for PrecompileEvents { 137 | fn default() -> Self { 138 | let mut events = HashMap::new(); 139 | for syscall_code in SyscallCode::iter() { 140 | if syscall_code.should_send() == 1 { 141 | events.insert(syscall_code, Vec::new()); 142 | } 143 | } 144 | 145 | Self { events } 146 | } 147 | } 148 | 149 | impl PrecompileEvents { 150 | /// Checks if the precompile events are empty. 151 | #[inline] 152 | #[must_use] 153 | pub fn is_empty(&self) -> bool { 154 | self.events.is_empty() 155 | } 156 | 157 | /// Get all the precompile events. 158 | pub fn all_events(&self) -> impl Iterator { 159 | self.events.values().flatten() 160 | } 161 | 162 | /// Get all the precompile events for a given syscall code. 163 | #[inline] 164 | #[must_use] 165 | pub fn get_events( 166 | &self, 167 | syscall_code: SyscallCode, 168 | ) -> Option<&Vec<(SyscallEvent, PrecompileEvent)>> { 169 | assert!(syscall_code.should_send() == 1); 170 | self.events.get(&syscall_code) 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /crates/executor/src/events/precompiles/sha256_compress.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::events::{ 4 | memory::{MemoryReadRecord, MemoryWriteRecord}, 5 | LookupId, MemoryLocalEvent, 6 | }; 7 | 8 | /// SHA-256 Compress Event. 9 | /// 10 | /// This event is emitted when a SHA-256 compress operation is performed. 11 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 12 | pub struct ShaCompressEvent { 13 | /// The lookup identifier. 14 | pub lookup_id: LookupId, 15 | /// The shard number. 16 | pub shard: u32, 17 | /// The clock cycle. 18 | pub clk: u32, 19 | /// The pointer to the word. 20 | pub w_ptr: u32, 21 | /// The word as a list of words. 22 | pub h_ptr: u32, 23 | /// The word as a list of words. 24 | pub w: Vec, 25 | /// The word as a list of words. 26 | pub h: [u32; 8], 27 | /// The memory records for the word. 28 | pub h_read_records: [MemoryReadRecord; 8], 29 | /// The memory records for the word. 30 | pub w_i_read_records: Vec, 31 | /// The memory records for the word. 32 | pub h_write_records: [MemoryWriteRecord; 8], 33 | /// The local memory accesses. 34 | pub local_mem_access: Vec, 35 | } 36 | -------------------------------------------------------------------------------- /crates/executor/src/events/precompiles/sha256_extend.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::events::{ 4 | memory::{MemoryReadRecord, MemoryWriteRecord}, 5 | LookupId, MemoryLocalEvent, 6 | }; 7 | 8 | /// SHA-256 Extend Event. 9 | /// 10 | /// This event is emitted when a SHA-256 extend operation is performed. 11 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 12 | pub struct ShaExtendEvent { 13 | /// The lookup identifier. 14 | pub lookup_id: LookupId, 15 | /// The shard number. 16 | pub shard: u32, 17 | /// The clock cycle. 18 | pub clk: u32, 19 | /// The pointer to the word. 20 | pub w_ptr: u32, 21 | /// The memory reads of w[i-15]. 22 | pub w_i_minus_15_reads: Vec, 23 | /// The memory reads of w[i-2]. 24 | pub w_i_minus_2_reads: Vec, 25 | /// The memory reads of w[i-16]. 26 | pub w_i_minus_16_reads: Vec, 27 | /// The memory reads of w[i-7]. 28 | pub w_i_minus_7_reads: Vec, 29 | /// The memory writes of w[i]. 30 | pub w_i_writes: Vec, 31 | /// The local memory accesses. 32 | pub local_mem_access: Vec, 33 | } 34 | -------------------------------------------------------------------------------- /crates/executor/src/events/precompiles/uint256.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::events::{ 4 | memory::{MemoryReadRecord, MemoryWriteRecord}, 5 | LookupId, MemoryLocalEvent, 6 | }; 7 | 8 | /// Uint256 Mul Event. 9 | /// 10 | /// This event is emitted when a uint256 mul operation is performed. 11 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 12 | pub struct Uint256MulEvent { 13 | /// The lookup identifier. 14 | pub lookup_id: LookupId, 15 | /// The shard number. 16 | pub shard: u32, 17 | /// The clock cycle. 18 | pub clk: u32, 19 | /// The pointer to the x value. 20 | pub x_ptr: u32, 21 | /// The x value as a list of words. 22 | pub x: Vec, 23 | /// The pointer to the y value. 24 | pub y_ptr: u32, 25 | /// The y value as a list of words. 26 | pub y: Vec, 27 | /// The modulus as a list of words. 28 | pub modulus: Vec, 29 | /// The memory records for the x value. 30 | pub x_memory_records: Vec, 31 | /// The memory records for the y value. 32 | pub y_memory_records: Vec, 33 | /// The memory records for the modulus. 34 | pub modulus_memory_records: Vec, 35 | /// The local memory access records. 36 | pub local_mem_access: Vec, 37 | } 38 | -------------------------------------------------------------------------------- /crates/executor/src/events/syscall.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::LookupId; 4 | 5 | /// Syscall Event. 6 | /// 7 | /// This object encapsulated the information needed to prove a syscall invocation from the CPU table. 8 | /// This includes its shard, clk, syscall id, arguments, other relevant information. 9 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 10 | pub struct SyscallEvent { 11 | /// The shard number. 12 | pub shard: u32, 13 | /// The clock cycle. 14 | pub clk: u32, 15 | /// The lookup id. 16 | pub lookup_id: LookupId, 17 | /// The syscall id. 18 | pub syscall_id: u32, 19 | /// The first argument. 20 | pub arg1: u32, 21 | /// The second operand. 22 | pub arg2: u32, 23 | /// The nonce for the syscall. 24 | pub nonce: u32, 25 | } 26 | -------------------------------------------------------------------------------- /crates/executor/src/events/utils.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use serde::Serialize; 3 | use std::{ 4 | fmt::Display, 5 | iter::{Map, Peekable}, 6 | }; 7 | 8 | use rand::{thread_rng, Rng}; 9 | 10 | /// A unique identifier for lookups. 11 | /// 12 | /// We use 4 u32s instead of a u128 to make it compatible with C. 13 | #[derive(Deserialize, Serialize, Debug, Clone, Copy, Default, Eq, Hash, PartialEq)] 14 | 15 | pub struct LookupId { 16 | /// First part of the id. 17 | pub a: u32, 18 | /// Second part of the id. 19 | pub b: u32, 20 | /// Third part of the id. 21 | pub c: u32, 22 | /// Fourth part of the id. 23 | pub d: u32, 24 | } 25 | 26 | /// Creates a new ALU lookup id with ``LookupId`` 27 | #[must_use] 28 | pub fn create_alu_lookup_id() -> LookupId { 29 | let mut rng = thread_rng(); 30 | LookupId { 31 | a: rng.gen(), 32 | b: rng.gen(), 33 | c: rng.gen(), 34 | d: rng.gen(), 35 | } 36 | } 37 | 38 | /// Creates a new ALU lookup id with ``LookupId`` 39 | #[must_use] 40 | pub fn create_alu_lookups() -> [LookupId; 6] { 41 | let mut rng = thread_rng(); 42 | [ 43 | LookupId { 44 | a: rng.gen(), 45 | b: rng.gen(), 46 | c: rng.gen(), 47 | d: rng.gen(), 48 | }, 49 | LookupId { 50 | a: rng.gen(), 51 | b: rng.gen(), 52 | c: rng.gen(), 53 | d: rng.gen(), 54 | }, 55 | LookupId { 56 | a: rng.gen(), 57 | b: rng.gen(), 58 | c: rng.gen(), 59 | d: rng.gen(), 60 | }, 61 | LookupId { 62 | a: rng.gen(), 63 | b: rng.gen(), 64 | c: rng.gen(), 65 | d: rng.gen(), 66 | }, 67 | LookupId { 68 | a: rng.gen(), 69 | b: rng.gen(), 70 | c: rng.gen(), 71 | d: rng.gen(), 72 | }, 73 | LookupId { 74 | a: rng.gen(), 75 | b: rng.gen(), 76 | c: rng.gen(), 77 | d: rng.gen(), 78 | }, 79 | ] 80 | } 81 | 82 | /// Returns sorted and formatted rows of a table of counts (e.g. `opcode_counts`). 83 | /// 84 | /// The table is sorted first by count (descending) and then by label (ascending). 85 | /// The first column consists of the counts, is right-justified, and is padded precisely 86 | /// enough to fit all the numbers. The second column consists of the labels (e.g. `OpCode`s). 87 | /// The columns are separated by a single space character. 88 | #[allow(clippy::type_complexity)] 89 | pub fn sorted_table_lines<'a, K, V>( 90 | table: impl IntoIterator + 'a, 91 | ) -> Map< 92 | Peekable, impl FnMut((K, V)) -> (String, String)>>, 93 | impl FnMut((String, String)) -> String, 94 | > 95 | where 96 | K: Ord + Display + 'a, 97 | V: Ord + Display + 'a, 98 | { 99 | let mut entries = table.into_iter().collect::>(); 100 | // Sort table by count (descending), then the name order (ascending). 101 | entries.sort_unstable_by(|a, b| a.1.cmp(&b.1).reverse().then_with(|| a.0.cmp(&b.0))); 102 | // Convert counts to `String`s to prepare them for printing and to measure their width. 103 | let mut table_with_string_counts = entries 104 | .into_iter() 105 | .map(|(label, ct)| (label.to_string().to_lowercase(), ct.to_string())) 106 | .peekable(); 107 | // Calculate width for padding the counts. 108 | let width = table_with_string_counts 109 | .peek() 110 | .map(|(_, b)| b.len()) 111 | .unwrap_or_default(); 112 | table_with_string_counts.map(move |(label, count)| format!("{count:>width$} {label}")) 113 | } 114 | -------------------------------------------------------------------------------- /crates/executor/src/hook.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Debug; 2 | 3 | use std::sync::{Arc, RwLock, RwLockWriteGuard}; 4 | 5 | use hashbrown::HashMap; 6 | use sp1_curves::k256::{Invert, RecoveryId, Signature, VerifyingKey}; 7 | 8 | use crate::Executor; 9 | 10 | /// A runtime hook, wrapped in a smart pointer. 11 | pub type BoxedHook<'a> = Arc>; 12 | 13 | /// The file descriptor through which to access `hook_ecrecover`. 14 | pub const FD_ECRECOVER_HOOK: u32 = 5; 15 | 16 | /// A runtime hook. May be called during execution by writing to a specified file descriptor, 17 | /// accepting and returning arbitrary data. 18 | pub trait Hook { 19 | /// Invoke the runtime hook with a standard environment and arbitrary data. 20 | /// Returns the computed data. 21 | fn invoke_hook(&mut self, env: HookEnv, buf: &[u8]) -> Vec>; 22 | } 23 | 24 | impl Vec>> Hook for F { 25 | /// Invokes the function `self` as a hook. 26 | fn invoke_hook(&mut self, env: HookEnv, buf: &[u8]) -> Vec> { 27 | self(env, buf) 28 | } 29 | } 30 | 31 | /// Wrap a function in a smart pointer so it may be placed in a `HookRegistry`. 32 | /// 33 | /// Note: the Send + Sync requirement may be logically extraneous. Requires further investigation. 34 | pub fn hookify<'a>( 35 | f: impl FnMut(HookEnv, &[u8]) -> Vec> + Send + Sync + 'a, 36 | ) -> BoxedHook<'a> { 37 | Arc::new(RwLock::new(f)) 38 | } 39 | 40 | /// A registry of hooks to call, indexed by the file descriptors through which they are accessed. 41 | #[derive(Clone)] 42 | pub struct HookRegistry<'a> { 43 | /// Table of registered hooks. Prefer using `Runtime::hook`, ` Runtime::hook_env`, 44 | /// and `HookRegistry::get` over interacting with this field directly. 45 | pub(crate) table: HashMap>, 46 | } 47 | 48 | impl<'a> HookRegistry<'a> { 49 | /// Create a default [`HookRegistry`]. 50 | #[must_use] 51 | pub fn new() -> Self { 52 | HookRegistry::default() 53 | } 54 | 55 | /// Create an empty [`HookRegistry`]. 56 | #[must_use] 57 | pub fn empty() -> Self { 58 | Self { 59 | table: HashMap::default(), 60 | } 61 | } 62 | 63 | /// Get a hook with exclusive write access, if it exists. 64 | /// 65 | /// Note: This function should not be called in async contexts, unless you know what you are 66 | /// doing. 67 | #[must_use] 68 | pub fn get(&self, fd: u32) -> Option> { 69 | // Calling `.unwrap()` panics on a poisoned lock. Should never happen normally. 70 | self.table.get(&fd).map(|x| x.write().unwrap()) 71 | } 72 | } 73 | 74 | impl Default for HookRegistry<'_> { 75 | fn default() -> Self { 76 | // When `LazyCell` gets stabilized (1.81.0), we can use it to avoid unnecessary allocations. 77 | let table = HashMap::from([ 78 | // Note: To ensure any `fd` value is synced with `zkvm/precompiles/src/io.rs`, 79 | // add an assertion to the test `hook_fds_match` below. 80 | (FD_ECRECOVER_HOOK, hookify(hook_ecrecover)), 81 | ]); 82 | 83 | Self { table } 84 | } 85 | } 86 | 87 | impl Debug for HookRegistry<'_> { 88 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 89 | let mut keys = self.table.keys().collect::>(); 90 | keys.sort_unstable(); 91 | f.debug_struct("HookRegistry") 92 | .field( 93 | "table", 94 | &format_args!("{{{} hooks registered at {:?}}}", self.table.len(), keys), 95 | ) 96 | .finish() 97 | } 98 | } 99 | 100 | /// Environment that a hook may read from. 101 | pub struct HookEnv<'a, 'b: 'a> { 102 | /// The runtime. 103 | pub runtime: &'a Executor<'b>, 104 | } 105 | 106 | /// Recovers the public key from the signature and message hash using the k256 crate. 107 | /// 108 | /// # Arguments 109 | /// 110 | /// * `env` - The environment in which the hook is invoked. 111 | /// * `buf` - The buffer containing the signature and message hash. 112 | /// - The signature is 65 bytes, the first 64 bytes are the signature and the last byte is the 113 | /// recovery ID. 114 | /// - The message hash is 32 bytes. 115 | /// 116 | /// The result is returned as a pair of bytes, where the first 32 bytes are the X coordinate 117 | /// and the second 32 bytes are the Y coordinate of the decompressed point. 118 | /// 119 | /// WARNING: This function is used to recover the public key outside of the zkVM context. These 120 | /// values must be constrained by the zkVM for correctness. 121 | #[must_use] 122 | pub fn hook_ecrecover(_: HookEnv, buf: &[u8]) -> Vec> { 123 | assert_eq!( 124 | buf.len(), 125 | 65 + 32, 126 | "ecrecover input should have length 65 + 32" 127 | ); 128 | let (sig, msg_hash) = buf.split_at(65); 129 | let sig: &[u8; 65] = sig.try_into().unwrap(); 130 | let msg_hash: &[u8; 32] = msg_hash.try_into().unwrap(); 131 | 132 | let mut recovery_id = sig[64]; 133 | let mut sig = Signature::from_slice(&sig[..64]).unwrap(); 134 | 135 | if let Some(sig_normalized) = sig.normalize_s() { 136 | sig = sig_normalized; 137 | recovery_id ^= 1; 138 | }; 139 | let recid = RecoveryId::from_byte(recovery_id).expect("Computed recovery ID is invalid!"); 140 | 141 | let recovered_key = VerifyingKey::recover_from_prehash(&msg_hash[..], &sig, recid).unwrap(); 142 | let bytes = recovered_key.to_sec1_bytes(); 143 | 144 | let (_, s) = sig.split_scalars(); 145 | let s_inverse = s.invert(); 146 | 147 | vec![bytes.to_vec(), s_inverse.to_bytes().to_vec()] 148 | } 149 | -------------------------------------------------------------------------------- /crates/executor/src/instruction.rs: -------------------------------------------------------------------------------- 1 | //! Instructions for the SP1 zkVM. 2 | 3 | use core::fmt::Debug; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::opcode::Opcode; 7 | 8 | /// RISC-V 32IM Instruction. 9 | /// 10 | /// The structure of the instruction differs from the RISC-V ISA. We do not encode the instructions 11 | /// as 32-bit words, but instead use a custom encoding that is more friendly to decode in the 12 | /// SP1 zkVM. 13 | #[derive(Clone, Copy, Serialize, Deserialize)] 14 | pub struct Instruction { 15 | /// The operation to execute. 16 | pub opcode: Opcode, 17 | /// The first operand. 18 | pub op_a: u32, 19 | /// The second operand. 20 | pub op_b: u32, 21 | /// The third operand. 22 | pub op_c: u32, 23 | /// Whether the second operand is an immediate value. 24 | pub imm_b: bool, 25 | /// Whether the third operand is an immediate value. 26 | pub imm_c: bool, 27 | } 28 | 29 | impl Instruction { 30 | /// Create a new [`RiscvInstruction`]. 31 | #[must_use] 32 | pub const fn new( 33 | opcode: Opcode, 34 | op_a: u32, 35 | op_b: u32, 36 | op_c: u32, 37 | imm_b: bool, 38 | imm_c: bool, 39 | ) -> Self { 40 | Self { 41 | opcode, 42 | op_a, 43 | op_b, 44 | op_c, 45 | imm_b, 46 | imm_c, 47 | } 48 | } 49 | 50 | /// Returns if the instruction is an ALU instruction. 51 | #[must_use] 52 | pub const fn is_alu_instruction(&self) -> bool { 53 | matches!( 54 | self.opcode, 55 | Opcode::ADD 56 | | Opcode::SUB 57 | | Opcode::XOR 58 | | Opcode::OR 59 | | Opcode::AND 60 | | Opcode::SLL 61 | | Opcode::SRL 62 | | Opcode::SRA 63 | | Opcode::SLT 64 | | Opcode::SLTU 65 | | Opcode::MUL 66 | | Opcode::MULH 67 | | Opcode::MULHU 68 | | Opcode::MULHSU 69 | | Opcode::DIV 70 | | Opcode::DIVU 71 | | Opcode::REM 72 | | Opcode::REMU 73 | ) 74 | } 75 | 76 | /// Returns if the instruction is a ecall instruction. 77 | #[must_use] 78 | pub fn is_ecall_instruction(&self) -> bool { 79 | self.opcode == Opcode::ECALL 80 | } 81 | 82 | /// Returns if the instruction is a memory instruction. 83 | #[must_use] 84 | pub const fn is_memory_instruction(&self) -> bool { 85 | matches!( 86 | self.opcode, 87 | Opcode::LB 88 | | Opcode::LH 89 | | Opcode::LW 90 | | Opcode::LBU 91 | | Opcode::LHU 92 | | Opcode::SB 93 | | Opcode::SH 94 | | Opcode::SW 95 | ) 96 | } 97 | 98 | /// Returns if the instruction is a branch instruction. 99 | #[must_use] 100 | pub const fn is_branch_instruction(&self) -> bool { 101 | matches!( 102 | self.opcode, 103 | Opcode::BEQ | Opcode::BNE | Opcode::BLT | Opcode::BGE | Opcode::BLTU | Opcode::BGEU 104 | ) 105 | } 106 | 107 | /// Returns if the instruction is a jump instruction. 108 | #[must_use] 109 | pub const fn is_jump_instruction(&self) -> bool { 110 | matches!(self.opcode, Opcode::JAL | Opcode::JALR) 111 | } 112 | } 113 | 114 | impl Debug for Instruction { 115 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 116 | let mnemonic = self.opcode.mnemonic(); 117 | let op_a_formatted = format!("%x{}", self.op_a); 118 | let op_b_formatted = if self.imm_b || self.opcode == Opcode::AUIPC { 119 | format!("{}", self.op_b as i32) 120 | } else { 121 | format!("%x{}", self.op_b) 122 | }; 123 | let op_c_formatted = if self.imm_c { 124 | format!("{}", self.op_c as i32) 125 | } else { 126 | format!("%x{}", self.op_c) 127 | }; 128 | 129 | let width = 10; 130 | write!( 131 | f, 132 | "{mnemonic: { 8 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result { 9 | self.read_public_values_slice(buf); 10 | Ok(buf.len()) 11 | } 12 | } 13 | 14 | impl Executor<'_> { 15 | /// Write a serializable input to the standard input stream. 16 | pub fn write_stdin(&mut self, input: &T) { 17 | let mut buf = Vec::new(); 18 | bincode::serialize_into(&mut buf, input).expect("serialization failed"); 19 | self.state.input_stream.push(buf); 20 | } 21 | 22 | /// Write a slice of bytes to the standard input stream. 23 | pub fn write_stdin_slice(&mut self, input: &[u8]) { 24 | self.state.input_stream.push(input.to_vec()); 25 | } 26 | 27 | /// Write a slice of vecs to the standard input stream. 28 | pub fn write_vecs(&mut self, inputs: &[Vec]) { 29 | for input in inputs { 30 | self.state.input_stream.push(input.clone()); 31 | } 32 | } 33 | 34 | /// Read a serializable public values from the public values stream. 35 | pub fn read_public_values(&mut self) -> T { 36 | let result = bincode::deserialize_from::<_, T>(self); 37 | result.unwrap() 38 | } 39 | 40 | /// Read a slice of bytes from the public values stream. 41 | pub fn read_public_values_slice(&mut self, buf: &mut [u8]) { 42 | let len = buf.len(); 43 | let start = self.state.public_values_stream_ptr; 44 | let end = start + len; 45 | assert!(end <= self.state.public_values_stream.len()); 46 | buf.copy_from_slice(&self.state.public_values_stream[start..end]); 47 | self.state.public_values_stream_ptr = end; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /crates/executor/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! An implementation of an exucutor for the SP1 RISC-V zkVM. 2 | 3 | #![warn(clippy::pedantic)] 4 | #![allow(clippy::similar_names)] 5 | #![allow(clippy::cast_possible_wrap)] 6 | #![allow(clippy::cast_possible_truncation)] 7 | #![allow(clippy::cast_sign_loss)] 8 | #![allow(clippy::module_name_repetitions)] 9 | #![allow(clippy::needless_range_loop)] 10 | #![allow(clippy::cast_lossless)] 11 | #![allow(clippy::bool_to_int_with_if)] 12 | #![allow(clippy::should_panic_without_expect)] 13 | #![allow(clippy::field_reassign_with_default)] 14 | #![allow(clippy::manual_assert)] 15 | #![allow(clippy::unreadable_literal)] 16 | #![allow(clippy::match_wildcard_for_single_variants)] 17 | #![allow(clippy::missing_panics_doc)] 18 | #![allow(clippy::missing_errors_doc)] 19 | #![allow(clippy::explicit_iter_loop)] 20 | #![warn(missing_docs)] 21 | 22 | mod context; 23 | mod disassembler; 24 | pub mod events; 25 | mod executor; 26 | mod hook; 27 | mod instruction; 28 | mod io; 29 | mod opcode; 30 | mod program; 31 | mod register; 32 | mod state; 33 | pub mod syscalls; 34 | mod utils; 35 | 36 | pub use context::*; 37 | pub use executor::*; 38 | pub use hook::*; 39 | pub use instruction::*; 40 | pub use opcode::*; 41 | pub use program::*; 42 | pub use register::*; 43 | pub use state::*; 44 | pub use utils::*; 45 | -------------------------------------------------------------------------------- /crates/executor/src/opcode.rs: -------------------------------------------------------------------------------- 1 | //! Opcodes for the SP1 zkVM. 2 | 3 | use std::fmt::Display; 4 | 5 | use enum_map::Enum; 6 | use p3_field::Field; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | /// An opcode (short for "operation code") specifies the operation to be performed by the processor. 10 | /// 11 | /// In the context of the RISC-V ISA, an opcode specifies which operation (i.e., addition, 12 | /// subtraction, multiplication, etc.) to perform on up to three operands such as registers, 13 | /// immediates, or memory addresses. 14 | /// 15 | /// While the SP1 zkVM targets the RISC-V ISA, it uses a custom instruction encoding that uses 16 | /// a different set of opcodes. The main difference is that the SP1 zkVM encodes register 17 | /// operations and immediate operations as the same opcode. For example, the RISC-V opcodes ADD and 18 | /// ADDI both become ADD inside the SP1 zkVM. We utilize flags inside the instruction itself to 19 | /// distinguish between the two. 20 | /// 21 | /// Refer to the "RV32I Reference Card" [here](https://github.com/johnwinans/rvalp/releases) for 22 | /// more details. 23 | #[allow(non_camel_case_types)] 24 | #[derive( 25 | Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord, Enum, 26 | )] 27 | pub enum Opcode { 28 | /// rd ← rs1 + rs2, pc ← pc + 4 29 | ADD = 0, 30 | /// rd ← rs1 - rs2, pc ← pc + 4 31 | SUB = 1, 32 | /// rd ← rs1 ^ rs2, pc ← pc + 4 33 | XOR = 2, 34 | /// rd ← rs1 | rs2, pc ← pc + 4 35 | OR = 3, 36 | /// rd ← rs1 & rs2, pc ← pc + 4 37 | AND = 4, 38 | /// rd ← rs1 << rs2, pc ← pc + 4 39 | SLL = 5, 40 | /// rd ← rs1 >> rs2 (logical), pc ← pc + 4 41 | SRL = 6, 42 | /// rd ← rs1 >> rs2 (arithmetic), pc ← pc + 4 43 | SRA = 7, 44 | /// rd ← (rs1 < rs2) ? 1 : 0 (signed), pc ← pc + 4 45 | SLT = 8, 46 | /// rd ← (rs1 < rs2) ? 1 : 0 (unsigned), pc ← pc + 4 47 | SLTU = 9, 48 | /// rd ← sx(m8(rs1 + imm)), pc ← pc + 4 49 | LB = 10, 50 | /// rd ← sx(m16(rs1 + imm)), pc ← pc + 4 51 | LH = 11, 52 | /// rd ← sx(m32(rs1 + imm)), pc ← pc + 4 53 | LW = 12, 54 | /// rd ← zx(m8(rs1 + imm)), pc ← pc + 4 55 | LBU = 13, 56 | /// rd ← zx(m16(rs1 + imm)), pc ← pc + 4 57 | LHU = 14, 58 | /// m8(rs1 + imm) ← rs2[7:0], pc ← pc + 4 59 | SB = 15, 60 | /// m16(rs1 + imm) ← rs2[15:0], pc ← pc + 4 61 | SH = 16, 62 | /// m32(rs1 + imm) ← rs2[31:0], pc ← pc + 4 63 | SW = 17, 64 | /// pc ← pc + ((rs1 == rs2) ? imm : 4) 65 | BEQ = 18, 66 | /// pc ← pc + ((rs1 != rs2) ? imm : 4) 67 | BNE = 19, 68 | /// pc ← pc + ((rs1 < rs2) ? imm : 4) (signed) 69 | BLT = 20, 70 | /// pc ← pc + ((rs1 >= rs2) ? imm : 4) (signed) 71 | BGE = 21, 72 | /// pc ← pc + ((rs1 < rs2) ? imm : 4) (unsigned) 73 | BLTU = 22, 74 | /// pc ← pc + ((rs1 >= rs2) ? imm : 4) (unsigned) 75 | BGEU = 23, 76 | /// rd ← pc + 4, pc ← pc + imm 77 | JAL = 24, 78 | /// rd ← pc + 4, pc ← (rs1 + imm) & ∼1 79 | JALR = 25, 80 | /// rd ← pc + imm, pc ← pc + 4 81 | AUIPC = 27, 82 | /// Transfer control to the debugger. 83 | ECALL = 28, 84 | /// Transfer control to the operating system. 85 | EBREAK = 29, 86 | /// rd ← rs1 * rs2 (signed), pc ← pc + 4 87 | MUL = 30, 88 | /// rd ← rs1 * rs2 (half), pc ← pc + 4 89 | MULH = 31, 90 | /// rd ← rs1 * rs2 (half unsigned), pc ← pc + 4 91 | MULHU = 32, 92 | /// rd ← rs1 * rs2 (half signed unsigned), pc ← pc + 4 93 | MULHSU = 33, 94 | /// rd ← rs1 / rs2 (signed), pc ← pc + 4 95 | DIV = 34, 96 | /// rd ← rs1 / rs2 (unsigned), pc ← pc + 4 97 | DIVU = 35, 98 | /// rd ← rs1 % rs2 (signed), pc ← pc + 4 99 | REM = 36, 100 | /// rd ← rs1 % rs2 (unsigned), pc ← pc + 4 101 | REMU = 37, 102 | /// Unimplemented instruction. 103 | UNIMP = 39, 104 | } 105 | 106 | /// Byte Opcode. 107 | /// 108 | /// This represents a basic operation that can be performed on a byte. Usually, these operations 109 | /// are performed via lookup tables on that iterate over the domain of two 8-bit values. The 110 | /// operations include both bitwise operations (AND, OR, XOR) as well as basic arithmetic. 111 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] 112 | #[allow(clippy::upper_case_acronyms)] 113 | pub enum ByteOpcode { 114 | /// Bitwise AND. 115 | AND = 0, 116 | /// Bitwise OR. 117 | OR = 1, 118 | /// Bitwise XOR. 119 | XOR = 2, 120 | /// Shift Left Logical. 121 | SLL = 3, 122 | /// Unsigned 8-bit Range Check. 123 | U8Range = 4, 124 | /// Shift Right with Carry. 125 | ShrCarry = 5, 126 | /// Unsigned Less Than. 127 | LTU = 6, 128 | /// Most Significant Bit. 129 | MSB = 7, 130 | /// Unsigned 16-bit Range Check. 131 | U16Range = 8, 132 | } 133 | 134 | impl Opcode { 135 | /// Get the mnemonic for the opcode. 136 | #[must_use] 137 | pub const fn mnemonic(&self) -> &str { 138 | match self { 139 | Opcode::ADD => "add", 140 | Opcode::SUB => "sub", 141 | Opcode::XOR => "xor", 142 | Opcode::OR => "or", 143 | Opcode::AND => "and", 144 | Opcode::SLL => "sll", 145 | Opcode::SRL => "srl", 146 | Opcode::SRA => "sra", 147 | Opcode::SLT => "slt", 148 | Opcode::SLTU => "sltu", 149 | Opcode::LB => "lb", 150 | Opcode::LH => "lh", 151 | Opcode::LW => "lw", 152 | Opcode::LBU => "lbu", 153 | Opcode::LHU => "lhu", 154 | Opcode::SB => "sb", 155 | Opcode::SH => "sh", 156 | Opcode::SW => "sw", 157 | Opcode::BEQ => "beq", 158 | Opcode::BNE => "bne", 159 | Opcode::BLT => "blt", 160 | Opcode::BGE => "bge", 161 | Opcode::BLTU => "bltu", 162 | Opcode::BGEU => "bgeu", 163 | Opcode::JAL => "jal", 164 | Opcode::JALR => "jalr", 165 | Opcode::AUIPC => "auipc", 166 | Opcode::ECALL => "ecall", 167 | Opcode::EBREAK => "ebreak", 168 | Opcode::MUL => "mul", 169 | Opcode::MULH => "mulh", 170 | Opcode::MULHU => "mulhu", 171 | Opcode::MULHSU => "mulhsu", 172 | Opcode::DIV => "div", 173 | Opcode::DIVU => "divu", 174 | Opcode::REM => "rem", 175 | Opcode::REMU => "remu", 176 | Opcode::UNIMP => "unimp", 177 | } 178 | } 179 | 180 | /// Convert the opcode to a field element. 181 | #[must_use] 182 | pub fn as_field(self) -> F { 183 | F::from_canonical_u32(self as u32) 184 | } 185 | } 186 | 187 | impl Display for Opcode { 188 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 189 | f.write_str(self.mnemonic()) 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /crates/executor/src/program.rs: -------------------------------------------------------------------------------- 1 | //! Programs that can be executed by the SP1 zkVM. 2 | 3 | use std::{fs::File, io::Read}; 4 | 5 | use hashbrown::HashMap; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | use crate::{ 9 | disassembler::{transpile, Elf}, 10 | instruction::Instruction, 11 | }; 12 | 13 | /// A program that can be executed by the SP1 zkVM. 14 | /// 15 | /// Contains a series of instructions along with the initial memory image. It also contains the 16 | /// start address and base address of the program. 17 | #[derive(Debug, Clone, Default, Serialize, Deserialize)] 18 | pub struct Program { 19 | /// The instructions of the program. 20 | pub instructions: Vec, 21 | /// The start address of the program. 22 | pub pc_start: u32, 23 | /// The base address of the program. 24 | pub pc_base: u32, 25 | /// The initial memory image, useful for global constants. 26 | pub memory_image: HashMap, 27 | } 28 | 29 | impl Program { 30 | /// Create a new [Program]. 31 | #[must_use] 32 | pub fn new(instructions: Vec, pc_start: u32, pc_base: u32) -> Self { 33 | Self { 34 | instructions, 35 | pc_start, 36 | pc_base, 37 | memory_image: HashMap::new(), 38 | } 39 | } 40 | 41 | /// Disassemble a RV32IM ELF to a program that be executed by the VM. 42 | /// 43 | /// # Errors 44 | /// 45 | /// This function may return an error if the ELF is not valid. 46 | pub fn from(input: &[u8]) -> eyre::Result { 47 | // Decode the bytes as an ELF. 48 | let elf = Elf::decode(input)?; 49 | 50 | // Transpile the RV32IM instructions. 51 | let instructions = transpile(&elf.instructions); 52 | 53 | // Return the program. 54 | Ok(Program { 55 | instructions, 56 | pc_start: elf.pc_start, 57 | pc_base: elf.pc_base, 58 | memory_image: elf.memory_image, 59 | }) 60 | } 61 | 62 | /// Disassemble a RV32IM ELF to a program that be executed by the VM from a file path. 63 | /// 64 | /// # Errors 65 | /// 66 | /// This function will return an error if the file cannot be opened or read. 67 | pub fn from_elf(path: &str) -> eyre::Result { 68 | let mut elf_code = Vec::new(); 69 | File::open(path)?.read_to_end(&mut elf_code)?; 70 | Program::from(&elf_code) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /crates/executor/src/register.rs: -------------------------------------------------------------------------------- 1 | //! Registers for the SP1 zkVM. 2 | 3 | /// A register stores a 32-bit value used by operations. 4 | #[derive(Debug, Clone, Copy, PartialEq)] 5 | pub enum Register { 6 | /// %x0 7 | X0 = 0, 8 | /// %x1 9 | X1 = 1, 10 | /// %x2 11 | X2 = 2, 12 | /// %x3 13 | X3 = 3, 14 | /// %x4 15 | X4 = 4, 16 | /// %x5 17 | X5 = 5, 18 | /// %x6 19 | X6 = 6, 20 | /// %x7 21 | X7 = 7, 22 | /// %x8 23 | X8 = 8, 24 | /// %x9 25 | X9 = 9, 26 | /// %x10 27 | X10 = 10, 28 | /// %x11 29 | X11 = 11, 30 | /// %x12 31 | X12 = 12, 32 | /// %x13 33 | X13 = 13, 34 | /// %x14 35 | X14 = 14, 36 | /// %x15 37 | X15 = 15, 38 | /// %x16 39 | X16 = 16, 40 | /// %x17 41 | X17 = 17, 42 | /// %x18 43 | X18 = 18, 44 | /// %x19 45 | X19 = 19, 46 | /// %x20 47 | X20 = 20, 48 | /// %x21 49 | X21 = 21, 50 | /// %x22 51 | X22 = 22, 52 | /// %x23 53 | X23 = 23, 54 | /// %x24 55 | X24 = 24, 56 | /// %x25 57 | X25 = 25, 58 | /// %x26 59 | X26 = 26, 60 | /// %x27 61 | X27 = 27, 62 | /// %x28 63 | X28 = 28, 64 | /// %x29 65 | X29 = 29, 66 | /// %x30 67 | X30 = 30, 68 | /// %x31 69 | X31 = 31, 70 | } 71 | 72 | impl Register { 73 | /// Create a new register from a u32. 74 | /// 75 | /// # Panics 76 | /// 77 | /// This function will panic if the register is invalid. 78 | #[inline] 79 | #[must_use] 80 | pub fn from_u32(value: u32) -> Self { 81 | match value { 82 | 0 => Register::X0, 83 | 1 => Register::X1, 84 | 2 => Register::X2, 85 | 3 => Register::X3, 86 | 4 => Register::X4, 87 | 5 => Register::X5, 88 | 6 => Register::X6, 89 | 7 => Register::X7, 90 | 8 => Register::X8, 91 | 9 => Register::X9, 92 | 10 => Register::X10, 93 | 11 => Register::X11, 94 | 12 => Register::X12, 95 | 13 => Register::X13, 96 | 14 => Register::X14, 97 | 15 => Register::X15, 98 | 16 => Register::X16, 99 | 17 => Register::X17, 100 | 18 => Register::X18, 101 | 19 => Register::X19, 102 | 20 => Register::X20, 103 | 21 => Register::X21, 104 | 22 => Register::X22, 105 | 23 => Register::X23, 106 | 24 => Register::X24, 107 | 25 => Register::X25, 108 | 26 => Register::X26, 109 | 27 => Register::X27, 110 | 28 => Register::X28, 111 | 29 => Register::X29, 112 | 30 => Register::X30, 113 | 31 => Register::X31, 114 | _ => panic!("invalid register {value}"), 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /crates/executor/src/state.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::File, 3 | io::{Seek, Write}, 4 | }; 5 | 6 | use hashbrown::HashMap; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | use crate::{events::MemoryRecord, syscalls::SyscallCode, ExecutorMode}; 10 | 11 | /// Holds data describing the current state of a program's execution. 12 | #[derive(Debug, Clone, Default, Serialize, Deserialize)] 13 | #[repr(C)] 14 | pub struct ExecutionState { 15 | /// The program counter. 16 | pub pc: u32, 17 | 18 | /// The shard clock keeps track of how many shards have been executed. 19 | pub current_shard: u32, 20 | 21 | /// The memory which instructions operate over. Values contain the memory value and last shard 22 | /// + timestamp that each memory address was accessed. 23 | pub memory: HashMap, 24 | 25 | /// The global clock keeps track of how many instructions have been executed through all shards. 26 | pub global_clk: u64, 27 | 28 | /// The clock increments by 4 (possibly more in syscalls) for each instruction that has been 29 | /// executed in this shard. 30 | pub clk: u32, 31 | 32 | /// Uninitialized memory addresses that have a specific value they should be initialized with. 33 | /// `SyscallHintRead` uses this to write hint data into uninitialized memory. 34 | pub uninitialized_memory: HashMap, 35 | 36 | /// A stream of input values (global to the entire program). 37 | pub input_stream: Vec>, 38 | 39 | /// A ptr to the current position in the input stream incremented by `HINT_READ` opcode. 40 | pub input_stream_ptr: usize, 41 | 42 | /// A ptr to the current position in the proof stream, incremented after verifying a proof. 43 | pub proof_stream_ptr: usize, 44 | 45 | /// A stream of public values from the program (global to entire program). 46 | pub public_values_stream: Vec, 47 | 48 | /// A ptr to the current position in the public values stream, incremented when reading from 49 | /// `public_values_stream`. 50 | pub public_values_stream_ptr: usize, 51 | 52 | /// Keeps track of how many times a certain syscall has been called. 53 | pub syscall_counts: HashMap, 54 | } 55 | 56 | impl ExecutionState { 57 | #[must_use] 58 | /// Create a new [`ExecutionState`]. 59 | pub fn new(pc_start: u32) -> Self { 60 | Self { 61 | global_clk: 0, 62 | // Start at shard 1 since shard 0 is reserved for memory initialization. 63 | current_shard: 1, 64 | clk: 0, 65 | pc: pc_start, 66 | memory: HashMap::new(), 67 | uninitialized_memory: HashMap::new(), 68 | input_stream: Vec::new(), 69 | input_stream_ptr: 0, 70 | public_values_stream: Vec::new(), 71 | public_values_stream_ptr: 0, 72 | proof_stream_ptr: 0, 73 | syscall_counts: HashMap::new(), 74 | } 75 | } 76 | } 77 | 78 | /// Holds data to track changes made to the runtime since a fork point. 79 | #[derive(Debug, Clone, Default)] 80 | #[allow(dead_code)] 81 | pub struct ForkState { 82 | /// The `global_clk` value at the fork point. 83 | pub global_clk: u64, 84 | /// The original `clk` value at the fork point. 85 | pub clk: u32, 86 | /// The original `pc` value at the fork point. 87 | pub pc: u32, 88 | /// All memory changes since the fork point. 89 | pub memory_diff: HashMap>, 90 | // /// The original memory access record at the fork point. 91 | // pub op_record: MemoryAccessRecord, 92 | // /// The original execution record at the fork point. 93 | // pub record: ExecutionRecord, 94 | /// Whether `emit_events` was enabled at the fork point. 95 | pub executor_mode: ExecutorMode, 96 | } 97 | 98 | impl ExecutionState { 99 | /// Save the execution state to a file. 100 | pub fn save(&self, file: &mut File) -> std::io::Result<()> { 101 | let mut writer = std::io::BufWriter::new(file); 102 | bincode::serialize_into(&mut writer, self).unwrap(); 103 | writer.flush()?; 104 | writer.seek(std::io::SeekFrom::Start(0))?; 105 | Ok(()) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/code.rs: -------------------------------------------------------------------------------- 1 | use enum_map::Enum; 2 | use serde::{Deserialize, Serialize}; 3 | use strum_macros::EnumIter; 4 | 5 | /// System Calls. 6 | /// 7 | /// A system call is invoked by the the `ecall` instruction with a specific value in register t0. 8 | /// The syscall number is a 32-bit integer with the following little-endian layout: 9 | /// 10 | /// | Byte 0 | Byte 1 | Byte 2 | Byte 3 | 11 | /// | ------ | ------ | ------ | ------ | 12 | /// | ID | Table | Cycles | Unused | 13 | /// 14 | /// where: 15 | /// - Byte 0: The system call identifier. 16 | /// - Byte 1: Whether the handler of the system call has its own table. This is used in the CPU 17 | /// table to determine whether to lookup the syscall using the syscall interaction. 18 | /// - Byte 2: The number of additional cycles the syscall uses. This is used to make sure the # of 19 | /// memory accesses is bounded. 20 | /// - Byte 3: Currently unused. 21 | #[derive( 22 | Debug, Copy, Clone, PartialEq, Eq, Hash, EnumIter, Ord, PartialOrd, Serialize, Deserialize, Enum, 23 | )] 24 | #[allow(non_camel_case_types)] 25 | #[allow(clippy::upper_case_acronyms)] 26 | pub enum SyscallCode { 27 | /// Halts the program. 28 | HALT = 0x00_00_00_00, 29 | 30 | /// Write to the output buffer. 31 | WRITE = 0x00_00_00_02, 32 | 33 | /// Enter unconstrained block. 34 | ENTER_UNCONSTRAINED = 0x00_00_00_03, 35 | 36 | /// Exit unconstrained block. 37 | EXIT_UNCONSTRAINED = 0x00_00_00_04, 38 | 39 | /// Executes the `SHA_EXTEND` precompile. 40 | SHA_EXTEND = 0x00_30_01_05, 41 | 42 | /// Executes the `SHA_COMPRESS` precompile. 43 | SHA_COMPRESS = 0x00_01_01_06, 44 | 45 | /// Executes the `ED_ADD` precompile. 46 | ED_ADD = 0x00_01_01_07, 47 | 48 | /// Executes the `ED_DECOMPRESS` precompile. 49 | ED_DECOMPRESS = 0x00_00_01_08, 50 | 51 | /// Executes the `KECCAK_PERMUTE` precompile. 52 | KECCAK_PERMUTE = 0x00_01_01_09, 53 | 54 | /// Executes the `SECP256K1_ADD` precompile. 55 | SECP256K1_ADD = 0x00_01_01_0A, 56 | 57 | /// Executes the `SECP256K1_DOUBLE` precompile. 58 | SECP256K1_DOUBLE = 0x00_00_01_0B, 59 | 60 | /// Executes the `SECP256K1_DECOMPRESS` precompile. 61 | SECP256K1_DECOMPRESS = 0x00_00_01_0C, 62 | 63 | /// Executes the `BN254_ADD` precompile. 64 | BN254_ADD = 0x00_01_01_0E, 65 | 66 | /// Executes the `BN254_DOUBLE` precompile. 67 | BN254_DOUBLE = 0x00_00_01_0F, 68 | 69 | /// Executes the `COMMIT` precompile. 70 | COMMIT = 0x00_00_00_10, 71 | 72 | /// Executes the `COMMIT_DEFERRED_PROOFS` precompile. 73 | COMMIT_DEFERRED_PROOFS = 0x00_00_00_1A, 74 | 75 | /// Executes the `VERIFY_SP1_PROOF` precompile. 76 | VERIFY_SP1_PROOF = 0x00_00_00_1B, 77 | 78 | /// Executes the `BLS12381_DECOMPRESS` precompile. 79 | BLS12381_DECOMPRESS = 0x00_00_01_1C, 80 | 81 | /// Executes the `HINT_LEN` precompile. 82 | HINT_LEN = 0x00_00_00_F0, 83 | 84 | /// Executes the `HINT_READ` precompile. 85 | HINT_READ = 0x00_00_00_F1, 86 | 87 | /// Executes the `UINT256_MUL` precompile. 88 | UINT256_MUL = 0x00_01_01_1D, 89 | 90 | /// Executes the `BLS12381_ADD` precompile. 91 | BLS12381_ADD = 0x00_01_01_1E, 92 | 93 | /// Executes the `BLS12381_DOUBLE` precompile. 94 | BLS12381_DOUBLE = 0x00_00_01_1F, 95 | 96 | /// Executes the `BLS12381_FP_ADD` precompile. 97 | BLS12381_FP_ADD = 0x00_01_01_20, 98 | 99 | /// Executes the `BLS12381_FP_SUB` precompile. 100 | BLS12381_FP_SUB = 0x00_01_01_21, 101 | 102 | /// Executes the `BLS12381_FP_MUL` precompile. 103 | BLS12381_FP_MUL = 0x00_01_01_22, 104 | 105 | /// Executes the `BLS12381_FP2_ADD` precompile. 106 | BLS12381_FP2_ADD = 0x00_01_01_23, 107 | 108 | /// Executes the `BLS12381_FP2_SUB` precompile. 109 | BLS12381_FP2_SUB = 0x00_01_01_24, 110 | 111 | /// Executes the `BLS12381_FP2_MUL` precompile. 112 | BLS12381_FP2_MUL = 0x00_01_01_25, 113 | 114 | /// Executes the `BN254_FP_ADD` precompile. 115 | BN254_FP_ADD = 0x00_01_01_26, 116 | 117 | /// Executes the `BN254_FP_SUB` precompile. 118 | BN254_FP_SUB = 0x00_01_01_27, 119 | 120 | /// Executes the `BN254_FP_MUL` precompile. 121 | BN254_FP_MUL = 0x00_01_01_28, 122 | 123 | /// Executes the `BN254_FP2_ADD` precompile. 124 | BN254_FP2_ADD = 0x00_01_01_29, 125 | 126 | /// Executes the `BN254_FP2_SUB` precompile. 127 | BN254_FP2_SUB = 0x00_01_01_2A, 128 | 129 | /// Executes the `BN254_FP2_MUL` precompile. 130 | BN254_FP2_MUL = 0x00_01_01_2B, 131 | } 132 | 133 | impl SyscallCode { 134 | /// Create a [`SyscallCode`] from a u32. 135 | #[must_use] 136 | pub fn from_u32(value: u32) -> Self { 137 | match value { 138 | 0x00_00_00_00 => SyscallCode::HALT, 139 | 0x00_00_00_02 => SyscallCode::WRITE, 140 | 0x00_00_00_03 => SyscallCode::ENTER_UNCONSTRAINED, 141 | 0x00_00_00_04 => SyscallCode::EXIT_UNCONSTRAINED, 142 | 0x00_30_01_05 => SyscallCode::SHA_EXTEND, 143 | 0x00_01_01_06 => SyscallCode::SHA_COMPRESS, 144 | 0x00_01_01_07 => SyscallCode::ED_ADD, 145 | 0x00_00_01_08 => SyscallCode::ED_DECOMPRESS, 146 | 0x00_01_01_09 => SyscallCode::KECCAK_PERMUTE, 147 | 0x00_01_01_0A => SyscallCode::SECP256K1_ADD, 148 | 0x00_00_01_0B => SyscallCode::SECP256K1_DOUBLE, 149 | 0x00_00_01_0C => SyscallCode::SECP256K1_DECOMPRESS, 150 | 0x00_01_01_0E => SyscallCode::BN254_ADD, 151 | 0x00_00_01_0F => SyscallCode::BN254_DOUBLE, 152 | 0x00_01_01_1E => SyscallCode::BLS12381_ADD, 153 | 0x00_00_01_1F => SyscallCode::BLS12381_DOUBLE, 154 | 0x00_00_00_10 => SyscallCode::COMMIT, 155 | 0x00_00_00_1A => SyscallCode::COMMIT_DEFERRED_PROOFS, 156 | 0x00_00_00_1B => SyscallCode::VERIFY_SP1_PROOF, 157 | 0x00_00_00_F0 => SyscallCode::HINT_LEN, 158 | 0x00_00_00_F1 => SyscallCode::HINT_READ, 159 | 0x00_01_01_1D => SyscallCode::UINT256_MUL, 160 | 0x00_01_01_20 => SyscallCode::BLS12381_FP_ADD, 161 | 0x00_01_01_21 => SyscallCode::BLS12381_FP_SUB, 162 | 0x00_01_01_22 => SyscallCode::BLS12381_FP_MUL, 163 | 0x00_01_01_23 => SyscallCode::BLS12381_FP2_ADD, 164 | 0x00_01_01_24 => SyscallCode::BLS12381_FP2_SUB, 165 | 0x00_01_01_25 => SyscallCode::BLS12381_FP2_MUL, 166 | 0x00_01_01_26 => SyscallCode::BN254_FP_ADD, 167 | 0x00_01_01_27 => SyscallCode::BN254_FP_SUB, 168 | 0x00_01_01_28 => SyscallCode::BN254_FP_MUL, 169 | 0x00_01_01_29 => SyscallCode::BN254_FP2_ADD, 170 | 0x00_01_01_2A => SyscallCode::BN254_FP2_SUB, 171 | 0x00_01_01_2B => SyscallCode::BN254_FP2_MUL, 172 | 0x00_00_01_1C => SyscallCode::BLS12381_DECOMPRESS, 173 | _ => panic!("invalid syscall number: {value}"), 174 | } 175 | } 176 | 177 | /// Get the system call identifier. 178 | #[must_use] 179 | pub fn syscall_id(self) -> u32 { 180 | (self as u32).to_le_bytes()[0].into() 181 | } 182 | 183 | /// Get whether the handler of the system call has its own table. 184 | #[must_use] 185 | pub fn should_send(self) -> u32 { 186 | (self as u32).to_le_bytes()[1].into() 187 | } 188 | 189 | /// Get the number of additional cycles the syscall uses. 190 | #[must_use] 191 | pub fn num_cycles(self) -> u32 { 192 | (self as u32).to_le_bytes()[2].into() 193 | } 194 | 195 | /// Map a syscall to another one in order to coalesce their counts. 196 | #[must_use] 197 | #[allow(clippy::match_same_arms)] 198 | pub fn count_map(&self) -> Self { 199 | match self { 200 | SyscallCode::BN254_FP_SUB => SyscallCode::BN254_FP_ADD, 201 | SyscallCode::BN254_FP_MUL => SyscallCode::BN254_FP_ADD, 202 | SyscallCode::BN254_FP2_SUB => SyscallCode::BN254_FP2_ADD, 203 | SyscallCode::BLS12381_FP_SUB => SyscallCode::BLS12381_FP_ADD, 204 | SyscallCode::BLS12381_FP_MUL => SyscallCode::BLS12381_FP_ADD, 205 | SyscallCode::BLS12381_FP2_SUB => SyscallCode::BLS12381_FP2_ADD, 206 | _ => *self, 207 | } 208 | } 209 | } 210 | 211 | impl std::fmt::Display for SyscallCode { 212 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 213 | write!(f, "{self:?}") 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/commit.rs: -------------------------------------------------------------------------------- 1 | use super::{Syscall, SyscallCode, SyscallContext}; 2 | 3 | pub(crate) struct CommitSyscall; 4 | 5 | impl Syscall for CommitSyscall { 6 | #[allow(clippy::mut_mut)] 7 | fn execute(&self, _: &mut SyscallContext, _: SyscallCode, _: u32, _: u32) -> Option { 8 | None 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/context.rs: -------------------------------------------------------------------------------- 1 | use hashbrown::HashMap; 2 | 3 | use crate::{ 4 | events::{LookupId, MemoryLocalEvent, MemoryReadRecord, MemoryWriteRecord}, 5 | Executor, Register, 6 | }; 7 | 8 | /// A runtime for syscalls that is protected so that developers cannot arbitrarily modify the 9 | /// runtime. 10 | #[allow(dead_code)] 11 | pub struct SyscallContext<'a, 'b: 'a> { 12 | /// The current shard. 13 | pub current_shard: u32, 14 | /// The clock cycle. 15 | pub clk: u32, 16 | /// The next program counter. 17 | pub next_pc: u32, 18 | /// The exit code. 19 | pub exit_code: u32, 20 | /// The runtime. 21 | pub rt: &'a mut Executor<'b>, 22 | /// The syscall lookup id. 23 | pub syscall_lookup_id: LookupId, 24 | /// The local memory access events for the syscall. 25 | pub local_memory_access: HashMap, 26 | } 27 | 28 | impl<'a, 'b> SyscallContext<'a, 'b> { 29 | /// Create a new [`SyscallContext`]. 30 | pub fn new(runtime: &'a mut Executor<'b>) -> Self { 31 | let current_shard = runtime.shard(); 32 | let clk = runtime.state.clk; 33 | Self { 34 | current_shard, 35 | clk, 36 | next_pc: runtime.state.pc.wrapping_add(4), 37 | exit_code: 0, 38 | rt: runtime, 39 | syscall_lookup_id: LookupId::default(), 40 | local_memory_access: HashMap::new(), 41 | } 42 | } 43 | 44 | // /// Get a mutable reference to the execution record. 45 | // pub fn record_mut(&mut self) -> &mut ExecutionRecord { 46 | // &mut self.rt.record 47 | // } 48 | 49 | /// Get the current shard. 50 | #[must_use] 51 | pub fn current_shard(&self) -> u32 { 52 | self.rt.state.current_shard 53 | } 54 | 55 | /// Read a word from memory. 56 | pub fn mr(&mut self, addr: u32) -> (MemoryReadRecord, u32) { 57 | let record = self.rt.mr( 58 | addr, 59 | self.current_shard, 60 | self.clk, 61 | Some(&mut self.local_memory_access), 62 | ); 63 | (record, record.value) 64 | } 65 | 66 | /// Read a slice of words from memory. 67 | pub fn mr_slice(&mut self, addr: u32, len: usize) -> (Vec, Vec) { 68 | let mut records = Vec::new(); 69 | let mut values = Vec::new(); 70 | for i in 0..len { 71 | let (record, value) = self.mr(addr + i as u32 * 4); 72 | records.push(record); 73 | values.push(value); 74 | } 75 | (records, values) 76 | } 77 | 78 | /// Write a word to memory. 79 | pub fn mw(&mut self, addr: u32, value: u32) -> MemoryWriteRecord { 80 | self.rt.mw( 81 | addr, 82 | value, 83 | self.current_shard, 84 | self.clk, 85 | Some(&mut self.local_memory_access), 86 | ) 87 | } 88 | 89 | /// Write a slice of words to memory. 90 | pub fn mw_slice(&mut self, addr: u32, values: &[u32]) -> Vec { 91 | let mut records = Vec::new(); 92 | for i in 0..values.len() { 93 | let record = self.mw(addr + i as u32 * 4, values[i]); 94 | records.push(record); 95 | } 96 | records 97 | } 98 | 99 | /// Postprocess the syscall. Specifically will process the syscall's memory local events. 100 | pub fn postprocess(&mut self) -> Vec { 101 | let mut syscall_local_mem_events = Vec::new(); 102 | 103 | if !self.rt.unconstrained { 104 | // Will need to transfer the existing memory local events in the executor to it's record, 105 | // and return all the syscall memory local events. This is similar to what 106 | // `bump_record` does. 107 | for (addr, event) in self.local_memory_access.drain() { 108 | self.rt.local_memory_access.remove(&addr); 109 | syscall_local_mem_events.push(event); 110 | } 111 | } 112 | 113 | syscall_local_mem_events 114 | } 115 | 116 | /// Get the current value of a register, but doesn't use a memory record. 117 | /// This is generally unconstrained, so you must be careful using it. 118 | #[must_use] 119 | pub fn register_unsafe(&mut self, register: Register) -> u32 { 120 | self.rt.register(register) 121 | } 122 | 123 | /// Get the current value of a byte, but doesn't use a memory record. 124 | #[must_use] 125 | pub fn byte_unsafe(&mut self, addr: u32) -> u8 { 126 | self.rt.byte(addr) 127 | } 128 | 129 | /// Get the current value of a word, but doesn't use a memory record. 130 | #[must_use] 131 | pub fn word_unsafe(&mut self, addr: u32) -> u32 { 132 | self.rt.word(addr) 133 | } 134 | 135 | /// Get a slice of words, but doesn't use a memory record. 136 | #[must_use] 137 | pub fn slice_unsafe(&mut self, addr: u32, len: usize) -> Vec { 138 | let mut values = Vec::new(); 139 | for i in 0..len { 140 | values.push(self.rt.word(addr + i as u32 * 4)); 141 | } 142 | values 143 | } 144 | 145 | /// Set the next program counter. 146 | pub fn set_next_pc(&mut self, next_pc: u32) { 147 | self.next_pc = next_pc; 148 | } 149 | 150 | /// Set the exit code. 151 | pub fn set_exit_code(&mut self, exit_code: u32) { 152 | self.exit_code = exit_code; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/deferred.rs: -------------------------------------------------------------------------------- 1 | use super::{Syscall, SyscallCode, SyscallContext}; 2 | 3 | pub(crate) struct CommitDeferredSyscall; 4 | 5 | impl Syscall for CommitDeferredSyscall { 6 | #[allow(clippy::mut_mut)] 7 | fn execute(&self, _: &mut SyscallContext, _: SyscallCode, _: u32, _: u32) -> Option { 8 | None 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/halt.rs: -------------------------------------------------------------------------------- 1 | use super::{context::SyscallContext, Syscall, SyscallCode}; 2 | 3 | pub(crate) struct HaltSyscall; 4 | 5 | impl Syscall for HaltSyscall { 6 | fn execute( 7 | &self, 8 | ctx: &mut SyscallContext, 9 | _: SyscallCode, 10 | exit_code: u32, 11 | _: u32, 12 | ) -> Option { 13 | ctx.set_next_pc(0); 14 | ctx.set_exit_code(exit_code); 15 | None 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/hint.rs: -------------------------------------------------------------------------------- 1 | use super::{Syscall, SyscallCode, SyscallContext}; 2 | 3 | pub(crate) struct HintLenSyscall; 4 | 5 | impl Syscall for HintLenSyscall { 6 | fn execute( 7 | &self, 8 | ctx: &mut SyscallContext, 9 | _: SyscallCode, 10 | _arg1: u32, 11 | _arg2: u32, 12 | ) -> Option { 13 | if ctx.rt.state.input_stream_ptr >= ctx.rt.state.input_stream.len() { 14 | panic!( 15 | "failed reading stdin due to insufficient input data: input_stream_ptr={}, input_stream_len={}", 16 | ctx.rt.state.input_stream_ptr, 17 | ctx.rt.state.input_stream.len() 18 | ); 19 | } 20 | Some(ctx.rt.state.input_stream[ctx.rt.state.input_stream_ptr].len() as u32) 21 | } 22 | } 23 | 24 | pub(crate) struct HintReadSyscall; 25 | 26 | impl Syscall for HintReadSyscall { 27 | fn execute(&self, ctx: &mut SyscallContext, _: SyscallCode, ptr: u32, len: u32) -> Option { 28 | if ctx.rt.state.input_stream_ptr >= ctx.rt.state.input_stream.len() { 29 | panic!( 30 | "failed reading stdin due to insufficient input data: input_stream_ptr={}, input_stream_len={}", 31 | ctx.rt.state.input_stream_ptr, 32 | ctx.rt.state.input_stream.len() 33 | ); 34 | } 35 | let vec = &ctx.rt.state.input_stream[ctx.rt.state.input_stream_ptr]; 36 | ctx.rt.state.input_stream_ptr += 1; 37 | assert!( 38 | !ctx.rt.unconstrained, 39 | "hint read should not be used in a unconstrained block" 40 | ); 41 | assert_eq!( 42 | vec.len() as u32, 43 | len, 44 | "hint input stream read length mismatch" 45 | ); 46 | assert_eq!(ptr % 4, 0, "hint read address not aligned to 4 bytes"); 47 | // Iterate through the vec in 4-byte chunks 48 | for i in (0..len).step_by(4) { 49 | // Get each byte in the chunk 50 | let b1 = vec[i as usize]; 51 | // In case the vec is not a multiple of 4, right-pad with 0s. This is fine because we 52 | // are assuming the word is uninitialized, so filling it with 0s makes sense. 53 | let b2 = vec.get(i as usize + 1).copied().unwrap_or(0); 54 | let b3 = vec.get(i as usize + 2).copied().unwrap_or(0); 55 | let b4 = vec.get(i as usize + 3).copied().unwrap_or(0); 56 | let word = u32::from_le_bytes([b1, b2, b3, b4]); 57 | 58 | // Save the data into runtime state so the runtime will use the desired data instead of 59 | // 0 when first reading/writing from this address. 60 | ctx.rt 61 | .uninitialized_memory_checkpoint 62 | .entry(ptr + i) 63 | .or_insert_with(|| false); 64 | ctx.rt 65 | .state 66 | .uninitialized_memory 67 | .entry(ptr + i) 68 | .and_modify(|_| panic!("hint read address is initialized already")) 69 | .or_insert(word); 70 | } 71 | None 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/mod.rs: -------------------------------------------------------------------------------- 1 | //! Syscall definitions & implementations for the [`crate::Executor`]. 2 | 3 | mod code; 4 | mod commit; 5 | mod context; 6 | mod deferred; 7 | mod halt; 8 | mod hint; 9 | mod precompiles; 10 | mod unconstrained; 11 | mod verify; 12 | mod write; 13 | 14 | use std::sync::Arc; 15 | 16 | use commit::CommitSyscall; 17 | use deferred::CommitDeferredSyscall; 18 | use halt::HaltSyscall; 19 | use hashbrown::HashMap; 20 | 21 | pub use code::*; 22 | pub use context::*; 23 | use hint::{HintLenSyscall, HintReadSyscall}; 24 | use precompiles::{ 25 | edwards::{add::EdwardsAddAssignSyscall, decompress::EdwardsDecompressSyscall}, 26 | fptower::{Fp2AddSubSyscall, Fp2MulSyscall, FpOpSyscall}, 27 | keccak256::permute::Keccak256PermuteSyscall, 28 | sha256::{compress::Sha256CompressSyscall, extend::Sha256ExtendSyscall}, 29 | uint256::Uint256MulSyscall, 30 | weierstrass::{ 31 | add::WeierstrassAddAssignSyscall, decompress::WeierstrassDecompressSyscall, 32 | double::WeierstrassDoubleAssignSyscall, 33 | }, 34 | }; 35 | 36 | use sp1_curves::{ 37 | edwards::ed25519::{Ed25519, Ed25519Parameters}, 38 | weierstrass::{ 39 | bls12_381::{Bls12381, Bls12381BaseField}, 40 | bn254::{Bn254, Bn254BaseField}, 41 | secp256k1::Secp256k1, 42 | }, 43 | }; 44 | use unconstrained::{EnterUnconstrainedSyscall, ExitUnconstrainedSyscall}; 45 | use verify::VerifySyscall; 46 | use write::WriteSyscall; 47 | 48 | use crate::events::FieldOperation; 49 | 50 | /// A system call in the SP1 RISC-V zkVM. 51 | /// 52 | /// This trait implements methods needed to execute a system call inside the [`crate::Executor`]. 53 | pub trait Syscall: Send + Sync { 54 | /// Executes the syscall. 55 | /// 56 | /// Returns the resulting value of register a0. `arg1` and `arg2` are the values in registers 57 | /// X10 and X11, respectively. While not a hard requirement, the convention is that the return 58 | /// value is only for system calls such as `HALT`. Most precompiles use `arg1` and `arg2` to 59 | /// denote the addresses of the input data, and write the result to the memory at `arg1`. 60 | fn execute( 61 | &self, 62 | ctx: &mut SyscallContext, 63 | syscall_code: SyscallCode, 64 | arg1: u32, 65 | arg2: u32, 66 | ) -> Option; 67 | 68 | /// The number of extra cycles that the syscall takes to execute. 69 | /// 70 | /// Unless this syscall is complex and requires many cycles, this should be zero. 71 | fn num_extra_cycles(&self) -> u32 { 72 | 0 73 | } 74 | } 75 | 76 | /// Creates the default syscall map. 77 | #[must_use] 78 | #[allow(clippy::too_many_lines)] 79 | pub fn default_syscall_map() -> HashMap> { 80 | let mut syscall_map = HashMap::>::default(); 81 | 82 | syscall_map.insert(SyscallCode::HALT, Arc::new(HaltSyscall)); 83 | 84 | syscall_map.insert(SyscallCode::SHA_EXTEND, Arc::new(Sha256ExtendSyscall)); 85 | 86 | syscall_map.insert(SyscallCode::SHA_COMPRESS, Arc::new(Sha256CompressSyscall)); 87 | 88 | syscall_map.insert( 89 | SyscallCode::ED_ADD, 90 | Arc::new(EdwardsAddAssignSyscall::::new()), 91 | ); 92 | 93 | syscall_map.insert( 94 | SyscallCode::ED_DECOMPRESS, 95 | Arc::new(EdwardsDecompressSyscall::::new()), 96 | ); 97 | 98 | syscall_map.insert( 99 | SyscallCode::KECCAK_PERMUTE, 100 | Arc::new(Keccak256PermuteSyscall), 101 | ); 102 | 103 | syscall_map.insert( 104 | SyscallCode::SECP256K1_ADD, 105 | Arc::new(WeierstrassAddAssignSyscall::::new()), 106 | ); 107 | 108 | syscall_map.insert( 109 | SyscallCode::SECP256K1_DOUBLE, 110 | Arc::new(WeierstrassDoubleAssignSyscall::::new()), 111 | ); 112 | 113 | syscall_map.insert( 114 | SyscallCode::SECP256K1_DECOMPRESS, 115 | Arc::new(WeierstrassDecompressSyscall::::new()), 116 | ); 117 | 118 | syscall_map.insert( 119 | SyscallCode::BN254_ADD, 120 | Arc::new(WeierstrassAddAssignSyscall::::new()), 121 | ); 122 | 123 | syscall_map.insert( 124 | SyscallCode::BN254_DOUBLE, 125 | Arc::new(WeierstrassDoubleAssignSyscall::::new()), 126 | ); 127 | 128 | syscall_map.insert( 129 | SyscallCode::BLS12381_ADD, 130 | Arc::new(WeierstrassAddAssignSyscall::::new()), 131 | ); 132 | 133 | syscall_map.insert( 134 | SyscallCode::BLS12381_DOUBLE, 135 | Arc::new(WeierstrassDoubleAssignSyscall::::new()), 136 | ); 137 | 138 | syscall_map.insert(SyscallCode::UINT256_MUL, Arc::new(Uint256MulSyscall)); 139 | 140 | syscall_map.insert( 141 | SyscallCode::BLS12381_FP_ADD, 142 | Arc::new(FpOpSyscall::::new(FieldOperation::Add)), 143 | ); 144 | 145 | syscall_map.insert( 146 | SyscallCode::BLS12381_FP_SUB, 147 | Arc::new(FpOpSyscall::::new(FieldOperation::Sub)), 148 | ); 149 | 150 | syscall_map.insert( 151 | SyscallCode::BLS12381_FP_MUL, 152 | Arc::new(FpOpSyscall::::new(FieldOperation::Mul)), 153 | ); 154 | 155 | syscall_map.insert( 156 | SyscallCode::BLS12381_FP2_ADD, 157 | Arc::new(Fp2AddSubSyscall::::new( 158 | FieldOperation::Add, 159 | )), 160 | ); 161 | 162 | syscall_map.insert( 163 | SyscallCode::BLS12381_FP2_SUB, 164 | Arc::new(Fp2AddSubSyscall::::new( 165 | FieldOperation::Sub, 166 | )), 167 | ); 168 | 169 | syscall_map.insert( 170 | SyscallCode::BLS12381_FP2_MUL, 171 | Arc::new(Fp2MulSyscall::::new()), 172 | ); 173 | 174 | syscall_map.insert( 175 | SyscallCode::BN254_FP_ADD, 176 | Arc::new(FpOpSyscall::::new(FieldOperation::Add)), 177 | ); 178 | 179 | syscall_map.insert( 180 | SyscallCode::BN254_FP_SUB, 181 | Arc::new(FpOpSyscall::::new(FieldOperation::Sub)), 182 | ); 183 | 184 | syscall_map.insert( 185 | SyscallCode::BN254_FP_MUL, 186 | Arc::new(FpOpSyscall::::new(FieldOperation::Mul)), 187 | ); 188 | 189 | syscall_map.insert( 190 | SyscallCode::BN254_FP2_ADD, 191 | Arc::new(Fp2AddSubSyscall::::new(FieldOperation::Add)), 192 | ); 193 | 194 | syscall_map.insert( 195 | SyscallCode::BN254_FP2_SUB, 196 | Arc::new(Fp2AddSubSyscall::::new(FieldOperation::Sub)), 197 | ); 198 | 199 | syscall_map.insert( 200 | SyscallCode::BN254_FP2_MUL, 201 | Arc::new(Fp2MulSyscall::::new()), 202 | ); 203 | 204 | syscall_map.insert( 205 | SyscallCode::ENTER_UNCONSTRAINED, 206 | Arc::new(EnterUnconstrainedSyscall), 207 | ); 208 | 209 | syscall_map.insert( 210 | SyscallCode::EXIT_UNCONSTRAINED, 211 | Arc::new(ExitUnconstrainedSyscall), 212 | ); 213 | 214 | syscall_map.insert(SyscallCode::WRITE, Arc::new(WriteSyscall)); 215 | 216 | syscall_map.insert(SyscallCode::COMMIT, Arc::new(CommitSyscall)); 217 | 218 | syscall_map.insert( 219 | SyscallCode::COMMIT_DEFERRED_PROOFS, 220 | Arc::new(CommitDeferredSyscall), 221 | ); 222 | 223 | syscall_map.insert(SyscallCode::VERIFY_SP1_PROOF, Arc::new(VerifySyscall)); 224 | 225 | syscall_map.insert(SyscallCode::HINT_LEN, Arc::new(HintLenSyscall)); 226 | 227 | syscall_map.insert(SyscallCode::HINT_READ, Arc::new(HintReadSyscall)); 228 | 229 | syscall_map.insert( 230 | SyscallCode::BLS12381_DECOMPRESS, 231 | Arc::new(WeierstrassDecompressSyscall::::new()), 232 | ); 233 | 234 | syscall_map 235 | } 236 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/edwards/add.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use sp1_curves::{edwards::EdwardsParameters, EllipticCurve}; 4 | 5 | use crate::{ 6 | events::create_ec_add_event, 7 | syscalls::{Syscall, SyscallCode, SyscallContext}, 8 | }; 9 | 10 | pub(crate) struct EdwardsAddAssignSyscall { 11 | _phantom: PhantomData, 12 | } 13 | 14 | impl EdwardsAddAssignSyscall { 15 | /// Create a new instance of the [`EdwardsAddAssignSyscall`]. 16 | pub const fn new() -> Self { 17 | Self { 18 | _phantom: PhantomData, 19 | } 20 | } 21 | } 22 | 23 | impl Syscall for EdwardsAddAssignSyscall { 24 | fn num_extra_cycles(&self) -> u32 { 25 | 1 26 | } 27 | 28 | fn execute( 29 | &self, 30 | rt: &mut SyscallContext, 31 | _: SyscallCode, 32 | arg1: u32, 33 | arg2: u32, 34 | ) -> Option { 35 | let _ = create_ec_add_event::(rt, arg1, arg2); 36 | None 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/edwards/decompress.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use sp1_curves::{ 4 | curve25519_dalek::CompressedEdwardsY, 5 | edwards::{ed25519::decompress, EdwardsParameters, WORDS_FIELD_ELEMENT}, 6 | COMPRESSED_POINT_BYTES, 7 | }; 8 | use sp1_primitives::consts::{bytes_to_words_le, words_to_bytes_le}; 9 | 10 | use crate::{ 11 | events::{EdDecompressEvent, MemoryReadRecord, MemoryWriteRecord}, 12 | syscalls::{Syscall, SyscallCode, SyscallContext}, 13 | }; 14 | 15 | pub(crate) struct EdwardsDecompressSyscall { 16 | _phantom: PhantomData, 17 | } 18 | 19 | impl EdwardsDecompressSyscall { 20 | /// Create a new instance of the [`EdwardsDecompressSyscall`]. 21 | pub const fn new() -> Self { 22 | Self { 23 | _phantom: PhantomData, 24 | } 25 | } 26 | } 27 | 28 | impl Syscall for EdwardsDecompressSyscall { 29 | fn execute( 30 | &self, 31 | rt: &mut SyscallContext, 32 | _: SyscallCode, 33 | arg1: u32, 34 | sign: u32, 35 | ) -> Option { 36 | let start_clk = rt.clk; 37 | let slice_ptr = arg1; 38 | assert!(slice_ptr % 4 == 0, "Pointer must be 4-byte aligned."); 39 | assert!(sign <= 1, "Sign bit must be 0 or 1."); 40 | 41 | let (y_memory_records_vec, y_vec) = rt.mr_slice( 42 | slice_ptr + (COMPRESSED_POINT_BYTES as u32), 43 | WORDS_FIELD_ELEMENT, 44 | ); 45 | let y_memory_records: [MemoryReadRecord; 8] = y_memory_records_vec.try_into().unwrap(); 46 | 47 | let sign_bool = sign != 0; 48 | 49 | let y_bytes: [u8; COMPRESSED_POINT_BYTES] = words_to_bytes_le(&y_vec); 50 | 51 | // Copy bytes into another array so we can modify the last byte and make CompressedEdwardsY, 52 | // which we'll use to compute the expected X. 53 | // Re-insert sign bit into last bit of Y for CompressedEdwardsY format 54 | let mut compressed_edwards_y: [u8; COMPRESSED_POINT_BYTES] = y_bytes; 55 | compressed_edwards_y[compressed_edwards_y.len() - 1] &= 0b0111_1111; 56 | compressed_edwards_y[compressed_edwards_y.len() - 1] |= (sign as u8) << 7; 57 | 58 | // Compute actual decompressed X 59 | let compressed_y = CompressedEdwardsY(compressed_edwards_y); 60 | let decompressed = decompress(&compressed_y); 61 | 62 | let mut decompressed_x_bytes = decompressed.x.to_bytes_le(); 63 | decompressed_x_bytes.resize(32, 0u8); 64 | let decompressed_x_words: [u32; WORDS_FIELD_ELEMENT] = 65 | bytes_to_words_le(&decompressed_x_bytes); 66 | 67 | // Write decompressed X into slice 68 | let x_memory_records_vec = rt.mw_slice(slice_ptr, &decompressed_x_words); 69 | let x_memory_records: [MemoryWriteRecord; 8] = x_memory_records_vec.try_into().unwrap(); 70 | 71 | let lookup_id = rt.syscall_lookup_id; 72 | let shard = rt.current_shard(); 73 | let _ = EdDecompressEvent { 74 | lookup_id, 75 | shard, 76 | clk: start_clk, 77 | ptr: slice_ptr, 78 | sign: sign_bool, 79 | y_bytes, 80 | decompressed_x_bytes: decompressed_x_bytes.try_into().unwrap(), 81 | x_memory_records, 82 | y_memory_records, 83 | local_mem_access: rt.postprocess(), 84 | }; 85 | None 86 | } 87 | 88 | fn num_extra_cycles(&self) -> u32 { 89 | 0 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/edwards/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod add; 2 | pub mod decompress; 3 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/fptower/fp.rs: -------------------------------------------------------------------------------- 1 | use num::BigUint; 2 | use sp1_curves::{params::NumWords, weierstrass::FpOpField}; 3 | use std::marker::PhantomData; 4 | use typenum::Unsigned; 5 | 6 | use crate::{ 7 | events::{FieldOperation, FpOpEvent}, 8 | syscalls::{Syscall, SyscallCode, SyscallContext}, 9 | }; 10 | 11 | pub struct FpOpSyscall

{ 12 | op: FieldOperation, 13 | _marker: PhantomData

, 14 | } 15 | 16 | impl

FpOpSyscall

{ 17 | pub const fn new(op: FieldOperation) -> Self { 18 | Self { 19 | op, 20 | _marker: PhantomData, 21 | } 22 | } 23 | } 24 | 25 | impl Syscall for FpOpSyscall

{ 26 | fn execute( 27 | &self, 28 | rt: &mut SyscallContext, 29 | _: SyscallCode, 30 | arg1: u32, 31 | arg2: u32, 32 | ) -> Option { 33 | let clk = rt.clk; 34 | let x_ptr = arg1; 35 | if x_ptr % 4 != 0 { 36 | panic!(); 37 | } 38 | let y_ptr = arg2; 39 | if y_ptr % 4 != 0 { 40 | panic!(); 41 | } 42 | 43 | let num_words =

::WordsFieldElement::USIZE; 44 | 45 | let x = rt.slice_unsafe(x_ptr, num_words); 46 | let (y_memory_records, y) = rt.mr_slice(y_ptr, num_words); 47 | 48 | let modulus = &BigUint::from_bytes_le(P::MODULUS); 49 | let a = BigUint::from_slice(&x) % modulus; 50 | let b = BigUint::from_slice(&y) % modulus; 51 | 52 | let result = match self.op { 53 | FieldOperation::Add => (a + b) % modulus, 54 | FieldOperation::Sub => ((a + modulus) - b) % modulus, 55 | FieldOperation::Mul => (a * b) % modulus, 56 | _ => panic!("Unsupported operation"), 57 | }; 58 | let mut result = result.to_u32_digits(); 59 | result.resize(num_words, 0); 60 | 61 | rt.clk += 1; 62 | let x_memory_records = rt.mw_slice(x_ptr, &result); 63 | 64 | let lookup_id = rt.syscall_lookup_id; 65 | let shard = rt.current_shard(); 66 | let _ = FpOpEvent { 67 | lookup_id, 68 | shard, 69 | clk, 70 | x_ptr, 71 | x, 72 | y_ptr, 73 | y, 74 | op: self.op, 75 | x_memory_records, 76 | y_memory_records, 77 | local_mem_access: rt.postprocess(), 78 | }; 79 | 80 | // Since all the Fp events are on the same table, we need to preserve the ordering of the 81 | // events b/c of the nonce. In this table's trace_gen, the nonce is simply the row number. 82 | // Group all of the events for a specific curve into the same syscall code key. 83 | // TODO: FIX THIS. 84 | 85 | None 86 | } 87 | 88 | fn num_extra_cycles(&self) -> u32 { 89 | 1 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/fptower/fp2_addsub.rs: -------------------------------------------------------------------------------- 1 | use num::BigUint; 2 | use sp1_curves::{ 3 | params::NumWords, 4 | weierstrass::{FieldType, FpOpField}, 5 | }; 6 | use std::marker::PhantomData; 7 | use typenum::Unsigned; 8 | 9 | use crate::{ 10 | events::{FieldOperation, Fp2AddSubEvent}, 11 | syscalls::{Syscall, SyscallCode, SyscallContext}, 12 | }; 13 | 14 | pub struct Fp2AddSubSyscall

{ 15 | op: FieldOperation, 16 | _marker: PhantomData

, 17 | } 18 | 19 | impl

Fp2AddSubSyscall

{ 20 | pub const fn new(op: FieldOperation) -> Self { 21 | Self { 22 | op, 23 | _marker: PhantomData, 24 | } 25 | } 26 | } 27 | 28 | impl Syscall for Fp2AddSubSyscall

{ 29 | fn execute( 30 | &self, 31 | rt: &mut SyscallContext, 32 | syscall_code: SyscallCode, 33 | arg1: u32, 34 | arg2: u32, 35 | ) -> Option { 36 | let clk = rt.clk; 37 | let x_ptr = arg1; 38 | if x_ptr % 4 != 0 { 39 | panic!(); 40 | } 41 | let y_ptr = arg2; 42 | if y_ptr % 4 != 0 { 43 | panic!(); 44 | } 45 | 46 | let num_words =

::WordsCurvePoint::USIZE; 47 | 48 | let x = rt.slice_unsafe(x_ptr, num_words); 49 | let (y_memory_records, y) = rt.mr_slice(y_ptr, num_words); 50 | rt.clk += 1; 51 | 52 | let (ac0, ac1) = x.split_at(x.len() / 2); 53 | let (bc0, bc1) = y.split_at(y.len() / 2); 54 | 55 | let ac0 = &BigUint::from_slice(ac0); 56 | let ac1 = &BigUint::from_slice(ac1); 57 | let bc0 = &BigUint::from_slice(bc0); 58 | let bc1 = &BigUint::from_slice(bc1); 59 | let modulus = &BigUint::from_bytes_le(P::MODULUS); 60 | 61 | let (c0, c1) = match self.op { 62 | FieldOperation::Add => ((ac0 + bc0) % modulus, (ac1 + bc1) % modulus), 63 | FieldOperation::Sub => ( 64 | (ac0 + modulus - bc0) % modulus, 65 | (ac1 + modulus - bc1) % modulus, 66 | ), 67 | _ => panic!("Invalid operation"), 68 | }; 69 | 70 | let mut result = c0 71 | .to_u32_digits() 72 | .into_iter() 73 | .chain(c1.to_u32_digits()) 74 | .collect::>(); 75 | 76 | result.resize(num_words, 0); 77 | let x_memory_records = rt.mw_slice(x_ptr, &result); 78 | 79 | let lookup_id = rt.syscall_lookup_id; 80 | let shard = rt.current_shard(); 81 | let op = self.op; 82 | let event = Fp2AddSubEvent { 83 | lookup_id, 84 | shard, 85 | clk, 86 | op, 87 | x_ptr, 88 | x, 89 | y_ptr, 90 | y, 91 | x_memory_records, 92 | y_memory_records, 93 | local_mem_access: rt.postprocess(), 94 | }; 95 | match P::FIELD_TYPE { 96 | // All the fp2 add and sub events for a given curve are coalesced to the curve's fp2 add operation. Only check for 97 | // that operation. 98 | // TODO: Fix this. 99 | FieldType::Bn254 => { 100 | let syscall_code_key = match syscall_code { 101 | SyscallCode::BN254_FP2_ADD | SyscallCode::BN254_FP2_SUB => { 102 | SyscallCode::BN254_FP2_ADD 103 | } 104 | _ => unreachable!(), 105 | }; 106 | 107 | // let syscall_event = rt.rt.syscall_event( 108 | // clk, 109 | // syscall_code.syscall_id(), 110 | // arg1, 111 | // arg2, 112 | // event.lookup_id, 113 | // ); 114 | // rt.record_mut().add_precompile_event( 115 | // syscall_code_key, 116 | // syscall_event, 117 | // PrecompileEvent::Bn254Fp2AddSub(event), 118 | // ); 119 | } 120 | FieldType::Bls12381 => { 121 | let syscall_code_key = match syscall_code { 122 | SyscallCode::BLS12381_FP2_ADD | SyscallCode::BLS12381_FP2_SUB => { 123 | SyscallCode::BLS12381_FP2_ADD 124 | } 125 | _ => unreachable!(), 126 | }; 127 | 128 | // let syscall_event = rt.rt.syscall_event( 129 | // clk, 130 | // syscall_code.syscall_id(), 131 | // arg1, 132 | // arg2, 133 | // event.lookup_id, 134 | // ); 135 | // rt.record_mut().add_precompile_event( 136 | // syscall_code_key, 137 | // syscall_event, 138 | // PrecompileEvent::Bls12381Fp2AddSub(event), 139 | // ); 140 | } 141 | } 142 | None 143 | } 144 | 145 | fn num_extra_cycles(&self) -> u32 { 146 | 1 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/fptower/fp2_mul.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use num::BigUint; 4 | use sp1_curves::{params::NumWords, weierstrass::FpOpField}; 5 | use typenum::Unsigned; 6 | 7 | use crate::{ 8 | events::Fp2MulEvent, 9 | syscalls::{Syscall, SyscallCode, SyscallContext}, 10 | }; 11 | 12 | pub struct Fp2MulSyscall

{ 13 | _marker: PhantomData

, 14 | } 15 | 16 | impl

Fp2MulSyscall

{ 17 | pub const fn new() -> Self { 18 | Self { 19 | _marker: PhantomData, 20 | } 21 | } 22 | } 23 | 24 | impl Syscall for Fp2MulSyscall

{ 25 | fn execute( 26 | &self, 27 | rt: &mut SyscallContext, 28 | syscall_code: SyscallCode, 29 | arg1: u32, 30 | arg2: u32, 31 | ) -> Option { 32 | let clk = rt.clk; 33 | let x_ptr = arg1; 34 | if x_ptr % 4 != 0 { 35 | panic!(); 36 | } 37 | let y_ptr = arg2; 38 | if y_ptr % 4 != 0 { 39 | panic!(); 40 | } 41 | 42 | let num_words =

::WordsCurvePoint::USIZE; 43 | 44 | let x = rt.slice_unsafe(x_ptr, num_words); 45 | let (y_memory_records, y) = rt.mr_slice(y_ptr, num_words); 46 | rt.clk += 1; 47 | 48 | let (ac0, ac1) = x.split_at(x.len() / 2); 49 | let (bc0, bc1) = y.split_at(y.len() / 2); 50 | 51 | let ac0 = &BigUint::from_slice(ac0); 52 | let ac1 = &BigUint::from_slice(ac1); 53 | let bc0 = &BigUint::from_slice(bc0); 54 | let bc1 = &BigUint::from_slice(bc1); 55 | let modulus = &BigUint::from_bytes_le(P::MODULUS); 56 | 57 | #[allow(clippy::match_bool)] 58 | let c0 = match (ac0 * bc0) % modulus < (ac1 * bc1) % modulus { 59 | true => ((modulus + (ac0 * bc0) % modulus) - (ac1 * bc1) % modulus) % modulus, 60 | false => ((ac0 * bc0) % modulus - (ac1 * bc1) % modulus) % modulus, 61 | }; 62 | let c1 = ((ac0 * bc1) % modulus + (ac1 * bc0) % modulus) % modulus; 63 | 64 | let mut result = c0 65 | .to_u32_digits() 66 | .into_iter() 67 | .chain(c1.to_u32_digits()) 68 | .collect::>(); 69 | 70 | result.resize(num_words, 0); 71 | let x_memory_records = rt.mw_slice(x_ptr, &result); 72 | 73 | let lookup_id = rt.syscall_lookup_id; 74 | let shard = rt.current_shard(); 75 | let event = Fp2MulEvent { 76 | lookup_id, 77 | shard, 78 | clk, 79 | x_ptr, 80 | x, 81 | y_ptr, 82 | y, 83 | x_memory_records, 84 | y_memory_records, 85 | local_mem_access: rt.postprocess(), 86 | }; 87 | // let syscall_event = 88 | // rt.rt.syscall_event(clk, syscall_code.syscall_id(), arg1, arg2, event.lookup_id); 89 | // match P::FIELD_TYPE { 90 | // FieldType::Bn254 => rt.record_mut().add_precompile_event( 91 | // syscall_code, 92 | // syscall_event, 93 | // PrecompileEvent::Bn254Fp2Mul(event), 94 | // ), 95 | // FieldType::Bls12381 => rt.record_mut().add_precompile_event( 96 | // syscall_code, 97 | // syscall_event, 98 | // PrecompileEvent::Bls12381Fp2Mul(event), 99 | // ), 100 | // }; 101 | None 102 | } 103 | 104 | fn num_extra_cycles(&self) -> u32 { 105 | 1 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/fptower/mod.rs: -------------------------------------------------------------------------------- 1 | mod fp; 2 | mod fp2_addsub; 3 | mod fp2_mul; 4 | 5 | pub use fp::*; 6 | pub use fp2_addsub::*; 7 | pub use fp2_mul::*; 8 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/keccak256/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod permute; 2 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/keccak256/permute.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | events::{KeccakPermuteEvent, PrecompileEvent}, 3 | syscalls::{Syscall, SyscallCode, SyscallContext}, 4 | }; 5 | 6 | use tiny_keccak::keccakf; 7 | 8 | pub(crate) const STATE_SIZE: usize = 25; 9 | 10 | // The permutation state is 25 u64's. Our word size is 32 bits, so it is 50 words. 11 | pub const STATE_NUM_WORDS: usize = STATE_SIZE * 2; 12 | 13 | pub(crate) struct Keccak256PermuteSyscall; 14 | 15 | impl Syscall for Keccak256PermuteSyscall { 16 | fn num_extra_cycles(&self) -> u32 { 17 | 1 18 | } 19 | 20 | fn execute( 21 | &self, 22 | rt: &mut SyscallContext, 23 | syscall_code: SyscallCode, 24 | arg1: u32, 25 | arg2: u32, 26 | ) -> Option { 27 | let start_clk = rt.clk; 28 | let state_ptr = arg1; 29 | if arg2 != 0 { 30 | panic!("Expected arg2 to be 0, got {arg2}"); 31 | } 32 | 33 | let mut state_read_records = Vec::new(); 34 | let mut state_write_records = Vec::new(); 35 | 36 | let mut state = Vec::new(); 37 | 38 | let (state_records, state_values) = rt.mr_slice(state_ptr, STATE_NUM_WORDS); 39 | state_read_records.extend_from_slice(&state_records); 40 | 41 | for values in state_values.chunks_exact(2) { 42 | let least_sig = values[0]; 43 | let most_sig = values[1]; 44 | state.push(least_sig as u64 + ((most_sig as u64) << 32)); 45 | } 46 | 47 | let saved_state = state.clone(); 48 | 49 | let mut state = state.try_into().unwrap(); 50 | keccakf(&mut state); 51 | 52 | // Increment the clk by 1 before writing because we read from memory at start_clk. 53 | rt.clk += 1; 54 | let mut values_to_write = Vec::new(); 55 | for i in 0..STATE_SIZE { 56 | let most_sig = ((state[i] >> 32) & 0xFFFFFFFF) as u32; 57 | let least_sig = (state[i] & 0xFFFFFFFF) as u32; 58 | values_to_write.push(least_sig); 59 | values_to_write.push(most_sig); 60 | } 61 | 62 | let write_records = rt.mw_slice(state_ptr, values_to_write.as_slice()); 63 | state_write_records.extend_from_slice(&write_records); 64 | 65 | // Push the Keccak permute event. 66 | let shard = rt.current_shard(); 67 | let lookup_id = rt.syscall_lookup_id; 68 | let event = PrecompileEvent::KeccakPermute(KeccakPermuteEvent { 69 | lookup_id, 70 | shard, 71 | clk: start_clk, 72 | pre_state: saved_state.as_slice().try_into().unwrap(), 73 | post_state: state.as_slice().try_into().unwrap(), 74 | state_read_records, 75 | state_write_records, 76 | state_addr: state_ptr, 77 | local_mem_access: rt.postprocess(), 78 | }); 79 | // let syscall_event = 80 | // rt.rt.syscall_event(start_clk, syscall_code.syscall_id(), arg1, arg2, lookup_id); 81 | // rt.record_mut().add_precompile_event(syscall_code, syscall_event, event); 82 | 83 | None 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_variables)] 2 | pub mod edwards; 3 | pub mod fptower; 4 | pub mod keccak256; 5 | pub mod sha256; 6 | pub mod uint256; 7 | pub mod weierstrass; 8 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/sha256/compress.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | events::{PrecompileEvent, ShaCompressEvent}, 3 | syscalls::{Syscall, SyscallCode, SyscallContext}, 4 | }; 5 | 6 | pub const SHA_COMPRESS_K: [u32; 64] = [ 7 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 8 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 9 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 10 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 11 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 12 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 13 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 14 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, 15 | ]; 16 | 17 | pub(crate) struct Sha256CompressSyscall; 18 | 19 | impl Syscall for Sha256CompressSyscall { 20 | fn num_extra_cycles(&self) -> u32 { 21 | 1 22 | } 23 | 24 | #[allow(clippy::too_many_lines)] 25 | #[allow(clippy::many_single_char_names)] 26 | fn execute( 27 | &self, 28 | rt: &mut SyscallContext, 29 | syscall_code: SyscallCode, 30 | arg1: u32, 31 | arg2: u32, 32 | ) -> Option { 33 | let w_ptr = arg1; 34 | let h_ptr = arg2; 35 | assert_ne!(w_ptr, h_ptr); 36 | 37 | let start_clk = rt.clk; 38 | let mut h_read_records = Vec::new(); 39 | let mut w_i_read_records = Vec::new(); 40 | let mut h_write_records = Vec::new(); 41 | 42 | // Execute the "initialize" phase where we read in the h values. 43 | let mut hx = [0u32; 8]; 44 | for i in 0..8 { 45 | let (record, value) = rt.mr(h_ptr + i as u32 * 4); 46 | h_read_records.push(record); 47 | hx[i] = value; 48 | } 49 | 50 | let mut original_w = Vec::new(); 51 | // Execute the "compress" phase. 52 | let mut a = hx[0]; 53 | let mut b = hx[1]; 54 | let mut c = hx[2]; 55 | let mut d = hx[3]; 56 | let mut e = hx[4]; 57 | let mut f = hx[5]; 58 | let mut g = hx[6]; 59 | let mut h = hx[7]; 60 | for i in 0..64 { 61 | let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25); 62 | let ch = (e & f) ^ (!e & g); 63 | let (record, w_i) = rt.mr(w_ptr + i * 4); 64 | original_w.push(w_i); 65 | w_i_read_records.push(record); 66 | let temp1 = h 67 | .wrapping_add(s1) 68 | .wrapping_add(ch) 69 | .wrapping_add(SHA_COMPRESS_K[i as usize]) 70 | .wrapping_add(w_i); 71 | let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22); 72 | let maj = (a & b) ^ (a & c) ^ (b & c); 73 | let temp2 = s0.wrapping_add(maj); 74 | 75 | h = g; 76 | g = f; 77 | f = e; 78 | e = d.wrapping_add(temp1); 79 | d = c; 80 | c = b; 81 | b = a; 82 | a = temp1.wrapping_add(temp2); 83 | } 84 | // Increment the clk by 1 before writing to h, since we've already read h at the start_clk 85 | // during the initialization phase. 86 | rt.clk += 1; 87 | 88 | // Execute the "finalize" phase. 89 | let v = [a, b, c, d, e, f, g, h]; 90 | for i in 0..8 { 91 | let record = rt.mw(h_ptr + i as u32 * 4, hx[i].wrapping_add(v[i])); 92 | h_write_records.push(record); 93 | } 94 | 95 | // Push the SHA extend event. 96 | let lookup_id = rt.syscall_lookup_id; 97 | let shard = rt.current_shard(); 98 | let event = PrecompileEvent::ShaCompress(ShaCompressEvent { 99 | lookup_id, 100 | shard, 101 | clk: start_clk, 102 | w_ptr, 103 | h_ptr, 104 | w: original_w, 105 | h: hx, 106 | h_read_records: h_read_records.try_into().unwrap(), 107 | w_i_read_records, 108 | h_write_records: h_write_records.try_into().unwrap(), 109 | local_mem_access: rt.postprocess(), 110 | }); 111 | // let syscall_event = 112 | // rt.rt.syscall_event(start_clk, syscall_code.syscall_id(), arg1, arg2, lookup_id); 113 | // rt.record_mut().add_precompile_event(syscall_code, syscall_event, event); 114 | 115 | None 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/sha256/extend.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | events::{PrecompileEvent, ShaExtendEvent}, 3 | syscalls::{Syscall, SyscallCode, SyscallContext}, 4 | }; 5 | 6 | pub(crate) struct Sha256ExtendSyscall; 7 | 8 | impl Syscall for Sha256ExtendSyscall { 9 | fn num_extra_cycles(&self) -> u32 { 10 | 48 11 | } 12 | 13 | fn execute( 14 | &self, 15 | rt: &mut SyscallContext, 16 | syscall_code: SyscallCode, 17 | arg1: u32, 18 | arg2: u32, 19 | ) -> Option { 20 | let clk_init = rt.clk; 21 | let w_ptr = arg1; 22 | assert!(arg2 == 0, "arg2 must be 0"); 23 | 24 | let w_ptr_init = w_ptr; 25 | let mut w_i_minus_15_reads = Vec::new(); 26 | let mut w_i_minus_2_reads = Vec::new(); 27 | let mut w_i_minus_16_reads = Vec::new(); 28 | let mut w_i_minus_7_reads = Vec::new(); 29 | let mut w_i_writes = Vec::new(); 30 | for i in 16..64 { 31 | // Read w[i-15]. 32 | let (record, w_i_minus_15) = rt.mr(w_ptr + (i - 15) * 4); 33 | w_i_minus_15_reads.push(record); 34 | 35 | // Compute `s0`. 36 | let s0 = 37 | w_i_minus_15.rotate_right(7) ^ w_i_minus_15.rotate_right(18) ^ (w_i_minus_15 >> 3); 38 | 39 | // Read w[i-2]. 40 | let (record, w_i_minus_2) = rt.mr(w_ptr + (i - 2) * 4); 41 | w_i_minus_2_reads.push(record); 42 | 43 | // Compute `s1`. 44 | let s1 = 45 | w_i_minus_2.rotate_right(17) ^ w_i_minus_2.rotate_right(19) ^ (w_i_minus_2 >> 10); 46 | 47 | // Read w[i-16]. 48 | let (record, w_i_minus_16) = rt.mr(w_ptr + (i - 16) * 4); 49 | w_i_minus_16_reads.push(record); 50 | 51 | // Read w[i-7]. 52 | let (record, w_i_minus_7) = rt.mr(w_ptr + (i - 7) * 4); 53 | w_i_minus_7_reads.push(record); 54 | 55 | // Compute `w_i`. 56 | let w_i = s1 57 | .wrapping_add(w_i_minus_16) 58 | .wrapping_add(s0) 59 | .wrapping_add(w_i_minus_7); 60 | 61 | // Write w[i]. 62 | w_i_writes.push(rt.mw(w_ptr + i * 4, w_i)); 63 | rt.clk += 1; 64 | } 65 | 66 | // Push the SHA extend event. 67 | let lookup_id = rt.syscall_lookup_id; 68 | let shard = rt.current_shard(); 69 | let event = PrecompileEvent::ShaExtend(ShaExtendEvent { 70 | lookup_id, 71 | shard, 72 | clk: clk_init, 73 | w_ptr: w_ptr_init, 74 | w_i_minus_15_reads, 75 | w_i_minus_2_reads, 76 | w_i_minus_16_reads, 77 | w_i_minus_7_reads, 78 | w_i_writes, 79 | local_mem_access: rt.postprocess(), 80 | }); 81 | // let syscall_event = 82 | // rt.rt.syscall_event(clk_init, syscall_code.syscall_id(), arg1, arg2, lookup_id); 83 | // rt.record_mut().add_precompile_event(syscall_code, syscall_event, event); 84 | 85 | None 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/sha256/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod compress; 2 | pub mod extend; 3 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/uint256.rs: -------------------------------------------------------------------------------- 1 | use num::{BigUint, One, Zero}; 2 | 3 | use sp1_curves::edwards::WORDS_FIELD_ELEMENT; 4 | use sp1_primitives::consts::{bytes_to_words_le, words_to_bytes_le_vec, WORD_SIZE}; 5 | 6 | use crate::{ 7 | events::{PrecompileEvent, Uint256MulEvent}, 8 | syscalls::{Syscall, SyscallCode, SyscallContext}, 9 | }; 10 | 11 | pub(crate) struct Uint256MulSyscall; 12 | 13 | impl Syscall for Uint256MulSyscall { 14 | fn execute( 15 | &self, 16 | rt: &mut SyscallContext, 17 | syscall_code: SyscallCode, 18 | arg1: u32, 19 | arg2: u32, 20 | ) -> Option { 21 | let clk = rt.clk; 22 | 23 | let x_ptr = arg1; 24 | if x_ptr % 4 != 0 { 25 | panic!(); 26 | } 27 | let y_ptr = arg2; 28 | if y_ptr % 4 != 0 { 29 | panic!(); 30 | } 31 | 32 | // First read the words for the x value. We can read a slice_unsafe here because we write 33 | // the computed result to x later. 34 | let x = rt.slice_unsafe(x_ptr, WORDS_FIELD_ELEMENT); 35 | 36 | // Read the y value. 37 | let (y_memory_records, y) = rt.mr_slice(y_ptr, WORDS_FIELD_ELEMENT); 38 | 39 | // The modulus is stored after the y value. We increment the pointer by the number of words. 40 | let modulus_ptr = y_ptr + WORDS_FIELD_ELEMENT as u32 * WORD_SIZE as u32; 41 | let (modulus_memory_records, modulus) = rt.mr_slice(modulus_ptr, WORDS_FIELD_ELEMENT); 42 | 43 | // Get the BigUint values for x, y, and the modulus. 44 | let uint256_x = BigUint::from_bytes_le(&words_to_bytes_le_vec(&x)); 45 | let uint256_y = BigUint::from_bytes_le(&words_to_bytes_le_vec(&y)); 46 | let uint256_modulus = BigUint::from_bytes_le(&words_to_bytes_le_vec(&modulus)); 47 | 48 | // Perform the multiplication and take the result modulo the modulus. 49 | let result: BigUint = if uint256_modulus.is_zero() { 50 | let modulus = BigUint::one() << 256; 51 | (uint256_x * uint256_y) % modulus 52 | } else { 53 | (uint256_x * uint256_y) % uint256_modulus 54 | }; 55 | 56 | let mut result_bytes = result.to_bytes_le(); 57 | result_bytes.resize(32, 0u8); // Pad the result to 32 bytes. 58 | 59 | // Convert the result to little endian u32 words. 60 | let result = bytes_to_words_le::<8>(&result_bytes); 61 | 62 | // Increment clk so that the write is not at the same cycle as the read. 63 | rt.clk += 1; 64 | // Write the result to x and keep track of the memory records. 65 | let x_memory_records = rt.mw_slice(x_ptr, &result); 66 | 67 | let lookup_id = rt.syscall_lookup_id; 68 | let shard = rt.current_shard(); 69 | let event = PrecompileEvent::Uint256Mul(Uint256MulEvent { 70 | lookup_id, 71 | shard, 72 | clk, 73 | x_ptr, 74 | x, 75 | y_ptr, 76 | y, 77 | modulus, 78 | x_memory_records, 79 | y_memory_records, 80 | modulus_memory_records, 81 | local_mem_access: rt.postprocess(), 82 | }); 83 | // let sycall_event = 84 | // rt.rt.syscall_event(clk, syscall_code.syscall_id(), arg1, arg2, lookup_id); 85 | // rt.record_mut().add_precompile_event(syscall_code, sycall_event, event); 86 | 87 | None 88 | } 89 | 90 | fn num_extra_cycles(&self) -> u32 { 91 | 1 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/weierstrass/add.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use sp1_curves::EllipticCurve; 4 | 5 | use crate::{ 6 | events::create_ec_add_event, 7 | syscalls::{Syscall, SyscallCode, SyscallContext}, 8 | }; 9 | 10 | pub(crate) struct WeierstrassAddAssignSyscall { 11 | _phantom: PhantomData, 12 | } 13 | 14 | impl WeierstrassAddAssignSyscall { 15 | /// Create a new instance of the [`WeierstrassAddAssignSyscall`]. 16 | pub const fn new() -> Self { 17 | Self { 18 | _phantom: PhantomData, 19 | } 20 | } 21 | } 22 | 23 | impl Syscall for WeierstrassAddAssignSyscall { 24 | fn execute( 25 | &self, 26 | rt: &mut SyscallContext, 27 | syscall_code: SyscallCode, 28 | arg1: u32, 29 | arg2: u32, 30 | ) -> Option { 31 | let event = create_ec_add_event::(rt, arg1, arg2); 32 | // let syscall_event = 33 | // rt.rt.syscall_event(event.clk, syscall_code.syscall_id(), arg1, arg2, event.lookup_id); 34 | // match E::CURVE_TYPE { 35 | // CurveType::Secp256k1 => rt.record_mut().add_precompile_event( 36 | // syscall_code, 37 | // syscall_event, 38 | // PrecompileEvent::Secp256k1Add(event), 39 | // ), 40 | // CurveType::Bn254 => { 41 | // rt.record_mut().add_precompile_event( 42 | // syscall_code, 43 | // syscall_event, 44 | // PrecompileEvent::Bn254Add(event), 45 | // ); 46 | // } 47 | // CurveType::Bls12381 => rt.record_mut().add_precompile_event( 48 | // syscall_code, 49 | // syscall_event, 50 | // PrecompileEvent::Bls12381Add(event), 51 | // ), 52 | // _ => panic!("Unsupported curve"), 53 | // } 54 | None 55 | } 56 | 57 | fn num_extra_cycles(&self) -> u32 { 58 | 1 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/weierstrass/decompress.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use sp1_curves::EllipticCurve; 4 | 5 | use crate::{ 6 | events::create_ec_decompress_event, 7 | syscalls::{Syscall, SyscallCode, SyscallContext}, 8 | }; 9 | 10 | pub(crate) struct WeierstrassDecompressSyscall { 11 | _phantom: std::marker::PhantomData, 12 | } 13 | 14 | impl WeierstrassDecompressSyscall { 15 | /// Create a new instance of the [`WeierstrassDecompressSyscall`]. 16 | pub const fn new() -> Self { 17 | Self { 18 | _phantom: PhantomData, 19 | } 20 | } 21 | } 22 | 23 | impl Syscall for WeierstrassDecompressSyscall { 24 | fn execute( 25 | &self, 26 | rt: &mut SyscallContext, 27 | syscall_code: SyscallCode, 28 | arg1: u32, 29 | arg2: u32, 30 | ) -> Option { 31 | let event = create_ec_decompress_event::(rt, arg1, arg2); 32 | // let syscall_event = 33 | // rt.rt.syscall_event(event.clk, syscall_code.syscall_id(), arg1, arg2, event.lookup_id); 34 | // match E::CURVE_TYPE { 35 | // CurveType::Secp256k1 => rt.record_mut().add_precompile_event( 36 | // syscall_code, 37 | // syscall_event, 38 | // PrecompileEvent::Secp256k1Decompress(event), 39 | // ), 40 | // CurveType::Bls12381 => rt.record_mut().add_precompile_event( 41 | // syscall_code, 42 | // syscall_event, 43 | // PrecompileEvent::Bls12381Decompress(event), 44 | // ), 45 | // _ => panic!("Unsupported curve"), 46 | // } 47 | None 48 | } 49 | 50 | fn num_extra_cycles(&self) -> u32 { 51 | 0 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/weierstrass/double.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use sp1_curves::EllipticCurve; 4 | 5 | use crate::{ 6 | events::create_ec_double_event, 7 | syscalls::{Syscall, SyscallCode, SyscallContext}, 8 | }; 9 | 10 | pub(crate) struct WeierstrassDoubleAssignSyscall { 11 | _phantom: std::marker::PhantomData, 12 | } 13 | 14 | impl WeierstrassDoubleAssignSyscall { 15 | /// Create a new instance of the [`WeierstrassDoubleAssignSyscall`]. 16 | pub const fn new() -> Self { 17 | Self { 18 | _phantom: PhantomData, 19 | } 20 | } 21 | } 22 | 23 | impl Syscall for WeierstrassDoubleAssignSyscall { 24 | fn execute( 25 | &self, 26 | rt: &mut SyscallContext, 27 | syscall_code: SyscallCode, 28 | arg1: u32, 29 | arg2: u32, 30 | ) -> Option { 31 | let event = create_ec_double_event::(rt, arg1, arg2); 32 | // let syscall_event = 33 | // rt.rt.syscall_event(event.clk, syscall_code.syscall_id(), arg1, arg2, event.lookup_id); 34 | // match E::CURVE_TYPE { 35 | // CurveType::Secp256k1 => { 36 | // rt.record_mut().add_precompile_event( 37 | // syscall_code, 38 | // syscall_event, 39 | // PrecompileEvent::Secp256k1Double(event), 40 | // ); 41 | // } 42 | // CurveType::Bn254 => { 43 | // rt.record_mut().add_precompile_event( 44 | // syscall_code, 45 | // syscall_event, 46 | // PrecompileEvent::Bn254Double(event), 47 | // ); 48 | // } 49 | // CurveType::Bls12381 => { 50 | // rt.record_mut().add_precompile_event( 51 | // syscall_code, 52 | // syscall_event, 53 | // PrecompileEvent::Bls12381Double(event), 54 | // ); 55 | // } 56 | // _ => panic!("Unsupported curve"), 57 | // } 58 | None 59 | } 60 | 61 | fn num_extra_cycles(&self) -> u32 { 62 | 0 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/precompiles/weierstrass/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod add; 2 | pub mod decompress; 3 | pub mod double; 4 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/unconstrained.rs: -------------------------------------------------------------------------------- 1 | use hashbrown::HashMap; 2 | 3 | use crate::{state::ForkState, ExecutorMode}; 4 | 5 | use super::{Syscall, SyscallCode, SyscallContext}; 6 | 7 | pub(crate) struct EnterUnconstrainedSyscall; 8 | 9 | impl Syscall for EnterUnconstrainedSyscall { 10 | fn execute(&self, ctx: &mut SyscallContext, _: SyscallCode, _: u32, _: u32) -> Option { 11 | if ctx.rt.unconstrained { 12 | panic!("Unconstrained block is already active."); 13 | } 14 | ctx.rt.unconstrained = true; 15 | ctx.rt.unconstrained_state = ForkState { 16 | global_clk: ctx.rt.state.global_clk, 17 | clk: ctx.rt.state.clk, 18 | pc: ctx.rt.state.pc, 19 | memory_diff: HashMap::default(), 20 | // record: std::mem::take(&mut ctx.rt.record), 21 | // op_record: std::mem::take(&mut ctx.rt.memory_accesses), 22 | executor_mode: ctx.rt.executor_mode, 23 | }; 24 | ctx.rt.executor_mode = ExecutorMode::Simple; 25 | Some(1) 26 | } 27 | } 28 | 29 | pub(crate) struct ExitUnconstrainedSyscall; 30 | 31 | impl Syscall for ExitUnconstrainedSyscall { 32 | fn execute(&self, ctx: &mut SyscallContext, _: SyscallCode, _: u32, _: u32) -> Option { 33 | // Reset the state of the runtime. 34 | if ctx.rt.unconstrained { 35 | ctx.rt.state.global_clk = ctx.rt.unconstrained_state.global_clk; 36 | ctx.rt.state.clk = ctx.rt.unconstrained_state.clk; 37 | ctx.rt.state.pc = ctx.rt.unconstrained_state.pc; 38 | ctx.next_pc = ctx.rt.state.pc.wrapping_add(4); 39 | for (addr, value) in ctx.rt.unconstrained_state.memory_diff.drain() { 40 | match value { 41 | Some(value) => { 42 | ctx.rt.state.memory.insert(addr, value); 43 | } 44 | None => { 45 | ctx.rt.state.memory.remove(&addr); 46 | } 47 | } 48 | } 49 | // ctx.rt.record = std::mem::take(&mut ctx.rt.unconstrained_state.record); 50 | // ctx.rt.memory_accesses = std::mem::take(&mut ctx.rt.unconstrained_state.op_record); 51 | ctx.rt.executor_mode = ctx.rt.unconstrained_state.executor_mode; 52 | ctx.rt.unconstrained = false; 53 | } 54 | ctx.rt.unconstrained_state = ForkState::default(); 55 | Some(0) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/verify.rs: -------------------------------------------------------------------------------- 1 | use super::{Syscall, SyscallCode, SyscallContext}; 2 | 3 | pub(crate) struct VerifySyscall; 4 | 5 | impl Syscall for VerifySyscall { 6 | #[allow(clippy::mut_mut)] 7 | fn execute(&self, _: &mut SyscallContext, _: SyscallCode, _: u32, _: u32) -> Option { 8 | None 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /crates/executor/src/syscalls/write.rs: -------------------------------------------------------------------------------- 1 | use sp1_primitives::consts::num_to_comma_separated; 2 | 3 | use crate::{Executor, Register}; 4 | 5 | use super::{Syscall, SyscallCode, SyscallContext}; 6 | 7 | pub(crate) struct WriteSyscall; 8 | 9 | impl Syscall for WriteSyscall { 10 | /// Handle writes to file descriptors during execution. 11 | /// 12 | /// If stdout (fd = 1): 13 | /// - If the stream is a cycle tracker, either log the cycle tracker or accumulate it in the 14 | /// report. 15 | /// - Else, print the stream to stdout. 16 | /// 17 | /// If stderr (fd = 2): 18 | /// - Print the stream to stderr. 19 | /// 20 | /// If fd = 3: 21 | /// - Update the public value stream. 22 | /// 23 | /// If fd = 4: 24 | /// - Update the input stream. 25 | /// 26 | /// If the fd matches a hook in the hook registry, invoke the hook. 27 | /// 28 | /// Else, log a warning. 29 | #[allow(clippy::pedantic)] 30 | fn execute( 31 | &self, 32 | ctx: &mut SyscallContext, 33 | _: SyscallCode, 34 | arg1: u32, 35 | arg2: u32, 36 | ) -> Option { 37 | let a2 = Register::X12; 38 | let rt = &mut ctx.rt; 39 | let fd = arg1; 40 | let write_buf = arg2; 41 | let nbytes = rt.register(a2); 42 | // Read nbytes from memory starting at write_buf. 43 | let bytes = (0..nbytes) 44 | .map(|i| rt.byte(write_buf + i)) 45 | .collect::>(); 46 | let slice = bytes.as_slice(); 47 | if fd == 1 { 48 | let s = core::str::from_utf8(slice).unwrap(); 49 | match parse_cycle_tracker_command(s) { 50 | Some(command) => handle_cycle_tracker_command(rt, command), 51 | None => { 52 | // If the string does not match any known command, print it to stdout. 53 | let flush_s = update_io_buf(ctx, fd, s); 54 | if !flush_s.is_empty() { 55 | flush_s 56 | .into_iter() 57 | .for_each(|_| {}); 58 | } 59 | } 60 | } 61 | } else if fd == 2 { 62 | let s = core::str::from_utf8(slice).unwrap(); 63 | let flush_s = update_io_buf(ctx, fd, s); 64 | if !flush_s.is_empty() { 65 | flush_s 66 | .into_iter() 67 | .for_each(|line| println!("stderr: {}", line)); 68 | } 69 | } else if fd == 3 { 70 | rt.state.public_values_stream.extend_from_slice(slice); 71 | } else if fd == 4 { 72 | rt.state.input_stream.push(slice.to_vec()); 73 | } else if let Some(mut hook) = rt.hook_registry.get(fd) { 74 | let res = hook.invoke_hook(rt.hook_env(), slice); 75 | // Add result vectors to the beginning of the stream. 76 | let ptr = rt.state.input_stream_ptr; 77 | rt.state.input_stream.splice(ptr..ptr, res); 78 | } else { 79 | tracing::warn!("tried to write to unknown file descriptor {fd}"); 80 | } 81 | None 82 | } 83 | } 84 | 85 | /// An enum representing the different cycle tracker commands. 86 | #[derive(Clone)] 87 | enum CycleTrackerCommand { 88 | Start(String), 89 | End(String), 90 | ReportStart(String), 91 | ReportEnd(()), 92 | } 93 | 94 | /// Parse a cycle tracker command from a string. If the string does not match any known command, 95 | /// returns None. 96 | fn parse_cycle_tracker_command(s: &str) -> Option { 97 | let (command, fn_name) = s.split_once(':')?; 98 | let trimmed_name = fn_name.trim().to_string(); 99 | 100 | match command { 101 | "cycle-tracker-start" => Some(CycleTrackerCommand::Start(trimmed_name)), 102 | "cycle-tracker-end" => Some(CycleTrackerCommand::End(trimmed_name)), 103 | "cycle-tracker-report-start" => Some(CycleTrackerCommand::ReportStart(trimmed_name)), 104 | "cycle-tracker-report-end" => Some(CycleTrackerCommand::ReportEnd(())), 105 | _ => None, 106 | } 107 | } 108 | 109 | /// Handle a cycle tracker command. 110 | fn handle_cycle_tracker_command(rt: &mut Executor, command: CycleTrackerCommand) { 111 | match command { 112 | CycleTrackerCommand::Start(name) | CycleTrackerCommand::ReportStart(name) => { 113 | start_cycle_tracker(rt, &name); 114 | } 115 | CycleTrackerCommand::End(name) => { 116 | end_cycle_tracker(rt, &name); 117 | } 118 | CycleTrackerCommand::ReportEnd(_) => { 119 | // Attempt to end the cycle tracker and accumulate the total cycles in the fn_name's 120 | // entry in the ExecutionReport. 121 | } 122 | } 123 | } 124 | 125 | /// Start tracking cycles for the given name at the specific depth and print out the log. 126 | fn start_cycle_tracker(rt: &mut Executor, name: &str) { 127 | let depth = rt.cycle_tracker.len() as u32; 128 | rt.cycle_tracker 129 | .insert(name.to_string(), (rt.state.global_clk, depth)); 130 | let padding = "│ ".repeat(depth as usize); 131 | log::info!("{}┌╴{}", padding, name); 132 | } 133 | 134 | /// End tracking cycles for the given name, print out the log, and return the total number of cycles 135 | /// in the span. If the name is not found in the cycle tracker cache, returns None. 136 | fn end_cycle_tracker(rt: &mut Executor, name: &str) -> Option { 137 | if let Some((start, depth)) = rt.cycle_tracker.remove(name) { 138 | let padding = "│ ".repeat(depth as usize); 139 | let total_cycles = rt.state.global_clk - start; 140 | log::info!( 141 | "{}└╴{} cycles", 142 | padding, 143 | num_to_comma_separated(total_cycles) 144 | ); 145 | return Some(total_cycles); 146 | } 147 | None 148 | } 149 | 150 | /// Update the io buffer for the given file descriptor with the given string. 151 | #[allow(clippy::mut_mut)] 152 | fn update_io_buf(ctx: &mut SyscallContext, fd: u32, s: &str) -> Vec { 153 | let rt = &mut ctx.rt; 154 | let entry = rt.io_buf.entry(fd).or_default(); 155 | entry.push_str(s); 156 | if entry.contains('\n') { 157 | // Return lines except for the last from buf. 158 | let prev_buf = std::mem::take(entry); 159 | let mut lines = prev_buf.split('\n').collect::>(); 160 | let last = lines.pop().unwrap_or(""); 161 | *entry = last.to_string(); 162 | lines 163 | .into_iter() 164 | .map(std::string::ToString::to_string) 165 | .collect::>() 166 | } else { 167 | vec![] 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /crates/executor/src/utils.rs: -------------------------------------------------------------------------------- 1 | use hashbrown::HashMap; 2 | 3 | use nohash_hasher::BuildNoHashHasher; 4 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 5 | 6 | use crate::Opcode; 7 | 8 | /// Serialize a `HashMap` as a `Vec<(u32, V)>`. 9 | pub fn serialize_hashmap_as_vec( 10 | map: &HashMap>, 11 | serializer: S, 12 | ) -> Result { 13 | Serialize::serialize(&map.iter().collect::>(), serializer) 14 | } 15 | 16 | /// Deserialize a `Vec<(u32, V)>` as a `HashMap`. 17 | pub fn deserialize_hashmap_as_vec<'de, V: Deserialize<'de>, D: Deserializer<'de>>( 18 | deserializer: D, 19 | ) -> Result>, D::Error> { 20 | let seq: Vec<(u32, V)> = Deserialize::deserialize(deserializer)?; 21 | Ok(seq.into_iter().collect()) 22 | } 23 | 24 | /// Returns `true` if the given `opcode` is a signed operation. 25 | #[must_use] 26 | pub fn is_signed_operation(opcode: Opcode) -> bool { 27 | opcode == Opcode::DIV || opcode == Opcode::REM 28 | } 29 | 30 | /// Calculate the correct `quotient` and `remainder` for the given `b` and `c` per RISC-V spec. 31 | #[must_use] 32 | pub fn get_quotient_and_remainder(b: u32, c: u32, opcode: Opcode) -> (u32, u32) { 33 | if c == 0 { 34 | // When c is 0, the quotient is 2^32 - 1 and the remainder is b regardless of whether we 35 | // perform signed or unsigned division. 36 | (u32::MAX, b) 37 | } else if is_signed_operation(opcode) { 38 | ( 39 | (b as i32).wrapping_div(c as i32) as u32, 40 | (b as i32).wrapping_rem(c as i32) as u32, 41 | ) 42 | } else { 43 | (b.wrapping_div(c), b.wrapping_rem(c)) 44 | } 45 | } 46 | 47 | /// Calculate the most significant bit of the given 32-bit integer `a`, and returns it as a u8. 48 | #[must_use] 49 | pub const fn get_msb(a: u32) -> u8 { 50 | ((a >> 31) & 1) as u8 51 | } 52 | -------------------------------------------------------------------------------- /crates/primitives/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [1.1.0](https://github.com/succinctlabs/sp1/compare/sp1-primitives-v1.0.1...sp1-primitives-v1.1.0) - 2024-08-02 11 | 12 | ### Added 13 | - update tg ([#1214](https://github.com/succinctlabs/sp1/pull/1214)) 14 | 15 | ## [1.0.0-rc1](https://github.com/succinctlabs/sp1/compare/sp1-primitives-v1.0.0-rc1...sp1-primitives-v1.0.0-rc1) - 2024-07-19 16 | 17 | ### Added 18 | 19 | - publish sp1 to crates.io ([#1052](https://github.com/succinctlabs/sp1/pull/1052)) 20 | - plonk circuit optimizations ([#972](https://github.com/succinctlabs/sp1/pull/972)) 21 | - enable arbitrary constraint degree ([#593](https://github.com/succinctlabs/sp1/pull/593)) 22 | - complete reduce program ([#565](https://github.com/succinctlabs/sp1/pull/565)) 23 | - nested sp1 proof verification ([#494](https://github.com/succinctlabs/sp1/pull/494)) 24 | - new README img ([#226](https://github.com/succinctlabs/sp1/pull/226)) 25 | - readme updates ([#205](https://github.com/succinctlabs/sp1/pull/205)) 26 | - more final touches ([#194](https://github.com/succinctlabs/sp1/pull/194)) 27 | - curtaup + release system + cargo prove CLI updates ([#178](https://github.com/succinctlabs/sp1/pull/178)) 28 | - (perf) updates from Plonky3 and verifier refactor ([#156](https://github.com/succinctlabs/sp1/pull/156)) 29 | - developer experience improvements ([#145](https://github.com/succinctlabs/sp1/pull/145)) 30 | - toolchain build from source & install ([#113](https://github.com/succinctlabs/sp1/pull/113)) 31 | - io::read io::write ([#126](https://github.com/succinctlabs/sp1/pull/126)) 32 | - tracing, profiling, benchmarking ([#99](https://github.com/succinctlabs/sp1/pull/99)) 33 | 34 | ### Fixed 35 | 36 | - deferred proofs + cleanup hash_vkey ([#615](https://github.com/succinctlabs/sp1/pull/615)) 37 | 38 | ### Other 39 | 40 | - use global workspace version ([#1102](https://github.com/succinctlabs/sp1/pull/1102)) 41 | - fix release-plz ([#1088](https://github.com/succinctlabs/sp1/pull/1088)) 42 | - add release-plz ([#1086](https://github.com/succinctlabs/sp1/pull/1086)) 43 | - _(deps)_ bump lazy_static from 1.4.0 to 1.5.0 44 | - _(deps)_ bump itertools from 0.12.1 to 0.13.0 45 | - Clean up TOML files ([#796](https://github.com/succinctlabs/sp1/pull/796)) 46 | - prover tweaks ([#610](https://github.com/succinctlabs/sp1/pull/610)) 47 | - final touches for public release ([#239](https://github.com/succinctlabs/sp1/pull/239)) 48 | - update docs with slight nits ([#224](https://github.com/succinctlabs/sp1/pull/224)) 49 | - sp1 rename ([#212](https://github.com/succinctlabs/sp1/pull/212)) 50 | - enshrine AlignedBorrow macro ([#209](https://github.com/succinctlabs/sp1/pull/209)) 51 | - readme cleanup ([#196](https://github.com/succinctlabs/sp1/pull/196)) 52 | - rename succinct to curta ([#192](https://github.com/succinctlabs/sp1/pull/192)) 53 | - better curta graphic ([#184](https://github.com/succinctlabs/sp1/pull/184)) 54 | - Initial commit 55 | -------------------------------------------------------------------------------- /crates/primitives/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sp1-primitives" 3 | description = "SP1 is a performant, 100% open-source, contributor-friendly zkVM." 4 | readme = "../../README.md" 5 | version = { workspace = true } 6 | edition = { workspace = true } 7 | license = { workspace = true } 8 | repository = { workspace = true } 9 | keywords = { workspace = true } 10 | categories = { workspace = true } 11 | 12 | [dependencies] 13 | bincode = "1.3.3" 14 | hex = "0.4.3" 15 | lazy_static = "1.5.0" 16 | num-bigint = { version = "0.4.6", default-features = false } 17 | p3-field = { workspace = true } 18 | p3-baby-bear = { workspace = true } 19 | p3-poseidon2 = { workspace = true } 20 | p3-symmetric = { workspace = true } 21 | serde = { version = "1.0.207", features = ["derive"] } 22 | sha2 = "0.10.8" 23 | -------------------------------------------------------------------------------- /crates/primitives/src/consts.rs: -------------------------------------------------------------------------------- 1 | /// The maximum size of the memory in bytes. 2 | pub const MAXIMUM_MEMORY_SIZE: u32 = u32::MAX; 3 | 4 | /// The size of a word in bytes. 5 | pub const WORD_SIZE: usize = 4; 6 | 7 | /// Converts a slice of words to a byte vector in little endian. 8 | pub fn words_to_bytes_le_vec(words: &[u32]) -> Vec { 9 | words.iter().flat_map(|word| word.to_le_bytes().to_vec()).collect::>() 10 | } 11 | 12 | /// Converts a slice of words to a slice of bytes in little endian. 13 | pub fn words_to_bytes_le(words: &[u32]) -> [u8; B] { 14 | debug_assert_eq!(words.len() * 4, B); 15 | words 16 | .iter() 17 | .flat_map(|word| word.to_le_bytes().to_vec()) 18 | .collect::>() 19 | .try_into() 20 | .unwrap() 21 | } 22 | 23 | /// Converts a byte array in little endian to a slice of words. 24 | pub fn bytes_to_words_le(bytes: &[u8]) -> [u32; W] { 25 | debug_assert_eq!(bytes.len(), W * 4); 26 | bytes 27 | .chunks_exact(4) 28 | .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap())) 29 | .collect::>() 30 | .try_into() 31 | .unwrap() 32 | } 33 | 34 | /// Converts a byte array in little endian to a vector of words. 35 | pub fn bytes_to_words_le_vec(bytes: &[u8]) -> Vec { 36 | bytes 37 | .chunks_exact(4) 38 | .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap())) 39 | .collect::>() 40 | } 41 | 42 | // Converts a num to a string with commas every 3 digits. 43 | pub fn num_to_comma_separated(value: T) -> String { 44 | value 45 | .to_string() 46 | .chars() 47 | .rev() 48 | .collect::>() 49 | .chunks(3) 50 | .map(|chunk| chunk.iter().collect::()) 51 | .collect::>() 52 | .join(",") 53 | .chars() 54 | .rev() 55 | .collect() 56 | } 57 | -------------------------------------------------------------------------------- /crates/primitives/src/io.rs: -------------------------------------------------------------------------------- 1 | use crate::types::Buffer; 2 | use num_bigint::BigUint; 3 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; 4 | use sha2::{Digest, Sha256}; 5 | 6 | /// Public values for the prover. 7 | #[derive(Debug, Clone, Serialize, Deserialize, Default)] 8 | pub struct SP1PublicValues { 9 | buffer: Buffer, 10 | } 11 | 12 | impl SP1PublicValues { 13 | /// Create a new `SP1PublicValues`. 14 | pub const fn new() -> Self { 15 | Self { buffer: Buffer::new() } 16 | } 17 | 18 | pub fn raw(&self) -> String { 19 | format!("0x{}", hex::encode(self.buffer.data.clone())) 20 | } 21 | 22 | /// Create a `SP1PublicValues` from a slice of bytes. 23 | pub fn from(data: &[u8]) -> Self { 24 | Self { buffer: Buffer::from(data) } 25 | } 26 | 27 | pub fn as_slice(&self) -> &[u8] { 28 | self.buffer.data.as_slice() 29 | } 30 | 31 | pub fn to_vec(&self) -> Vec { 32 | self.buffer.data.clone() 33 | } 34 | 35 | /// Read a value from the buffer. 36 | pub fn read(&mut self) -> T { 37 | self.buffer.read() 38 | } 39 | 40 | /// Read a slice of bytes from the buffer. 41 | pub fn read_slice(&mut self, slice: &mut [u8]) { 42 | self.buffer.read_slice(slice); 43 | } 44 | 45 | /// Write a value to the buffer. 46 | pub fn write(&mut self, data: &T) { 47 | self.buffer.write(data); 48 | } 49 | 50 | /// Write a slice of bytes to the buffer. 51 | pub fn write_slice(&mut self, slice: &[u8]) { 52 | self.buffer.write_slice(slice); 53 | } 54 | 55 | /// Hash the public values. 56 | pub fn hash(&self) -> Vec { 57 | let mut hasher = Sha256::new(); 58 | hasher.update(self.buffer.data.as_slice()); 59 | hasher.finalize().to_vec() 60 | } 61 | 62 | /// Hash the public values, mask the top 3 bits and return a BigUint. Matches the implementation 63 | /// of `hashPublicValues` in the Solidity verifier. 64 | /// 65 | /// ```solidity 66 | /// sha256(publicValues) & bytes32(uint256((1 << 253) - 1)); 67 | /// ``` 68 | pub fn hash_bn254(&self) -> BigUint { 69 | // Hash the public values. 70 | let mut hasher = Sha256::new(); 71 | hasher.update(self.buffer.data.as_slice()); 72 | let hash_result = hasher.finalize(); 73 | let mut hash = hash_result.to_vec(); 74 | 75 | // Mask the top 3 bits. 76 | hash[0] &= 0b00011111; 77 | 78 | // Return the masked hash as a BigUint. 79 | BigUint::from_bytes_be(&hash) 80 | } 81 | } 82 | 83 | impl AsRef<[u8]> for SP1PublicValues { 84 | fn as_ref(&self) -> &[u8] { 85 | &self.buffer.data 86 | } 87 | } 88 | 89 | #[cfg(test)] 90 | mod tests { 91 | use super::*; 92 | 93 | #[test] 94 | fn test_hash_public_values() { 95 | let test_hex = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; 96 | let test_bytes = hex::decode(test_hex).unwrap(); 97 | 98 | let mut public_values = SP1PublicValues::new(); 99 | public_values.write_slice(&test_bytes); 100 | let hash = public_values.hash_bn254(); 101 | 102 | let expected_hash = "1ce987d0a7fcc2636fe87e69295ba12b1cc46c256b369ae7401c51b805ee91bd"; 103 | let expected_hash_biguint = BigUint::from_bytes_be(&hex::decode(expected_hash).unwrap()); 104 | 105 | assert_eq!(hash, expected_hash_biguint); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /crates/primitives/src/types.rs: -------------------------------------------------------------------------------- 1 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Clone, Copy)] 4 | pub enum RecursionProgramType { 5 | Core, 6 | Deferred, 7 | Compress, 8 | Shrink, 9 | Wrap, 10 | } 11 | 12 | /// A buffer of serializable/deserializable objects. 13 | #[derive(Debug, Clone, Serialize, Deserialize)] 14 | pub struct Buffer { 15 | pub data: Vec, 16 | #[serde(skip)] 17 | pub ptr: usize, 18 | } 19 | 20 | impl Buffer { 21 | pub const fn new() -> Self { 22 | Self { data: Vec::new(), ptr: 0 } 23 | } 24 | 25 | pub fn from(data: &[u8]) -> Self { 26 | Self { data: data.to_vec(), ptr: 0 } 27 | } 28 | 29 | /// Set the position ptr to the beginning of the buffer. 30 | pub fn head(&mut self) { 31 | self.ptr = 0; 32 | } 33 | 34 | /// Read the serializable object from the buffer. 35 | pub fn read(&mut self) -> T { 36 | let result: T = 37 | bincode::deserialize(&self.data[self.ptr..]).expect("failed to deserialize"); 38 | let nb_bytes = bincode::serialized_size(&result).expect("failed to get serialized size"); 39 | self.ptr += nb_bytes as usize; 40 | result 41 | } 42 | 43 | pub fn read_slice(&mut self, slice: &mut [u8]) { 44 | slice.copy_from_slice(&self.data[self.ptr..self.ptr + slice.len()]); 45 | self.ptr += slice.len(); 46 | } 47 | 48 | /// Write the serializable object from the buffer. 49 | pub fn write(&mut self, data: &T) { 50 | let mut tmp = Vec::new(); 51 | bincode::serialize_into(&mut tmp, data).expect("serialization failed"); 52 | self.data.extend(tmp); 53 | } 54 | 55 | /// Write the slice of bytes to the buffer. 56 | pub fn write_slice(&mut self, slice: &[u8]) { 57 | self.data.extend_from_slice(slice); 58 | } 59 | } 60 | 61 | impl Default for Buffer { 62 | fn default() -> Self { 63 | Self::new() 64 | } 65 | } 66 | --------------------------------------------------------------------------------