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
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# This file is generated by build.rs, do not modify
# This file contains deterministic values. Do not modify manually.

# This file contains the canonical zeros for the Keccak hash function.
# This file contains the canonical zeros for the Keccak hash function.
# Zero of height `n` (ZERO_N) is the root of the binary tree of height `n` with leaves equal zero.
#
#
# Since the Keccak hash is represented by eight u32 values, each constant consists of two Words.

const ZERO_0_L = [0, 0, 0, 0]
Expand Down Expand Up @@ -102,7 +102,8 @@ const ZERO_31_L = [2340505732, 1648733876, 2660540036, 3759582231]
const ZERO_31_R = [2389186238, 4049365781, 1653344606, 2840985724]

use ::agglayer::common::utils::mem_store_double_word



#! Inputs: [zeros_ptr]
#! Outputs: []
pub proc load_zeros_to_memory
Expand Down
131 changes: 40 additions & 91 deletions crates/miden-agglayer/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,14 @@ use miden_standards::account::mint_policies::OwnerControlled;
// CONSTANTS
// ================================================================================================

/// Defines whether the build script should generate files in `/src`.
/// The docs.rs build pipeline has a read-only filesystem, so we have to avoid writing to `src`,
/// otherwise the docs will fail to build there. Note that writing to `OUT_DIR` is fine.
const BUILD_GENERATED_FILES_IN_SRC: bool = option_env!("BUILD_GENERATED_FILES_IN_SRC").is_some();

const ASSETS_DIR: &str = "assets";
const ASM_DIR: &str = "asm";
const ASM_NOTE_SCRIPTS_DIR: &str = "note_scripts";
const ASM_AGGLAYER_DIR: &str = "agglayer";
const ASM_AGGLAYER_BRIDGE_DIR: &str = "agglayer/bridge";
const ASM_COMPONENTS_DIR: &str = "components";

const AGGLAYER_ERRORS_FILE: &str = "src/errors/agglayer.rs";
const AGGLAYER_ERRORS_RS_FILE: &str = "agglayer_errors.rs";
const AGGLAYER_ERRORS_ARRAY_NAME: &str = "AGGLAYER_ERRORS";
const AGGLAYER_GLOBAL_CONSTANTS_FILE_NAME: &str = "agglayer_constants.rs";

