Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .oss-scanner/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ sp1-test = """test --release --workspace --features native-gnark,experimental,pr
--exclude sp1-gpu-tracegen --exclude sp1-gpu-tracing --exclude sp1-gpu-utils --exclude sp1-gpu-zerocheck"""
EOF
RUN cargo sp1-test --no-run
# sp1-build marks the guest programs as changed on every build; the second skipped build settles the fingerprints.
ENV SP1_SKIP_PROGRAM_BUILD=true
RUN cargo sp1-test --no-run && cargo sp1-test --no-run
RUN cd crates/recursion/gnark-ffi/go && go mod download

ENV CARGO_NET_OFFLINE=true \
Expand Down
2 changes: 1 addition & 1 deletion .oss-scanner/threat_model.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Out of scope: `sp1-gpu/` (needs CUDA, which the scanner does not have), `example

## How to exercise it
- The image is built with `cargo sp1-test --no-run`, which is an alias in `/root/.cargo/config.toml` for the CI test command. Run tests with `cargo sp1-test -- <test name filter>`, or run a test binary in `target/release/deps` directly, to reuse that build. A different `-p` or `--features` set causes a long rebuild.
- Guest test programs are in `crates/test-artifacts/programs` and are prebuilt. The image sets `SP1_SKIP_PROGRAM_BUILD=true`, so changes to a guest program have no effect until you build with `SP1_SKIP_PROGRAM_BUILD=false`. Guest programs compile offline with the `succinct` Rust toolchain.
- Guest test programs are in `crates/test-artifacts/programs` and are prebuilt. They rebuild when their source changes, offline, with the `succinct` Rust toolchain.
- On the scan machine (2 CPUs, 8 GB), the executor tests take less than a second, and a small core proof such as `sp1_core_machine` `riscv::tests::test_fibonacci_prove_simple` takes about 30 seconds. The end-to-end tests in `sp1-prover` (`test_e2e_*`) are too slow. For a soundness bug, a small core proof with a modified trace, or a test in `crates/core/machine/src/utils/zerocheck_unit_test.rs`, is a good reproducer.
- The PLONK and Groth16 circuit artifacts are not in the image (they are 8.5 GB). The verifying keys that `crates/verifier` uses are in `crates/verifier/vk-artifacts`. The `sp1-verifier` tests are not prebuilt, because they make full proofs.

Expand Down
4 changes: 4 additions & 0 deletions crates/build/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ error: failed to load manifest for dependency `...`
To fix this, you can either:
1. Move the program into the workspace that contains the crate.
2. Build the crate locally instead.

Adding a new file directly beside an excluded `target/` directory may not trigger a guest rebuild, leaving the previous ELF in use.
Changes within watched directories such as `src/` are still detected.
To force a rebuild, run `cargo clean -p <host-package>` for the package whose build script invokes `sp1-build`, then repeat your build.
62 changes: 60 additions & 2 deletions crates/build/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::Path;
use std::path::{Path, PathBuf};

use cargo_metadata::Metadata;
use chrono::Local;
Expand Down Expand Up @@ -32,8 +32,66 @@ pub(crate) fn cargo_rerun_if_changed(metadata: &Metadata, program_dir: &Path) {
for package in &metadata.packages {
for dependency in &package.dependencies {
if let Some(path) = &dependency.path {
println!("cargo:rerun-if-changed={}", path.as_str());
for path in
paths_excluding(path.as_std_path(), metadata.target_directory.as_std_path())
{
println!("cargo:rerun-if-changed={}", path.display());
}
}
}
}
}

/// Skips build output, which would otherwise make Cargo's recursive directory scan always see changes.
fn paths_excluding(path: &Path, excluded: &Path) -> Vec<PathBuf> {
if path == excluded
|| (path.file_name() == Some("target".as_ref())
&& path.with_file_name("Cargo.toml").exists())
{
return vec![];
}
if !path.is_dir() {
return vec![path.to_path_buf()];
}
let mut children: Vec<PathBuf> =
std::fs::read_dir(path).unwrap().map(|entry| entry.unwrap().path()).collect();
children.sort();
let paths: Vec<PathBuf> =
children.iter().flat_map(|child| paths_excluding(child, excluded)).collect();
if paths == children && !excluded.starts_with(path) {
Comment thread
fakedev9999 marked this conversation as resolved.
vec![path.to_path_buf()]
} else {
paths
}
}

#[cfg(test)]
mod tests {
use super::paths_excluding;

#[test]
fn paths_excluding_skips_only_build_output() {
let root = std::env::temp_dir().join(format!("sp1-build-paths-{}", std::process::id()));
let (other, program) = (root.join("other"), root.join("program"));
for dir in
[other.join("target"), other.join("src"), program.join("src"), root.join("src/target")]
{
std::fs::create_dir_all(dir).unwrap();
}
std::fs::write(other.join("Cargo.toml"), "").unwrap();
let not_yet_built = program.join("target");

assert_eq!(
paths_excluding(&root, &not_yet_built),
vec![
other.join("Cargo.toml"),
other.join("src"),
program.join("src"),
root.join("src")
]
);
assert_eq!(paths_excluding(&root.join("src"), &not_yet_built), vec![root.join("src")]);

std::fs::remove_dir_all(root).unwrap();
}
}
Loading