Skip to content

Commit 67e7c27

Browse files
committed
Merge branch 'chainwayxyz-fix-merkle-tree-and-spv-vuln'
2 parents 58b809c + cc44324 commit 67e7c27

36 files changed

Lines changed: 2765 additions & 1039 deletions

Cargo.lock

Lines changed: 1493 additions & 688 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

final-spv/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.1.0"
44
edition = "2021"
55

66
[dependencies]
7-
risc0-zkvm = {version = "1.2.5", default-features = false, features = ["std"]}
7+
risc0-zkvm = {version = "2.0.2", default-features = false, features = ["std"]}
88
borsh = {version = "1.5.3", features = ["derive"] }
99
sha2 = { version = "0.10.8", default-features = false }
1010
crypto-bigint = { version = "0.5.5", default-features = false }

final-spv/build.dockerfile

Lines changed: 0 additions & 34 deletions
This file was deleted.

final-spv/final-spv-guest/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ version = "0.1.0"
44
edition = "2021"
55

66
[build-dependencies]
7-
risc0-build = { version = "1.2.5" }
7+
risc0-build = { version = "2.1.1" }
8+
risc0-binfmt = {version = "2.0.1"}
89

910
[package.metadata.risc0]
1011
methods = ["guest"]

final-spv/final-spv-guest/build.rs

Lines changed: 197 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,203 @@
1-
use std::env;
2-
use std::process::Command;
1+
use risc0_binfmt::compute_image_id;
2+
use risc0_build::{embed_methods_with_options, DockerOptionsBuilder, GuestOptionsBuilder};
3+
use std::{collections::HashMap, env, fs, path::Path};
34

