diff --git a/CHANGELOG.md b/CHANGELOG.md index fc28ae1a2a..e7930ce653 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ #### CLI - Introduced the `!use` command for the Miden REPL (#1162). +- Introduced a `BLAKE3` hashing example (#1180). ## 0.7.0 (2023-10-11) diff --git a/docs/src/intro/usage.md b/docs/src/intro/usage.md index ab9b60622a..168881fe0d 100644 --- a/docs/src/intro/usage.md +++ b/docs/src/intro/usage.md @@ -64,6 +64,7 @@ Currently, Miden VM can be executed with the following subcommands: * `debug` - this will instantiate a [Miden debugger](../tools/debugger.md) against the specified Miden assembly program and inputs. * `analyze` - this will run a Miden assembly program against specific inputs and will output stats about its execution. * `repl` - this will initiate the [Miden REPL](../tools/repl.md) tool. +* `example` - this will execute a Miden assembly example program, generate a STARK proof of execution and verify it. Currently it is possible to run `blake3` and `fibonacci` examples. All of the above subcommands require various parameters to be provided. To get more detailed help on what is needed for a given subcommand, you can run the following: ``` diff --git a/miden/Cargo.toml b/miden/Cargo.toml index b6046d40f6..f8ccc399d9 100644 --- a/miden/Cargo.toml +++ b/miden/Cargo.toml @@ -47,6 +47,7 @@ sve = ["processor/sve", "prover/sve", "std"] [dependencies] assembly = { package = "miden-assembly", path = "../assembly", version = "0.8", default-features = false } +blake3 = "1.5" clap = { version = "4.4", features = ["derive"], optional = true } env_logger = { version = "0.10", default-features = false, optional = true } hex = { version = "0.4", optional = true } @@ -59,6 +60,7 @@ serde_derive = {version = "1.0", optional = true } serde_json = {version = "1.0", optional = true } stdlib = { package = "miden-stdlib", path = "../stdlib", version = "0.8", default-features = false } verifier = { package = "miden-verifier", path = "../verifier", version = "0.8", default-features = false } +vm-core = { package = "miden-core", path = "../core", version = "0.8", default-features = false } [dev-dependencies] assert_cmd = "2.0" diff --git a/miden/src/examples/blake3.rs b/miden/src/examples/blake3.rs new file mode 100644 index 0000000000..a1c79e0326 --- /dev/null +++ b/miden/src/examples/blake3.rs @@ -0,0 +1,87 @@ +use super::Example; +use miden::{Assembler, DefaultHost, MemAdviceProvider, Program, StackInputs}; +use stdlib::StdLibrary; +use vm_core::utils::group_slice_elements; + +// CONSTANTS +// ================================================================================================ + +const INITIAL_HASH_VALUE: [u32; 8] = [u32::MAX; 8]; + +// EXAMPLE BUILDER +// ================================================================================================ + +pub fn get_example(n: usize) -> Example> { + // generate the program and expected results + let program = generate_blake3_program(n); + let expected_result = compute_hash_chain(n); + println!( + "Generated a program to compute {}-th iteration of BLAKE3 1-to-1 hash; expected result: {:?}", + n, expected_result + ); + + Example { + program, + stack_inputs: StackInputs::try_from_values(INITIAL_HASH_VALUE.iter().map(|&v| v as u64)) + .unwrap(), + host: DefaultHost::default(), + expected_result, + num_outputs: 8, + } +} + +/// Generates a program to compute the `n`-th hash of blake3 1-to-1 hash chain +fn generate_blake3_program(n: usize) -> Program { + let program = format!( + " + use.std::crypto::hashes::blake3 + + begin + repeat.{} + exec.blake3::hash_1to1 + end + end", + n + ); + + Assembler::default() + .with_library(&StdLibrary::default()) + .unwrap() + .compile(&program) + .unwrap() +} + +/// Computes the `n`-th hash of blake3 1-to-1 hash chain +fn compute_hash_chain(n: usize) -> Vec { + let mut bytes: [u8; 32] = INITIAL_HASH_VALUE + .iter() + .flat_map(|v| v.to_le_bytes()) + .collect::>() + .try_into() + .unwrap(); + + for _ in 0..n { + let hasher = blake3::hash(&bytes); + bytes = *hasher.as_bytes(); + } + + group_slice_elements::(&bytes) + .iter() + .map(|&bytes| u32::from_le_bytes(bytes) as u64) + .collect::>() +} + +// EXAMPLE TESTER +// ================================================================================================ + +#[test] +fn test_blake3_example() { + let example = get_example(2); + super::test_example(example, false); +} + +#[test] +fn test_blake3_example_fail() { + let example = get_example(2); + super::test_example(example, true); +} diff --git a/miden/src/examples/mod.rs b/miden/src/examples/mod.rs index c3b3d26a49..efe7c9dd17 100644 --- a/miden/src/examples/mod.rs +++ b/miden/src/examples/mod.rs @@ -4,6 +4,7 @@ use processor::{ExecutionOptions, ExecutionOptionsError, ONE, ZERO}; use std::io::Write; use std::time::Instant; +pub mod blake3; pub mod fibonacci; // EXAMPLE @@ -55,6 +56,13 @@ pub enum ExampleType { #[clap(short = 'n', default_value = "1024")] sequence_length: usize, }, + + /// Compute a chain of the BLAKE3 1-to-1 hashes + Blake3 { + /// Length of the hash chain + #[clap(short = 'n', default_value = "32")] + chain_length: usize, + }, } impl ExampleOptions { @@ -82,6 +90,7 @@ impl ExampleOptions { // instantiate and prepare the example let example = match self.example { ExampleType::Fib { sequence_length } => fibonacci::get_example(sequence_length), + ExampleType::Blake3 { chain_length } => blake3::get_example(chain_length), }; let Example {