Expand All @@ -47,21 +42,17 @@ const AGGLAYER_GLOBAL_CONSTANTS_FILE_NAME: &str = "agglayer_constants.rs";
fn main() -> Result<()> {
// re-build when the MASM code changes
println!("cargo::rerun-if-changed={ASM_DIR}/");
println!("cargo::rerun-if-env-changed=BUILD_GENERATED_FILES_IN_SRC");
println!("cargo::rerun-if-env-changed=REGENERATE_CANONICAL_ZEROS");

// Copies the MASM code to the build directory
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let build_dir = env::var("OUT_DIR").unwrap();
let src = Path::new(&crate_dir).join(ASM_DIR);

// generate canonical zeros in `asm/agglayer/bridge/canonical_zeros.masm`
generate_canonical_zeros(&src.join(ASM_AGGLAYER_BRIDGE_DIR))?;

let dst = Path::new(&build_dir).to_path_buf();
shared::copy_directory(src, &dst, ASM_DIR)?;
// validate (or regenerate) canonical zeros in `asm/agglayer/bridge/canonical_zeros.masm`
let crate_path = Path::new(&crate_dir);
ensure_canonical_zeros(&crate_path.join(ASM_DIR).join(ASM_AGGLAYER_BRIDGE_DIR))?;

// set source directory to {OUT_DIR}/asm
let source_dir = dst.join(ASM_DIR);
// Read MASM sources directly from the crate's asm/ directory.
let source_dir = crate_path.join(ASM_DIR);

// set target directory to {OUT_DIR}/assets
let target_dir = Path::new(&build_dir).join(ASSETS_DIR);
Expand Down Expand Up @@ -91,7 +82,7 @@ fn main() -> Result<()> {
let constants_out_path = Path::new(&build_dir).join(AGGLAYER_GLOBAL_CONSTANTS_FILE_NAME);
generate_agglayer_constants(constants_out_path, component_libraries)?;

generate_error_constants(&source_dir)?;
generate_error_constants(&source_dir, &build_dir)?;

Ok(())
}
Expand Down Expand Up @@ -320,23 +311,15 @@ fn generate_agglayer_constants(
///
/// The function ensures that a constant is not defined twice, except if their error message is the
/// same. This can happen across multiple files.
///
/// Because the error files will be written to ./src/errors, this should be a no-op if ./src is
/// read-only. To enable writing to ./src, set the `BUILD_GENERATED_FILES_IN_SRC` environment
/// variable.
fn generate_error_constants(asm_source_dir: &Path) -> Result<()> {
if !BUILD_GENERATED_FILES_IN_SRC {
return Ok(());
}

fn generate_error_constants(asm_source_dir: &Path, build_dir: &str) -> Result<()> {
// Miden agglayer errors
// ------------------------------------------

let errors = shared::extract_all_masm_errors(asm_source_dir)
.context("failed to extract all masm errors")?;
shared::generate_error_file(
shared::ErrorModule {
file_name: AGGLAYER_ERRORS_FILE,
file_path: Path::new(build_dir).join(AGGLAYER_ERRORS_RS_FILE),
array_name: AGGLAYER_ERRORS_ARRAY_NAME,
is_crate_local: false,
},
Expand All @@ -346,14 +329,13 @@ fn generate_error_constants(asm_source_dir: &Path) -> Result<()> {
Ok(())
}

// CANONICAL ZEROS FILE GENERATION
// CANONICAL ZEROS VALIDATION
// ================================================================================================

fn generate_canonical_zeros(target_dir: &Path) -> Result<()> {
if !BUILD_GENERATED_FILES_IN_SRC {
return Ok(());
}

/// Validates that the committed `canonical_zeros.masm` matches the expected content computed from
/// Keccak256 canonical zeros. If the `REGENERATE_CANONICAL_ZEROS` environment variable is set,
/// the file is regenerated instead.
fn ensure_canonical_zeros(target_dir: &Path) -> Result<()> {
Comment on lines +335 to +338
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I manually tested that:

  1. if the file canonical_zeros.masm is missing, cargo build fails
  2. if the file is missing, but we pass REGENERATE_CANONICAL_ZEROS=1 cargo build, it succeeds
  3. Repeated cargo builds don't require compilation

const TREE_HEIGHT: u8 = 32;

let mut zeros_by_height = Vec::with_capacity(TREE_HEIGHT as usize);
Expand All @@ -373,10 +355,10 @@ fn generate_canonical_zeros(target_dir: &Path) -> Result<()> {
// convert the keccak digest into the sequence of u32 values and create two word constants from
// them to represent the hash
let mut zero_constants = String::from(
"# This file is generated by build.rs, do not modify\n
# This file contains the canonical zeros for the Keccak hash function.
"# This file contains deterministic values. Do not modify manually.\n
# This file contains the canonical zeros for the Keccak hash function.
# Zero of height `n` (ZERO_N) is the root of the binary tree of height `n` with leaves equal zero.
#
#
# Since the Keccak hash is represented by eight u32 values, each constant consists of two Words.\n",
);

Expand All @@ -398,7 +380,8 @@ fn generate_canonical_zeros(target_dir: &Path) -> Result<()> {
zero_constants.push_str(
"
use ::agglayer::common::utils::mem_store_double_word



#! Inputs: [zeros_ptr]
#! Outputs: []
pub proc load_zeros_to_memory\n",
Expand All @@ -410,9 +393,23 @@ pub proc load_zeros_to_memory\n",

zero_constants.push_str("\tdrop\nend\n");

// write the resulting masm content into the file only if it changed to avoid
// invalidating the cargo fingerprint for the `asm/` directory
shared::write_if_changed(target_dir.join("canonical_zeros.masm"), zero_constants)?;
let file_path = target_dir.join("canonical_zeros.masm");

if option_env!("REGENERATE_CANONICAL_ZEROS").is_some() {
// Regeneration mode: write the file
shared::write_if_changed(&file_path, &zero_constants)?;
} else {
// Validation mode: ensure the committed file matches
let committed = fs::read_to_string(&file_path)
.into_diagnostic()
.wrap_err("canonical_zeros.masm not found - it should be committed in the repo")?;
if committed != zero_constants {
return Err(Report::msg(
"canonical_zeros.masm is out of date. \
Run with REGENERATE_CANONICAL_ZEROS=1 to regenerate and commit the result.",
));
}
}

Ok(())
}
Expand All @@ -431,54 +428,6 @@ mod shared {
use regex::Regex;
use walkdir::WalkDir;

/// Recursively copies `src` into `dst`.
///
/// This function will overwrite the existing files if re-executed.
pub fn copy_directory<T: AsRef<Path>, R: AsRef<Path>>(
src: T,
dst: R,
asm_dir: &str,
) -> Result<()> {
let mut prefix = src.as_ref().canonicalize().unwrap();
// keep all the files inside the `asm` folder
prefix.pop();

let target_dir = dst.as_ref().join(asm_dir);
if target_dir.exists() {
// Clear existing asm files that were copied earlier which may no longer exist.
fs::remove_dir_all(&target_dir)
.into_diagnostic()
.wrap_err("failed to remove ASM directory")?;
}

// Recreate the directory structure.
fs::create_dir_all(&target_dir)
.into_diagnostic()
.wrap_err("failed to create ASM directory")?;

let dst = dst.as_ref();
let mut todo = vec![src.as_ref().to_path_buf()];

while let Some(goal) = todo.pop() {
for entry in fs::read_dir(goal).unwrap() {
let path = entry.unwrap().path();
if path.is_dir() {
let src_dir = path.canonicalize().unwrap();
let dst_dir = dst.join(src_dir.strip_prefix(&prefix).unwrap());
if !dst_dir.exists() {
fs::create_dir_all(&dst_dir).unwrap();
}
todo.push(src_dir);
} else {
let dst_file = dst.join(path.strip_prefix(&prefix).unwrap());
fs::copy(&path, dst_file).unwrap();
}
}
}

Ok(())
}

/// Returns a vector with paths to all MASM files in the specified directory.
///
/// All non-MASM files are skipped.
Expand Down Expand Up @@ -657,7 +606,7 @@ mod shared {
.into_diagnostic()?;
}

write_if_changed(module.file_name, output)?;
fs::write(module.file_path, output).into_diagnostic()?;

Ok(())
}
Expand Down Expand Up @@ -690,9 +639,9 @@ mod shared {
pub message: String,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, Clone)]
pub struct ErrorModule {
pub file_name: &'static str,
pub file_path: PathBuf,
pub array_name: &'static str,
pub is_crate_local: bool,
}
Expand Down
96 changes: 0 additions & 96 deletions crates/miden-agglayer/src/errors/agglayer.rs

This file was deleted.

2 changes: 1 addition & 1 deletion crates/miden-agglayer/src/errors/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Include generated error constants
#[cfg(any(feature = "testing", test))]
include!("agglayer.rs");
include!(concat!(env!("OUT_DIR"), "/agglayer_errors.rs"));
Loading
Loading