Skip to content

Commit

Permalink
Merge pull request #339 from 0xPolygonMiden/dominik_restructure_asm_c…
Browse files Browse the repository at this point in the history
…ompile_note_scripts

refactor: change asm structure and compile note scripts
  • Loading branch information
Dominik1999 authored Dec 11, 2023
2 parents a95cdb8 + 1deb79a commit 226a9c4
Show file tree
Hide file tree
Showing 29 changed files with 217 additions and 105 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
86 changes: 86 additions & 0 deletions miden-lib/asm/scripts/P2ID.masm
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use.miden::sat::account
use.miden::sat::note
use.miden::sat::tx
use.miden::wallets::basic->wallet

#! Helper procedure to add all assets of a note to an account.
#!
#! Inputs: []
#! Outputs: []
#!
proc.add_note_assets_to_account
push.0 exec.note::get_assets
# => [num_of_assets, 0 = ptr, ...]

# compute the pointer at which we should stop iterating
dup.1 add
# => [end_ptr, ptr, ...]

# pad the stack and move the pointer to the top
padw movup.5
# => [ptr, 0, 0, 0, 0, end_ptr, ...]

# compute the loop latch
dup dup.6 neq
# => [latch, ptr, 0, 0, 0, 0, end_ptr, ...]

while.true
# => [ptr, 0, 0, 0, 0, end_ptr, ...]

# save the pointer so that we can use it later
dup movdn.5
# => [ptr, 0, 0, 0, 0, ptr, end_ptr, ...]

# load the asset and add it to the account
mem_loadw call.wallet::receive_asset
# => [ASSET, ptr, end_ptr, ...]

# increment the pointer and compare it to the end_ptr
movup.4 add.1 dup dup.6 neq
# => [latch, ptr+1, ASSET, end_ptr, ...]
end

# clear the stack
drop dropw drop
end

# Pay-to-ID script: adds all assets from the note to the account, assuming ID of the account
# matches target account ID specified by the note inputs.
#
# Requires that the account exposes: miden::wallets::basic::receive_asset procedure.
#
# Inputs: []
# Outputs: []
#
# Note inputs are assumed to be as follows:
# - target_account_id is the ID of the account for which the note is intended.
#
# FAILS if:
# - Account does not expose miden::wallets::basic::receive_asset procedure.
# - Account ID of executing account is not equal to the Account ID specified via note inputs.
# - The same non-fungible asset already exists in the account.
# - Adding a fungible asset would result in amount overflow, i.e., the total amount would be
# greater than 2^63.
begin
# drop the transaction script root
dropw
# => []

# load the note inputs to memory starting at address 0
push.0 exec.note::get_inputs
# => [inputs_ptr]

# read the target account id from the note inputs
mem_load
# => [target_account_id]

exec.account::get_id
# => [account_id, target_account_id, ...]

# ensure account_id = target_account_id, fails otherwise
assert_eq
# => [...]

exec.add_note_assets_to_account
# => [...]
end
Original file line number Diff line number Diff line change
Expand Up @@ -44,65 +44,31 @@ proc.add_note_assets_to_account
drop dropw drop
end

# Pay to ID reclaimable: adds all assets from the note to the account, assuming ID of the account
# matches target account ID specified by the note inputs OR matches the sender ID if the note is
# consumed after the reclaim block height specified by the note inputs.
#
# Inputs: []
# Outputs: []
#
# Note inputs are assumed to be as follows:
# - target_account_id is the ID of the account for which the note is intended.
# - reclaim_block_height is the block height at which the note can be reclaimed by the sender.
#
# FAILS if:
# - Account does not expose miden::wallets::basic::receive_asset procedure.
# - Before reclaim block height: account ID of executing account is not equal to specified
# account ID.
# - At and after reclaim block height: account ID of executing account is not equal to
# specified account ID or Sender account ID.
# - The same non-fungible asset already exists in the account.
# - Adding a fungible asset would result in amount overflow, i.e., the total amount would be
# greater than 2^63.
begin
# drop the transaction script root
dropw
# => []

#! Pay-to-ID script: adds all assets from the note to the account, assuming ID of the account
#! matches target account ID specified by the note inputs.
#!
#! Requires that the account exposes: miden::wallets::basic::receive_asset procedure.
#!
#! Inputs: []
#! Outputs: []
#!
#! Note inputs are assumed to be as follows:
#! - target_account_id is the ID of the account for which the note is intended.
#!
#! FAILS if:
#! - Account does not expose miden::wallets::basic::receive_asset procedure.
#! - Account ID of executing account is not equal to the Account ID specified via note inputs.
#! - The same non-fungible asset already exists in the account.
#! - Adding a fungible asset would result in amount overflow, i.e., the total amount would be
#! greater than 2^63.
export.p2id
# load the note inputs to memory starting at address 0
push.0 exec.note::get_inputs
# => [inputs_ptr]

