diff --git a/aggregation_mode/Cargo.toml b/aggregation_mode/Cargo.toml index fe40bbd5ac..4fb8d31e35 100644 --- a/aggregation_mode/Cargo.toml +++ b/aggregation_mode/Cargo.toml @@ -29,6 +29,7 @@ risc0-ethereum-contracts = { git = "https://github.com/risc0/risc0-ethereum/", t [build-dependencies] sp1-build = { version = "4.1.3" } risc0-build = { version = "2.0.0" } +sha3 = "0.10.8" [package.metadata.risc0] # Tell risc0 build to find method in ./aggregation_programs/risc0 package diff --git a/aggregation_mode/build.rs b/aggregation_mode/build.rs index cb6e6363b5..3b43459e01 100644 --- a/aggregation_mode/build.rs +++ b/aggregation_mode/build.rs @@ -1,15 +1,72 @@ use risc0_build::{DockerOptionsBuilder, GuestOptionsBuilder}; +use sha3::{Digest, Keccak256}; + use std::collections::HashMap; +use std::io::Read; +use std::path::Path; +use std::{env, fs}; + +fn hash_files_and_features>(paths: &[P], features: Vec) -> String { + let mut hasher = Keccak256::new(); + for path in paths { + let mut f = fs::File::open(path).unwrap(); + let mut buffer = Vec::new(); + f.read_to_end(&mut buffer).unwrap(); + hasher.update(&buffer); + } + + for feature in features { + hasher.update(&feature); + } + + format!("{:x}", hasher.finalize()) +} // Reference: https://docs.succinct.xyz/docs/sp1/writing-programs/compiling#advanced-build-options-1 fn main() { + let programs = [ + "build.rs", + "aggregation_programs/Cargo.toml", + "aggregation_programs/Cargo.lock", + "aggregation_programs/sp1/Cargo.toml", + "aggregation_programs/sp1/src/lib.rs", + "aggregation_programs/sp1/src/user_proofs_aggregator_main.rs", + "aggregation_programs/sp1/src/chunk_aggregator_main.rs", + "aggregation_programs/risc0/Cargo.toml", + "aggregation_programs/risc0/src/user_proofs_aggregator_main.rs", + "aggregation_programs/risc0/src/chunk_aggregator_main.rs", + "aggregation_programs/risc0/src/lib.rs", + ]; + + for file in &programs { + println!("cargo:rerun-if-changed={}", file); + } + + // Get all the env vars from rust (RUSTC, CARGO_FEATURES, etc) + // But filter those that don't affect the build of the program + let mut flags: Vec = env::vars() + .filter(|(k, _)| k != "AGGREGATOR" || k != "RISC0_DEV_MODE" || k != "SP1_PROVER") + .map(|(k, v)| format!("{k}={v}")) + .collect(); + // Sort them to make it deterministic in spite of the order. + flags.sort(); + + let hash = hash_files_and_features(&programs, flags); + let hash_file = Path::new("target/programs_hash.txt"); + + let needs_build = if let Ok(prev) = fs::read_to_string(hash_file) { + prev != hash + } else { + true + }; + + if !needs_build { + return; + } + sp1_build::build_program_with_args("./aggregation_programs/sp1", { sp1_build::BuildArgs { output_directory: Some("./aggregation_programs/sp1/elf".to_string()), - binaries: vec![ - "sp1_user_proofs_aggregator_program".into(), - "sp1_chunk_aggregator_program".into(), - ], // We use Docker to generate a reproducible ELF that will be identical across all platforms // (https://docs.succinct.xyz/docs/sp1/writing-programs/compiling#production-builds) docker: true, @@ -25,9 +82,10 @@ fn main() { .use_docker(docker_options) .build() .unwrap(); - risc0_build::embed_methods_with_options(HashMap::from([( "risc0_aggregation_program", guest_options, )])); + + fs::write(hash_file, hash).unwrap(); }