miden-client 0.9.0

Client library that facilitates interaction with the Miden rollup
Documentation
use std::{
    fs::{self, File},
    io::Write,
    path::Path,
};

use miden_lib::account::auth::RpoFalcon512;
use miden_node_proto_build::rpc_api_descriptor;
use miden_objects::{EMPTY_WORD, account::AccountComponent, crypto::dsa::rpo_falcon512::PublicKey};
use miette::IntoDiagnostic;

const STD_PROTO_OUT_DIR: &str = "src/rpc/generated/std";
const NO_STD_PROTO_OUT_DIR: &str = "src/rpc/generated/nostd";

/// 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 CODEGEN: bool = option_env!("CODEGEN").is_some();

fn main() -> miette::Result<()> {
    println!("cargo::rerun-if-env-changed=CODEGEN");
    if !CODEGEN {
        return Ok(());
    }

    compile_tonic_client_proto()?;
    replace_no_std_types();
    generate_known_procedure_roots().into_diagnostic()
}
// NODE RPC CLIENT PROTO CODEGEN
// ===============================================================================================

/// Generates the Rust protobuf bindings for the RPC client.
fn compile_tonic_client_proto() -> miette::Result<()> {
    let file_descriptors = rpc_api_descriptor();

    let mut prost_config = prost_build::Config::new();
    prost_config.skip_debug(["AccountId", "Digest"]);

    let mut web_tonic_prost_config = prost_build::Config::new();
    web_tonic_prost_config.skip_debug(["AccountId", "Digest"]);

    // Generate the header of the user facing server from its proto file
    tonic_build::configure()
        .build_transport(false)
        .build_server(false)
        .out_dir(NO_STD_PROTO_OUT_DIR)
        .compile_fds_with_config(web_tonic_prost_config, file_descriptors.clone())
        .into_diagnostic()?;

    tonic_build::configure()
        .build_server(false)
        .out_dir(STD_PROTO_OUT_DIR)
        .compile_fds_with_config(prost_config, file_descriptors)
        .into_diagnostic()?;

    Ok(())
}

/// This function replaces all `std::result` with `core::result` in the generated "rpc.rs" file
/// for the web tonic client. This is needed as `tonic_build` doesn't generate `no_std` compatible
/// files and we want to build wasm without `std`.
fn replace_no_std_types() {
    let path = NO_STD_PROTO_OUT_DIR.to_string() + "/rpc.rs";
    let file_str = fs::read_to_string(&path).unwrap();
    let new_file_str = file_str
        .replace("std::result", "core::result")
        .replace("std::marker", "core::marker");

    let mut f = std::fs::OpenOptions::new().write(true).open(path).unwrap();
    f.write_all(new_file_str.as_bytes()).unwrap();
}

// KNOWN PROCEDURE ROOTS
// ===============================================================================================

fn generate_known_procedure_roots() -> std::io::Result<()> {
    // Get the output directory from the environment variables
    let out_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("src/account/procedure_roots.rs");
    let mut f = File::create(&dest_path)?;
    // Write the top-level doc comment
    writeln!(f, "//! Well-known procedure roots.")?;
    writeln!(f, "//! This file was generated by build.rs.\n")?;

    let rpo_falcon_512_component: AccountComponent =
        RpoFalcon512::new(PublicKey::new(EMPTY_WORD)).into();

    let rpo_auth_proc_root = rpo_falcon_512_component
        .library()
        .mast_forest()
        .procedure_digests()
        .next()
        .expect("RpoFalcon512 component should have one procedure root");

    writeln!(f, "/// Procedure root of the `RpoFalcon512` authentication component.")?;
    writeln!(f, "pub const RPO_FALCON_512_AUTH: &str =\n    \"{rpo_auth_proc_root}\";")?;

    Ok(())
}