# read the target account id from the note inputs
mem_load
# => [target_account_id]

exec.account::get_id
# => [account_id, target_account_id, ...]

# ensure account_id = target_account_id, fails otherwise
assert_eq
# => [...]

exec.add_note_assets_to_account
# => [...]
end

#! Pay to ID reclaimable: adds all assets from the note to the account, assuming ID of the account
#! matches target account ID specified by the note inputs OR matches the sender ID if the note is
#! consumed after the reclaim block height specified by the note inputs.
#!
#! Inputs: []
#! Outputs: []
#!
#! Note inputs are assumed to be as follows:
#! - target_account_id is the ID of the account for which the note is intended.
#! - reclaim_block_height is the block height at which the note can be reclaimed by the sender.
#!
#! FAILS if:
#! - Account does not expose miden::wallets::basic::receive_asset procedure.
#! - Before reclaim block height: account ID of executing account is not equal to specified
#! account ID.
#! - At and after reclaim block height: account ID of executing account is not equal to
#! specified account ID or Sender account ID.
#! - The same non-fungible asset already exists in the account.
#! - Adding a fungible asset would result in amount overflow, i.e., the total amount would be
#! greater than 2^63.
export.p2idr
# load the note inputs to memory starting at address 0
push.0 exec.note::get_inputs
# => [inputs_ptr]
Expand Down
91 changes: 76 additions & 15 deletions miden-lib/build.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
use assembly::{LibraryNamespace, MaslLibrary, Version};
use std::{env, fs, fs::File, io, io::BufRead, io::BufReader, io::Write, path::Path};
use assembly::{
ast::{AstSerdeOptions, ProgramAst},
LibraryNamespace, MaslLibrary, Version,
};
use std::{
env, fs,
fs::File,
io::{self, BufRead, BufReader, Write},
path::{Path, PathBuf},
};

// CONSTANTS
// ================================================================================================
const ASL_DIR_PATH: &str = "assets";
const ASM_DIR_PATH: &str = "asm";
const ASM_MIDEN_DIR_PATH: &str = "asm/miden";
const ASM_SCRIPTS_DIR_PATH: &str = "asm/scripts";