45
fn main() {
5-
println!("cargo:rerun-if-changed=../build.dockerfile");
6-
7-
if env::var("REPR_GUEST_BUILD").is_ok() {
8-
// Get the absolute path to the project root
9-
let current_dir = env::current_dir().expect("Failed to get current directory");
10-
let project_root = current_dir.parent().unwrap().parent().unwrap();
11-
let output_dir = project_root.join("target/riscv-guest/riscv32im-risc0-zkvm-elf/docker");
12-
13-
eprintln!("Current directory: {:?}", current_dir);
14-
eprintln!("Project root: {:?}", project_root);
15-
eprintln!("Output directory: {:?}", output_dir);
16-
17-
// Ensure the output directory exists
18-
std::fs::create_dir_all(&output_dir).expect("Failed to create output directory");
19-
20-
let output = Command::new("docker")
21-
.args([
22-
"buildx",
23-
"build",
24-
"--platform",
25-
"linux/amd64",
26-
"-f",
27-
"final-spv/build.dockerfile",
28-
"--output",
29-
&format!("type=local,dest=."),
30-
".", // Use current directory as context
31-
"--build-arg",
32-
&format!(
33-
"BITCOIN_NETWORK={}",
34-
std::env::var("BITCOIN_NETWORK").unwrap().as_str()
35-
),
36-
])
37-
.current_dir(project_root) // Set working directory to project root
38-
.output()
39-
.expect("Failed to execute Docker command");
40-
41-
if !output.status.success() {
42-
eprintln!("Docker build failed:");
43-
eprintln!("stdout: {}", String::from_utf8_lossy(&output.stdout));
44-
eprintln!("stderr: {}", String::from_utf8_lossy(&output.stderr));
45-
panic!("Docker build failed");
6+
// Build environment variables
7+
println!("cargo:rerun-if-env-changed=SKIP_GUEST_BUILD");
8+
println!("cargo:rerun-if-env-changed=REPR_GUEST_BUILD");
9+
println!("cargo:rerun-if-env-changed=OUT_DIR");
10+
11+
// Compile time constant environment variables
12+
println!("cargo:rerun-if-env-changed=BITCOIN_NETWORK");
13+
println!("cargo:rerun-if-env-changed=TEST_SKIP_GUEST_BUILD");
14+
15+
if std::env::var("CLIPPY_ARGS").is_ok() {
16+
let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set");
17+
let dummy_path = Path::new(&out_dir).join("methods.rs");
18+
fs::write(dummy_path, "// dummy methods.rs for Clippy\n")
19+
.expect("Failed to write dummy methods.rs");
20+
println!("cargo:warning=Skipping guest build in Clippy");
21+
return;
22+
}
23+
24+
// Check if we should skip the guest build for tests
25+
if let Ok("1" | "true") = env::var("TEST_SKIP_GUEST_BUILD").as_deref() {
26+
println!("cargo:warning=Skipping guest build in test. Exiting");
27+
return;
28+
}
29+
30+
let network = env::var("BITCOIN_NETWORK").unwrap_or_else(|_| {
31+
println!("cargo:warning=BITCOIN_NETWORK not set, defaulting to 'mainnet'");
32+
"mainnet".to_string()
33+
});
34+
println!("cargo:warning=Building for Bitcoin network: {}", network);
35+
36+
let is_repr_guest_build = match env::var("REPR_GUEST_BUILD") {
37+
Ok(value) => match value.as_str() {
38+
"1" | "true" => {
39+
println!("cargo:warning=REPR_GUEST_BUILD is set to true");
40+
true
41+
}
42+
"0" | "false" => {
43+
println!("cargo:warning=REPR_GUEST_BUILD is set to false");
44+
false
45+
}
46+
_ => {
47+
println!("cargo:warning=Invalid value for REPR_GUEST_BUILD: '{}'. Expected '0', '1', 'true', or 'false'. Defaulting to false.", value);
48+
false
49+
}
50+
},
51+
Err(env::VarError::NotPresent) => {
52+
println!("cargo:warning=REPR_GUEST_BUILD not set. Defaulting to false.");
53+
false
54+
}
55+
Err(env::VarError::NotUnicode(_)) => {
56+
println!(
57+
"cargo:warning=REPR_GUEST_BUILD contains invalid Unicode. Defaulting to false."
58+
);
59+
false
60+
}
61+
};
62+
63+
// Use embed_methods_with_options with our custom options
64+
let guest_pkg_to_options = get_guest_options(network.clone());
65+
embed_methods_with_options(guest_pkg_to_options);
66+
67+
// After the build is complete, copy the generated file to the elfs folder
68+
if is_repr_guest_build {
69+
println!("cargo:warning=Copying binary to elfs folder");
70+
copy_binary_to_elfs_folder(network);
71+
} else {
72+
println!("cargo:warning=Not copying binary to elfs folder");
73+
}
74+
}
75+
76+
fn get_guest_options(network: String) -> HashMap<&'static str, risc0_build::GuestOptions> {
77+
let mut guest_pkg_to_options = HashMap::new();
78+
79+
let opts = if env::var("REPR_GUEST_BUILD").is_ok() {
80+
let manifest_dir = env!("CARGO_MANIFEST_DIR");
81+
let root_dir = format!("{manifest_dir}/../../");
82+
83+
println!(
84+
"cargo:warning=Using Docker for guest build with root dir: {}",
85+
root_dir
86+
);
87+
88+
let docker_opts = DockerOptionsBuilder::default()
89+
.root_dir(root_dir)
90+
.env(vec![("BITCOIN_NETWORK".to_string(), network.clone())])
91+
.build()
92+
.unwrap();
93+
94+
GuestOptionsBuilder::default()
95+
// .features(features)
96+
.use_docker(docker_opts)
97+
.build()
98+
.unwrap()
99+
} else {
100+
println!("cargo:warning=Guest code is not built in docker");
101+
GuestOptionsBuilder::default()
102+
// .features(features)
103+
.build()
104+
.unwrap()
105+
};
106+
107+
guest_pkg_to_options.insert("final-spv-guest", opts);
108+
guest_pkg_to_options
109+
}
110+
111+
fn copy_binary_to_elfs_folder(network: String) {
112+
let current_dir = env::current_dir().expect("Failed to get current dir");
113+
// base_dir will be /home/ozan/workspace/BitVM/
114+
let base_dir = current_dir
115+
.join("../..")
116+
.canonicalize()
117+
.expect("Failed to canonicalize base_dir");
118+
119+
// Create elfs directory if it doesn't exist
120+
let elfs_dir = base_dir.join("prover/elfs");
121+
if !elfs_dir.exists() {
122+
fs::create_dir_all(&elfs_dir).expect("Failed to create elfs directory");
123+
println!("cargo:warning=Created elfs directory at {:?}", elfs_dir);
124+
}
125+
126+
// Build source path (ensure this path is correct based on risc0_build's actual output location)
127+
// This path assumes a specific structure relative to the workspace root.
128+
// A more robust way might be to get this path from OUT_DIR if risc0_build places it there predictably.
129+
let src_path = base_dir.join("target/riscv-guest/final-spv-circuit/final-spv-guest/riscv32im-risc0-zkvm-elf/docker/final-spv-guest.bin");
130+
131+
if !src_path.exists() {
132+
// If the source ELF from the Docker build isn't found, the copy will fail.
133+
// This could indicate an issue with the guest build itself or the path construction.
134+
// The Docker build log (#8 and #9) suggests the ELF is built.
135+
// RISC0 build scripts often place final ELFs in OUT_DIR/{method_name}.
136+
// Consider using: let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
137+
// let src_path = out_dir.join("final-spv-guest"); // or "final-spv-guest.elf" / ".bin"
138+
println!(
139+
"cargo:warning=Source binary not found at {:?}, skipping copy. This might be the root cause if copy fails.",
140+
src_path
141+
);
142+
// It's important to ensure src_path is correct. For now, we assume it is based on your log.
143+
// If the copy fails below, investigate src_path more deeply.
144+
// return; // Or panic, as subsequent steps depend on this.
145+
}
146+
147+
// Build destination path with network prefix
148+
let dest_filename = format!("{}-final-spv-guest.bin", network.to_lowercase());
149+
let dest_path = elfs_dir.join(&dest_filename); // This is an absolute PathBuf
150+
151+
// Copy the file
152+
match fs::copy(&src_path, &dest_path) {
153+
Ok(_) => println!(
154+
"cargo:warning=Successfully copied binary from {:?} to {:?}",
155+
src_path, dest_path
156+
),
157+
Err(e) => {
158+
// If the copy fails, the subsequent read will definitely fail.
159+
// It's crucial to handle this, e.g., by panicking to stop the build with a clear message.
160+
panic!(
161+
"Failed to copy binary from {:?} to {:?}: {}. Subsequent ELF read will fail.",
162+
src_path, dest_path, e
163+
);
46164
}
47165
}
48166

49-
risc0_build::embed_methods();
167+
// The `elf_path` string below is used as a logical identifier or for constructing paths within the prover,
168+
// but it should not be used directly for fs::read from the build script's CWD.
169+
let logical_elf_path_for_id = match network.as_str() {
170+
"mainnet" => "prover/elfs/mainnet-final-spv-guest.bin",
171+
"testnet4" => "prover/elfs/testnet4-final-spv-guest.bin",
172+
"signet" => "prover/elfs/signet-final-spv-guest.bin",
173+
"regtest" => "prover/elfs/regtest-final-spv-guest.bin",
174+
_ => {
175+
println!(
176+
"cargo:warning=Invalid network specified, defaulting to mainnet for logical path"
177+
);
178+
"prover/elfs/mainnet-final-spv-guest.bin"
179+
}
180+
};
181+
182+
println!(
183+
"cargo:warning=Logical ELF path for ID computation: {:?}",
184+
logical_elf_path_for_id
185+
);
186+
187+
// --- THIS IS THE FIX ---
188+
// Read the ELF file from `dest_path`, where it was actually copied.
189+
// `dest_path` is already an absolute `PathBuf`.
190+
println!(
191+
"cargo:warning=Attempting to read ELF file from: {:?}",
192+
dest_path
193+
);
194+
let elf_bytes: Vec<u8> = fs::read(&dest_path) // Use &dest_path here
195+
.unwrap_or_else(|e| panic!("Failed to read ELF file from {:?}: {}", dest_path, e));
196+
197+
let method_id = compute_image_id(elf_bytes.as_slice()).unwrap();
198+
println!("cargo:warning=Computed method ID: {:x?}", method_id);
199+
println!(
200+
"cargo:warning=Computed method ID words: {:?}",
201+
method_id.as_words()
202+
);
50203
}

0 commit comments

Comments
 (0)