// PRE-PROCESSING
// ================================================================================================
Expand All @@ -17,11 +27,12 @@ const ASM_DIR_PATH: &str = "asm";
/// - If any of the IO operation fails.
fn copy_directory<T: AsRef<Path>, R: AsRef<Path>>(src: T, dst: R) {
let mut prefix = src.as_ref().canonicalize().unwrap();
prefix.pop(); // keep all the files inside the `asm` folder
// keep all the files inside the `asm` folder
prefix.pop();

let target_dir = dst.as_ref().join(ASM_DIR_PATH);
if !target_dir.exists() {
fs::create_dir(target_dir).unwrap();
fs::create_dir_all(target_dir).unwrap();
}

let dst = dst.as_ref();
Expand All @@ -34,7 +45,7 @@ fn copy_directory<T: AsRef<Path>, R: AsRef<Path>>(src: T, dst: R) {
let src_dir = path.canonicalize().unwrap();
let dst_dir = dst.join(src_dir.strip_prefix(&prefix).unwrap());
if !dst_dir.exists() {
fs::create_dir(&dst_dir).unwrap();
fs::create_dir_all(&dst_dir).unwrap();
}
todo.push(src_dir);
} else {
Expand All @@ -58,25 +69,76 @@ fn decrease_pow(line: io::Result<String>) -> io::Result<String> {
Ok(line)
}

/// Read and parse the contents from `./asm` into a `LibraryContents` struct, serializing it into
/// `assets` folder under `std` namespace.
fn compile_miden_lib(build_dir: &String, dst: PathBuf) -> io::Result<()> {
let namespace =
LibraryNamespace::try_from("miden".to_string()).expect("invalid base namespace");
let version = Version::try_from(env!("CARGO_PKG_VERSION")).expect("invalid cargo version");
let midenlib =
MaslLibrary::read_from_dir(dst.join(ASM_MIDEN_DIR_PATH), namespace, true, version)?;

midenlib.write_to_dir(Path::new(&build_dir).join(ASL_DIR_PATH))?;

Ok(())
}

fn compile_note_scripts(dst: PathBuf) -> io::Result<()> {
let binding = dst.join(ASM_SCRIPTS_DIR_PATH);
let path = Path::new(&binding);

if path.is_dir() {
match fs::read_dir(path) {
Ok(entries) => {
for entry in entries {
match entry {
Ok(file) => {
let file_path = file.path();
let file_path_str =
file_path.to_str().unwrap_or("<invalid UTF-8 filename>");
let file_name = format!(
"{}.masb",
file_path_str.split('/').last().unwrap().trim_end_matches(".masm")
);
let note_script_ast =
ProgramAst::parse(&fs::read_to_string(file_path)?)?;
let note_script_bytes = note_script_ast.to_bytes(AstSerdeOptions {
serialize_imports: true,
});
fs::write(dst.join(ASL_DIR_PATH).join(file_name), note_script_bytes)?;
}
Err(e) => println!("Error reading directory entry: {}", e),
}
}
}
Err(e) => println!("Error reading directory: {}", e),
}
} else {
println!("cargo:rerun-The specified path is not a directory.");
}

Ok(())
}

/// Read and parse the contents from `./asm`.
/// - Compiles contents of asm/miden directory into a Miden library file (.masl) under
/// miden namespace.
/// - Compiles contents of asm/scripts directory into individual .masb files.
#[cfg(not(feature = "docs-rs"))]
fn main() -> io::Result<()> {
// re-build when the masm code changes.
println!("cargo:rerun-if-changed=asm");

// 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 build_dir: String = env::var("OUT_DIR").unwrap();
let src = Path::new(&crate_dir).join(ASM_DIR_PATH);
let dst = Path::new(&build_dir).to_path_buf();
copy_directory(src, &dst);

// if this build has the testing flag set, modify the code and reduce the cost of proof-of-work
match env::var("CARGO_FEATURE_TESTING") {
Ok(ref s) if s == "1" => {
let constants = dst.join(ASM_DIR_PATH).join("sat/internal/constants.masm");
let patched = dst.join(ASM_DIR_PATH).join("sat/internal/constants.masm.patched");
let constants = dst.join(ASM_MIDEN_DIR_PATH).join("sat/internal/constants.masm");
let patched = dst.join(ASM_MIDEN_DIR_PATH).join("sat/internal/constants.masm.patched");

// scope for file handlers
{
Expand All @@ -97,12 +159,11 @@ fn main() -> io::Result<()> {
_ => (),
}

let namespace =
LibraryNamespace::try_from("miden".to_string()).expect("invalid base namespace");
let version = Version::try_from(env!("CARGO_PKG_VERSION")).expect("invalid cargo version");
let stdlib = MaslLibrary::read_from_dir(dst.join(ASM_DIR_PATH), namespace, false, version)?;
// compile the stdlib
compile_miden_lib(&build_dir, dst.clone())?;

stdlib.write_to_dir(Path::new(&build_dir).join(ASL_DIR_PATH))?;
// compile the note scripts separately because they are not part of the stdlib
compile_note_scripts(dst)?;

Ok(())
}
14 changes: 9 additions & 5 deletions miden-lib/src/faucets/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use super::Library;
use crate::{assembler::assembler, auth::AuthScheme};
use assembly::LibraryPath;
use miden_objects::{
accounts::{Account, AccountCode, AccountId, AccountStorage, AccountType, AccountVault},
assembly::ModuleAst,
assets::TokenSymbol,
crypto::merkle::MerkleStore,
utils::{string::ToString, vec},
Expand Down Expand Up @@ -37,11 +38,14 @@ pub fn create_basic_fungible_faucet(
AuthScheme::RpoFalcon512 { pub_key } => pub_key.into(),
};

let account_code_src = include_str!("../../asm/faucets/basic_fungible.masm");
let account_code_ast = ModuleAst::parse(account_code_src)
.map_err(|e| AccountError::AccountCodeAssemblerError(e.into()))?;
let miden = super::MidenLib::default();
let path = "miden::faucets::basic_fungible";
let faucet_code_ast = miden
.get_module_ast(&LibraryPath::new(path).unwrap())
.expect("Getting module AST failed");

let account_assembler = assembler();
let account_code = AccountCode::new(account_code_ast.clone(), &account_assembler)?;
let account_code = AccountCode::new(faucet_code_ast.clone(), &account_assembler)?;

// First check that the metadata is valid.
if decimals > MAX_DECIMALS {
Expand Down
2 changes: 1 addition & 1 deletion miden-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl SatKernel {
// --------------------------------------------------------------------------------------------
/// Returns masm source code which encodes the transaction kernel system procedures.
pub fn kernel() -> &'static str {
include_str!("../asm/sat/kernel.masm")
include_str!("../asm/miden/sat/kernel.masm")
}

// SAT KERNEL MAIN
Expand Down
Loading

0 comments on commit 226a9c4

Please sign in to comment.