From fa13135eb8343640ffceebabb330b8a9c405c5f0 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Mon, 27 Feb 2023 17:36:06 -0800 Subject: [PATCH 01/47] chore: update create versions to v0.5 --- air/Cargo.toml | 4 ++-- assembly/Cargo.toml | 4 ++-- core/Cargo.toml | 2 +- miden/Cargo.toml | 16 ++++++++-------- processor/Cargo.toml | 6 +++--- prover/Cargo.toml | 6 +++--- stdlib/Cargo.toml | 6 +++--- verifier/Cargo.toml | 6 +++--- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/air/Cargo.toml b/air/Cargo.toml index 0ac937fa23..5c4c1a0ba9 100644 --- a/air/Cargo.toml +++ b/air/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "miden-air" -version = "0.4.0" +version = "0.5.0" description = "Algebraic intermediate representation of Miden VM processor" authors = ["miden contributors"] readme = "README.md" @@ -28,7 +28,7 @@ default = ["std"] std = ["vm-core/std", "winter-air/std"] [dependencies] -vm-core = { package = "miden-core", path = "../core", version = "0.4", default-features = false } +vm-core = { package = "miden-core", path = "../core", version = "0.5", default-features = false } winter-air = { package = "winter-air", version = "0.5", default-features = false } [dev-dependencies] diff --git a/assembly/Cargo.toml b/assembly/Cargo.toml index 9e4a472622..c90897c0c6 100644 --- a/assembly/Cargo.toml +++ b/assembly/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "miden-assembly" -version = "0.4.0" +version = "0.5.0" description = "Miden VM assembly language" authors = ["miden contributors"] readme = "README.md" @@ -21,4 +21,4 @@ std = ["vm-core/std"] [dependencies] num_enum = "0.5.10" -vm-core = { package = "miden-core", path = "../core", version = "0.4", default-features = false } +vm-core = { package = "miden-core", path = "../core", version = "0.5", default-features = false } diff --git a/core/Cargo.toml b/core/Cargo.toml index f3a2c82c80..f50e22bd71 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "miden-core" -version = "0.4.0" +version = "0.5.0" description = "Miden VM core components" authors = ["miden contributors"] readme = "README.md" diff --git a/miden/Cargo.toml b/miden/Cargo.toml index 8f6a406819..3801940aab 100644 --- a/miden/Cargo.toml +++ b/miden/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "miden-vm" -version = "0.4.0" +version = "0.5.0" description="Miden virtual machine" authors = ["miden contributors"] readme="README.md" @@ -45,19 +45,19 @@ internals = ["processor/internals"] std = ["assembly/std", "log/std", "processor/std", "prover/std", "verifier/std"] [dependencies] -assembly = { package = "miden-assembly", path = "../assembly", version = "0.4", default-features = false } +assembly = { package = "miden-assembly", path = "../assembly", version = "0.5", default-features = false } env_logger = { version = "0.10", default-features = false, optional = true } hex = { version = "0.4", optional = true } log = { version = "0.4", default-features = false } -processor = { package = "miden-processor", path = "../processor", version = "0.4", default-features = false } -prover = { package = "miden-prover", path = "../prover", version = "0.4", default-features = false } +processor = { package = "miden-processor", path = "../processor", version = "0.5", default-features = false } +prover = { package = "miden-prover", path = "../prover", version = "0.5", default-features = false } rustyline = { version = "10.0.0", default-features = false, optional = true} serde = {version = "1.0.117", optional = true } serde_derive = {version = "1.0.117", optional = true } serde_json = {version = "1.0.59", optional = true } -stdlib = { package = "miden-stdlib", path = "../stdlib", version = "0.3", default-features = false } +stdlib = { package = "miden-stdlib", path = "../stdlib", version = "0.4", default-features = false } structopt = { version = "0.3", default-features = false, optional = true } -verifier = { package = "miden-verifier", path = "../verifier", version = "0.4", default-features = false } +verifier = { package = "miden-verifier", path = "../verifier", version = "0.5", default-features = false } [dev-dependencies] assert_cmd = "2.0" @@ -70,8 +70,8 @@ proptest = "1.0.0" rand-utils = { package = "winter-rand-utils", version = "0.5" } sha2 = "0.10" sha3 = "0.10" -test-case = "2.2.2" -vm-core = { package = "miden-core", path = "../core", version = "0.4", default-features = false } +test-case = "3.0.0" +vm-core = { package = "miden-core", path = "../core", version = "0.5", default-features = false } winterfell = { package = "winter-prover", version = "0.5", default-features = false } winter-fri = { package = "winter-fri", version = "0.5" } winter-utils = { package = "winter-utils", version = "0.5" } diff --git a/processor/Cargo.toml b/processor/Cargo.toml index fca52141e7..490d340516 100644 --- a/processor/Cargo.toml +++ b/processor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "miden-processor" -version = "0.4.0" +version = "0.5.0" description = "Miden VM processor" authors = ["miden contributors"] readme = "README.md" @@ -23,12 +23,12 @@ std = ["vm-core/std", "winter-prover/std", "log/std"] [dependencies] log = "0.4.14" -vm-core = { package = "miden-core", path = "../core", version = "0.4", default-features = false } +vm-core = { package = "miden-core", path = "../core", version = "0.5", default-features = false } winter-prover = { package = "winter-prover", version = "0.5", default-features = false } [dev-dependencies] logtest = { version = "2.0", default-features = false } -miden-assembly = { package = "miden-assembly", path = "../assembly", version = "0.4", default-features = false } +miden-assembly = { package = "miden-assembly", path = "../assembly", version = "0.5", default-features = false } rand-utils = { package = "winter-rand-utils", version = "0.5" } winter-fri = { package = "winter-fri", version = "0.5" } winter-utils = { package = "winter-utils", version = "0.5" } diff --git a/prover/Cargo.toml b/prover/Cargo.toml index b4ddace6f7..174478b162 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "miden-prover" -version = "0.4.0" +version = "0.5.0" description = "Miden VM prover" authors = ["miden contributors"] readme = "README.md" @@ -17,7 +17,7 @@ default = ["std"] std = ["air/std", "processor/std", "log/std", "winter-prover/std"] [dependencies] -air = { package = "miden-air", path = "../air", version = "0.4", default-features = false } +air = { package = "miden-air", path = "../air", version = "0.5", default-features = false } log = { version = "0.4", default-features = false } -processor = { package = "miden-processor", path = "../processor", version = "0.4", default-features = false } +processor = { package = "miden-processor", path = "../processor", version = "0.5", default-features = false } winter-prover = { package = "winter-prover", version = "0.5", default-features = false } diff --git a/stdlib/Cargo.toml b/stdlib/Cargo.toml index 9e4d96371e..6ece178351 100644 --- a/stdlib/Cargo.toml +++ b/stdlib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "miden-stdlib" -version = "0.3.0" +version = "0.4.0" description = "Miden VM standard library" authors = ["miden contributors"] readme = "README.md" @@ -16,7 +16,7 @@ bench = false doctest = false [dependencies] -assembly = { package = "miden-assembly", default-features = false, path = "../assembly", version = "0.4" } +assembly = { package = "miden-assembly", default-features = false, path = "../assembly", version = "0.5" } [build-dependencies] -assembly = { package = "miden-assembly", path = "../assembly", version = "0.4" } +assembly = { package = "miden-assembly", path = "../assembly", version = "0.5" } diff --git a/verifier/Cargo.toml b/verifier/Cargo.toml index 8a345d3be5..bb8e978b9b 100644 --- a/verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "miden-verifier" -version = "0.4.0" +version = "0.5.0" description="Miden VM execution verifier" authors = ["miden contributors"] readme="README.md" @@ -20,6 +20,6 @@ default = ["std"] std = ["air/std", "vm-core/std", "winter-verifier/std"] [dependencies] -air = { package = "miden-air", path = "../air", version = "0.4", default-features = false } -vm-core = { package = "miden-core", path = "../core", version = "0.4", default-features = false } +air = { package = "miden-air", path = "../air", version = "0.5", default-features = false } +vm-core = { package = "miden-core", path = "../core", version = "0.5", default-features = false } winter-verifier = { package = "winter-verifier", version = "0.5", default-features = false } From 2b63e2206a2482fcde32b84c85d207f09600c419 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Tue, 28 Feb 2023 21:26:16 +0100 Subject: [PATCH 02/47] feat: added pre-commit hook config --- .pre-commit-config.yaml | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..d00cf263ff --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,43 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-json + - id: check-toml + - id: pretty-format-json + - id: check-added-large-files + - id: check-case-conflict + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: detect-private-key +- repo: https://github.com/hackaugusto/pre-commit-cargo + rev: v1.0.0 + hooks: + # Allows cargo fmt to modify the source code prior to the commit + - id: cargo + name: Cargo fmt + args: ["+stable", "fmt", "--all"] + stages: [commit] + # Requires code to be properly formatted prior to pushing upstream + - id: cargo + name: Cargo fmt --check + args: ["+stable", "fmt", "--all", "--check"] + stages: [push, manual] + - id: cargo + name: Cargo check --all-targets + args: ["+stable", "check", "--all-targets"] + - id: cargo + name: Cargo check --all-targets --no-default-features + args: ["+stable", "check", "--all-targets", "--no-default-features"] + - id: cargo + name: Cargo check --all-targets --all-features + args: ["+stable", "check", "--all-targets", "--all-features"] + # Unlike fmt, clippy will not be automatically applied + - id: cargo + name: Cargo clippy + args: ["+nightly", "clippy", "--workspace", "--", "--deny", "clippy::all", "--deny", "warnings"] From 7e025f9b5d0feccfc2c9b1630f951a4256024906 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Tue, 28 Feb 2023 21:27:26 +0100 Subject: [PATCH 03/47] chore: first run pre-commit hook --- .github/pull_request_template.md | 2 +- CONTRIBUTING.md | 2 +- air/README.md | 2 +- assembly/README.md | 2 +- .../src/assembler/instruction/ext2_ops.rs | 6 +- assembly/src/parsers/mod.rs | 4 +- assembly/src/parsers/tests.rs | 64 ++--- core/README.md | 2 +- docs/.gitignore | 2 +- docs/README.md | 4 +- docs/src/background.md | 1 - docs/src/design/chiplets/bitwise.md | 2 +- docs/src/design/chiplets/hasher.md | 6 +- docs/src/design/chiplets/main.md | 2 +- docs/src/design/decoder/constraints.md | 6 +- docs/src/design/multiset.md | 10 +- docs/src/design/programs.md | 6 +- docs/src/design/range.md | 6 +- docs/src/design/stack/crypto_ops.md | 2 +- docs/src/design/stack/field_ops.md | 2 +- docs/src/design/stack/main.md | 2 +- docs/src/design/stack/op_constraints.md | 2 +- docs/src/design/stack/stack_ops.md | 4 +- docs/src/design/stack/system_ops.md | 6 +- docs/src/design/stack/u32_ops.md | 2 +- docs/src/intro/overview.md | 2 +- docs/src/intro/usage.md | 4 +- .../user_docs/assembly/code_organization.md | 2 +- .../assembly/cryptographic_operations.md | 2 +- .../user_docs/assembly/field_operations.md | 2 +- docs/src/user_docs/assembly/io_operations.md | 3 +- .../user_docs/assembly/stack_manipulation.md | 1 - docs/src/user_docs/stdlib/main.md | 2 +- docs/src/user_docs/stdlib/sys.md | 2 +- miden/README.md | 2 +- miden/examples/fib/fib.inputs | 2 +- miden/examples/fib/fib.masm | 2 +- miden/src/examples/fibonacci.rs | 2 +- miden/src/repl/mod.rs | 2 +- miden/tests/integration/flow_control/mod.rs | 4 +- .../operations/decorators/advice.rs | 10 +- .../operations/decorators/asmop.rs | 2 +- .../operations/io_ops/local_ops.rs | 28 +- .../tests/integration/stdlib/crypto/falcon.rs | 262 +++++++++--------- .../stdlib/crypto/fri/remainder.rs | 2 +- miden/tests/integration/stdlib/math/ntt512.rs | 2 +- .../stdlib/math/secp256k1/group.rs | 10 +- processor/src/chiplets/bitwise/mod.rs | 2 +- processor/src/range/mod.rs | 2 +- stdlib/README.md | 2 +- stdlib/asm/crypto/dsa/ecdsa/secp256k1.masm | 4 +- stdlib/asm/crypto/dsa/falcon.masm | 18 +- stdlib/asm/crypto/fri/ext2fri.masm | 18 +- stdlib/asm/crypto/fri/frie2f4.masm | 44 +-- stdlib/asm/crypto/hashes/blake3.masm | 22 +- stdlib/asm/crypto/hashes/keccak256.masm | 34 +-- stdlib/asm/crypto/hashes/sha256.masm | 24 +- stdlib/asm/math/ecgfp5/base_field.masm | 38 +-- stdlib/asm/math/ecgfp5/group.masm | 28 +- stdlib/asm/math/ecgfp5/scalar_field.masm | 18 +- stdlib/asm/math/ntt512.masm | 10 +- stdlib/asm/math/poly512.masm | 14 +- stdlib/asm/math/secp256k1/base_field.masm | 26 +- stdlib/asm/math/secp256k1/group.masm | 124 ++++----- stdlib/asm/math/secp256k1/scalar_field.masm | 14 +- stdlib/asm/math/u256.masm | 2 +- stdlib/asm/sys.masm | 2 +- 67 files changed, 471 insertions(+), 474 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 8dfedbe9a6..ffd0f236fb 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -6,4 +6,4 @@ - Commit messages and codestyle follow [conventions](./CONTRIBUTING.md). - Relevant issues are linked in the PR description. - Tests added for new functionality. -- Documentation/comments updated according to changes. \ No newline at end of file +- Documentation/comments updated according to changes. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d85b19a5b..7042a42553 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,7 @@ We are using [Github Flow](https://docs.github.com/en/get-started/quickstart/git ### Branching - The current active branch is `next`. Every branch with a fix/feature must be forked from `next`. -- The branch name should contain a short issue/feature description separated with hyphens [(kebab-case)](https://en.wikipedia.org/wiki/Letter_case#Kebab_case). +- The branch name should contain a short issue/feature description separated with hyphens [(kebab-case)](https://en.wikipedia.org/wiki/Letter_case#Kebab_case). For example, if the issue title is `Fix functionality X in component Y` then the branch name will be something like: `fix-x-in-y`. diff --git a/air/README.md b/air/README.md index 452bc5bb84..eca38a5ed4 100644 --- a/air/README.md +++ b/air/README.md @@ -20,4 +20,4 @@ If you'd like to learn more about AIR, the following blog posts from StarkWare a * [StarkDEX Deep Dive: the STARK Core Engine](https://medium.com/starkware/starkdex-deep-dive-the-stark-core-engine-497942d0f0ab) ## License -This project is [MIT licensed](../LICENSE). \ No newline at end of file +This project is [MIT licensed](../LICENSE). diff --git a/assembly/README.md b/assembly/README.md index c6477629df..c78f9c97a1 100644 --- a/assembly/README.md +++ b/assembly/README.md @@ -95,4 +95,4 @@ let assembler = Assembler::default() ``` ## License -This project is [MIT licensed](../LICENSE). \ No newline at end of file +This project is [MIT licensed](../LICENSE). diff --git a/assembly/src/assembler/instruction/ext2_ops.rs b/assembly/src/assembler/instruction/ext2_ops.rs index 6313a74e04..1349467629 100644 --- a/assembly/src/assembler/instruction/ext2_ops.rs +++ b/assembly/src/assembler/instruction/ext2_ops.rs @@ -60,9 +60,9 @@ pub fn ext2_div(span: &mut SpanBuilder) -> Result, AssemblyErr Ext2Mul, // [b1', b0', 0, 1, a1, a0, ...] MovUp2, // [0, b1', b0', 1, a1, a0, ...] Eqz, // [1, b1', b0', 1, a1, a0, ...] - Assert, // [b1', b0', 1, a1, a0, ...] - MovUp2, // [1, b1', b0', a1, a0, ...] - Assert, // [b1', b0', a1, a0, ...] + Assert, // [b1', b0', 1, a1, a0, ...] + MovUp2, // [1, b1', b0', a1, a0, ...] + Assert, // [b1', b0', a1, a0, ...] Ext2Mul, // [b1', b0', a1*b1', a0*b0', ...] Drop, // [b0', a1*b1', a0*b0'...] Drop // [a1*b1', a0*b0'...] diff --git a/assembly/src/parsers/mod.rs b/assembly/src/parsers/mod.rs index dea2013c9e..f58704cd02 100644 --- a/assembly/src/parsers/mod.rs +++ b/assembly/src/parsers/mod.rs @@ -381,7 +381,7 @@ fn parse_const_value(op: &Token, const_value: &str) -> Result let range = 0..Felt::MODULUS; range.contains(&result).then_some(result).ok_or_else(|| ParsingError::invalid_const_value(op, const_value, format!( - "constant value must be greater than or equal to {lower_bound} and less than or equal to {upper_bound}", lower_bound = bound_into_included_u64(range.start_bound(), true), + "constant value must be greater than or equal to {lower_bound} and less than or equal to {upper_bound}", lower_bound = bound_into_included_u64(range.start_bound(), true), upper_bound = bound_into_included_u64(range.end_bound(), false) ) .as_str(),)) @@ -407,7 +407,7 @@ where op, param_idx, format!( - "parameter value must be greater than or equal to {lower_bound} and less than or equal to {upper_bound}", lower_bound = bound_into_included_u64(range.start_bound(), true), + "parameter value must be greater than or equal to {lower_bound} and less than or equal to {upper_bound}", lower_bound = bound_into_included_u64(range.start_bound(), true), upper_bound = bound_into_included_u64(range.end_bound(), false) ) .as_str(), diff --git a/assembly/src/parsers/tests.rs b/assembly/src/parsers/tests.rs index 9d0898ce70..b9c8d602e1 100644 --- a/assembly/src/parsers/tests.rs +++ b/assembly/src/parsers/tests.rs @@ -89,12 +89,12 @@ fn test_ast_parsing_program_u32() { #[test] fn test_ast_parsing_program_proc() { let source = "\ - proc.foo.1 + proc.foo.1 loc_load.0 end - proc.bar.2 + proc.bar.2 padw - end + end begin exec.foo exec.bar @@ -138,7 +138,7 @@ fn test_ast_parsing_program_proc() { #[test] fn test_ast_parsing_module() { let source = "\ - export.foo.1 + export.foo.1 loc_load.0 end"; let mut procedures: LocalProcMap = BTreeMap::new(); @@ -404,7 +404,7 @@ fn test_unterminated_if() { fn test_ast_parsing_simple_docs() { let source = "\ #! proc doc - export.foo.1 + export.foo.1 loc_load.0 end"; @@ -427,29 +427,29 @@ fn test_ast_parsing_simple_docs() { #[test] fn test_ast_parsing_module_docs() { let source = "\ -#! Test documenation for the whole module in parsing test. Lorem ipsum dolor sit amet, -#! consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +#! Test documenation for the whole module in parsing test. Lorem ipsum dolor sit amet, +#! consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. #! This comment is intentionally longer than 256 characters, since we need to be sure that the size #! of the comments is correctly parsed. There was a bug here earlier. -#! Test documenation for export procedure foo in parsing test. Lorem ipsum dolor sit amet, -#! consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +#! Test documenation for export procedure foo in parsing test. Lorem ipsum dolor sit amet, +#! consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. #! This comment is intentionally longer than 256 characters, since we need to be sure that the size #! of the comments is correctly parsed. There was a bug here earlier. -export.foo.1 +export.foo.1 loc_load.0 end -#! Test documenation for internal procedure bar in parsing test. Lorem ipsum dolor sit amet, +#! Test documenation for internal procedure bar in parsing test. Lorem ipsum dolor sit amet, #! consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna -#! aliqua. -proc.bar.2 +#! aliqua. +proc.bar.2 padw end -#! Test documenation for export procedure baz in parsing test. Lorem ipsum dolor sit amet, +#! Test documenation for export procedure baz in parsing test. Lorem ipsum dolor sit amet, #! consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna -#! aliqua. +#! aliqua. export.baz.3 padw push.0 @@ -544,10 +544,10 @@ fn test_ast_parsing_module_docs_fail() { #! module doc #! proc doc - export.foo.1 + export.foo.1 loc_load.0 end - + #! malformed doc "; parse_module(source) @@ -555,10 +555,10 @@ fn test_ast_parsing_module_docs_fail() { let source = "\ #! proc doc - export.foo.1 + export.foo.1 loc_load.0 end - + #! malformed doc "; parse_module(source) @@ -566,17 +566,17 @@ fn test_ast_parsing_module_docs_fail() { let source = "\ #! module doc - + #! malformed doc "; parse_module(source) .expect_err("Procedure comment is not immediately followed by a procedure declaration."); let source = "\ - export.foo.1 + export.foo.1 loc_load.0 end - + #! malformed doc "; parse_module(source) @@ -585,10 +585,10 @@ fn test_ast_parsing_module_docs_fail() { let source = "\ #! module doc - export.foo.1 + export.foo.1 loc_load.0 end - + #! malformed doc "; parse_module(source) @@ -596,7 +596,7 @@ fn test_ast_parsing_module_docs_fail() { let source = "\ #! proc doc - export.foo.1 + export.foo.1 #! malformed doc loc_load.0 end @@ -621,12 +621,12 @@ fn test_ast_program_serde_simple() { #[test] fn test_ast_program_serde_local_procs() { let source = "\ - proc.foo.1 + proc.foo.1 loc_load.0 end - proc.bar.2 + proc.bar.2 padw - end + end begin exec.foo exec.bar @@ -641,10 +641,10 @@ fn test_ast_program_serde_local_procs() { #[test] fn test_ast_program_serde_exported_procs() { let source = "\ - export.foo.1 + export.foo.1 loc_load.0 end - export.bar.2 + export.bar.2 padw end"; let module = parse_module(source).unwrap(); @@ -661,7 +661,7 @@ fn test_ast_program_serde_control_flow() { repeat.3 push.1 push.0.1 - end + end if.true and @@ -669,7 +669,7 @@ fn test_ast_program_serde_control_flow() { else padw end - + while.true push.5.7 u32checked_add diff --git a/core/README.md b/core/README.md index 352d8766ce..3d2175b6b1 100644 --- a/core/README.md +++ b/core/README.md @@ -1,4 +1,4 @@ -# Miden core +# Miden core This crate contains core components used by Miden VM. These components include: * Miden VM instruction set, defined in the [Operation](/../main/core/src/operations/mod.rs) struct. diff --git a/docs/.gitignore b/docs/.gitignore index e9c072897d..7585238efe 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1 +1 @@ -book \ No newline at end of file +book diff --git a/docs/README.md b/docs/README.md index 0d9431b181..92819d216d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,7 +1,7 @@ -# Miden docs +# Miden docs This crate contains source files and assets for [Miden VM documentation](https://0xpolygonmiden.github.io/miden-vm/). All doc files are written in Markdown and are converted into an online book using [mdBook](https://github.com/rust-lang/mdBook) utility. ## License -This project is [MIT licensed](../LICENSE). \ No newline at end of file +This project is [MIT licensed](../LICENSE). diff --git a/docs/src/background.md b/docs/src/background.md index 321a905867..6118cf266b 100644 --- a/docs/src/background.md +++ b/docs/src/background.md @@ -25,4 +25,3 @@ StarkWare's STARK Math blog series: StarkWare's STARK tutorial: * [STARK 101](https://starkware.co/stark-101/) - \ No newline at end of file diff --git a/docs/src/design/chiplets/bitwise.md b/docs/src/design/chiplets/bitwise.md index 55b6367882..cc5a03d542 100644 --- a/docs/src/design/chiplets/bitwise.md +++ b/docs/src/design/chiplets/bitwise.md @@ -33,7 +33,7 @@ In the above, the columns have the following meanings: Let's illustrate the above table on a concrete example. For simplicity, we'll use 16-bit values, and thus, we'll only need 4 rows to complete the operation (rather than 8 for 32-bit values). Let's say $a = 41851$ (`b1010_0011_0111_1011`) and $b = 40426$ (`b1001_1101_1110_1010`), then $and(a, b) = 33130$ (`b1000_0001_0110_1010`). The table for this computation looks like so: | a | b | x0 | x1 | x2 | x3 | y0 | y1 | y2 | y3 | zp | z | -| :---: | :---: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :----: | :---: | +| :---: | :---: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :----: | :---: | | 10 | 9 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 8 | | 163 | 157 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 8 | 129 | | 2615 | 2526 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 129 | 2070 | diff --git a/docs/src/design/chiplets/hasher.md b/docs/src/design/chiplets/hasher.md index 595d703c1b..c1021e8215 100644 --- a/docs/src/design/chiplets/hasher.md +++ b/docs/src/design/chiplets/hasher.md @@ -163,7 +163,7 @@ Verifying a Merkle path involves the following steps: 1. Initialize hasher state with the leaf and the first node of the path, setting all capacity elements to $0$s. a. Also, initialize the index register to the leaf's index value. -2. Apply Rescue Prime Optimized permutation. +2. Apply Rescue Prime Optimized permutation. a. Make sure the index value doesn't change during this step. 3. Copy the result of the hash to the next row, and absorb the next node of the Merkle path into the hasher state. a. Remove a single bit from the index, and use it to determine how to place the copied result and absorbed node in the state. @@ -343,7 +343,7 @@ When absorbing the next node during Merkle path computation (i.e., $f_{mp} + f_{ >$$ (f_{mp} + f_{mv} + f_{mu}) \cdot ((1 - b) \cdot (h_{j +4}' - h_{j+4}) + b \cdot (h_{j + 8}' - h_{j + 4})) = 0 \text{ | degree} = 6 -$$ +$$ Note, that when a computation is completed (i.e., $f_{out}=1$), the next hasher state is unconstrained. @@ -452,4 +452,4 @@ $$ The above means that whenever we start a new computation which is not the computation of the new Merkle root, the sibling table must be empty. Thus, after the hash chiplet computes the old Merkle root, the only way to clear the table is to compute the new Merkle root. -Together with boundary constraints enforcing that $p_1=1$ at the first and last rows, the above constraints ensure that if a node was included into $p_1$ as a part of computing the old Merkle root, the same node must be removed from $p_1$ as a part of computing the new Merkle root. \ No newline at end of file +Together with boundary constraints enforcing that $p_1=1$ at the first and last rows, the above constraints ensure that if a node was included into $p_1$ as a part of computing the old Merkle root, the same node must be removed from $p_1$ as a part of computing the new Merkle root. diff --git a/docs/src/design/chiplets/main.md b/docs/src/design/chiplets/main.md index da28bc2b76..1135918ae4 100644 --- a/docs/src/design/chiplets/main.md +++ b/docs/src/design/chiplets/main.md @@ -155,4 +155,4 @@ s_0 \cdot s_1 \cdot (s_1 - s'_1) \text{ | degree} = 3 \\ s_0 \cdot s_1 \cdot s_2 \cdot (s_2 - s'_2) \text{ | degree} = 4 \\ $$ -In other words, the above constraints enforce that if a selector is $0$ in the current row, then it must be either $0$ or $1$ in the next row; if it is $1$ in the current row, it must be $1$ in the next row. \ No newline at end of file +In other words, the above constraints enforce that if a selector is $0$ in the current row, then it must be either $0$ or $1$ in the next row; if it is $1$ in the current row, it must be $1$ in the next row. diff --git a/docs/src/design/decoder/constraints.md b/docs/src/design/decoder/constraints.md index 533b516ffd..bfccee34b9 100644 --- a/docs/src/design/decoder/constraints.md +++ b/docs/src/design/decoder/constraints.md @@ -131,7 +131,7 @@ $$ f_{ctrli} = f_{join} + f_{split} + f_{loop} + f_{call} + f_{syscall} \text{ | degree} = 6 $$ -In the above, $f_{ctrli}$ is set to $1$ when a control flow operation that signifies the initialization of a control block is being executed on the VM. Otherwise, it is set to $0$. +In the above, $f_{ctrli}$ is set to $1$ when a control flow operation that signifies the initialization of a control block is being executed on the VM. Otherwise, it is set to $0$. $$ d = \sum_{b=0}^6(b_i \cdot 2^i) @@ -461,7 +461,7 @@ When executing `SPAN` or `RESPAN` operations the next value of `op_index` must b > $$ (f_{span} + f_{respan}) \cdot ox' = 0 \text{ | degree} = 7 -$$ +$$ When starting a new operation group inside a *span* block, the next value of `op_index` must be set to $0$. Note that we multiply by $sp$ to exclude the cases when the group count is decremented because of `SPAN` or `RESPAN` operations: @@ -553,7 +553,7 @@ We also define a flag which is set to $1$ when a group needs to be removed from $$ f_{dg} = sp \cdot \Delta gc -$$ +$$ The above says that we remove groups from the op group table whenever group count is decremented. We multiply by $sp$ to exclude the cases when the group count is decremented due to `SPAN` or `RESPAN` operations. diff --git a/docs/src/design/multiset.md b/docs/src/design/multiset.md index dd5ab5eb49..ea2f8d1c7d 100644 --- a/docs/src/design/multiset.md +++ b/docs/src/design/multiset.md @@ -36,7 +36,7 @@ Since Miden uses a 64-bit field, each running product column needs to be represe Virtual tables can be used to store intermediate data which is computed at one cycle and used at a different cycle. When the data is computed, the row is added to the table, and when it is used later, the row is deleted from the table. Thus, all that needs to be proved is the data consistency between the row that was added and the row that was deleted. -The consistency of a virtual table can be proved with a single trace column $p$, which keeps a running product of rows that were inserted into and deleted from the table. This is done by reducing each row to a single value, multiplying the value into $p$ when the row is inserted, and dividing the value out of $p$ when the row is removed. Thus, at any step of the computation, $p$​ will contain a product of all rows currently in the table. +The consistency of a virtual table can be proved with a single trace column $p$, which keeps a running product of rows that were inserted into and deleted from the table. This is done by reducing each row to a single value, multiplying the value into $p$ when the row is inserted, and dividing the value out of $p$ when the row is removed. Thus, at any step of the computation, $p$​ will contain a product of all rows currently in the table. The initial value of $p$​ is set to 1. Thus, if the table is empty by the time Miden VM finishes executing a program (we added and then removed exactly the same set of rows), the final value of $p$​ will also be equal to 1. The initial and final values are enforced via boundary constraints. @@ -85,13 +85,13 @@ One strategy for improving the efficiency of a zero knowledge virtual machine is These specialized components are designed to prove the internal correctness of the execution of the operations they support. However, in isolation they cannot make any guarantees about the source of the input data or the destination of the output data. -In order to prove that the inputs and outputs specified by the main circuit match the inputs and outputs provably executed in the specialized component, some kind of provable communication bus is needed. +In order to prove that the inputs and outputs specified by the main circuit match the inputs and outputs provably executed in the specialized component, some kind of provable communication bus is needed. This bus is typically implemented as some kind of lookup argument, and in Miden VM in particular we use multiset checks. ### Implementation -A `bus` can be implemented as a single trace column $b$ where a request can be sent to a specific component and a corresponding response will be sent back by that component. +A `bus` can be implemented as a single trace column $b$ where a request can be sent to a specific component and a corresponding response will be sent back by that component. The values in this column contain a running product of the communication with the component as follows: @@ -106,7 +106,7 @@ Note that the order of the requests and responses does not matter, as long as th These constraints can be expressed in a general way with the 2 following requirements: -- The lookup value must be computed using random values $\alpha_0, \alpha_1$, etc. that are provided by the verifier after the prover has committed to the main execution trace. +- The lookup value must be computed using random values $\alpha_0, \alpha_1$, etc. that are provided by the verifier after the prover has committed to the main execution trace. - The lookup value must include all uniquely identifying information for the component/operation and its inputs and outputs. Given an example operation $op_{ex}$ with inputs $i_0, ..., i_n$ and outputs $o_0, ..., o_m$, the lookup value can be computed as follows: @@ -121,7 +121,7 @@ The constraint for sending this to the bus as a response would be: $$b' = b \cdot lookup$$ -However, these constraints must be combined, since it's possible that requests and responses both occur during the same cycle. +However, these constraints must be combined, since it's possible that requests and responses both occur during the same cycle. To combine them, let $u_{lookup}$ be the request value and let $v_{lookup}$ be the response value. These values are both computed the same way as shown above, but the data sources are different, since the input/output values used to compute $u_{lookup}$ come from the trace of the component that's "offloading" the computation, while the input/output values used to compute $v_{lookup}$ come from the trace of the specialized component. diff --git a/docs/src/design/programs.md b/docs/src/design/programs.md index 405ef7bca4..2c54e0e769 100644 --- a/docs/src/design/programs.md +++ b/docs/src/design/programs.md @@ -28,7 +28,7 @@ A *loop* block must always have one child, and thus, cannot be a leaf node in th ### Call block -A **call** block is used to describe a function call which is executed in a [user context](../user_docs/assembly/execution_contexts.md). When the VM encounters a *call* block, it creates a new user context, then executes a program which hashes to the target specified by the *call* block in the new context. Thus, in order to execute a *call* block, the VM must be aware of a program with the specified hash. Otherwise, the execution fails. At the end of the *call* block, execution returns to the previous context. +A **call** block is used to describe a function call which is executed in a [user context](../user_docs/assembly/execution_contexts.md). When the VM encounters a *call* block, it creates a new user context, then executes a program which hashes to the target specified by the *call* block in the new context. Thus, in order to execute a *call* block, the VM must be aware of a program with the specified hash. Otherwise, the execution fails. At the end of the *call* block, execution returns to the previous context. When executing a *call* block, the VM does the following: @@ -42,7 +42,7 @@ A *call* block does not have any children. Thus, it must be leaf node in the tre ### Syscall block -A **syscall** block is used to describe a function call which is executed in the [root context](../user_docs/assembly/execution_contexts.md). When the VM encounters a *syscall* block, it returns to the root context, then executes a program which hashes to the target specified by the *syscall* block. Thus, in order to execute a *syscall* block, the VM must be aware of a program with the specified hash, and that program must belong to the kernel against which the code is compiled. Otherwise, the execution fails. At the end of the *syscall* block, execution returns to the previous context. +A **syscall** block is used to describe a function call which is executed in the [root context](../user_docs/assembly/execution_contexts.md). When the VM encounters a *syscall* block, it returns to the root context, then executes a program which hashes to the target specified by the *syscall* block. Thus, in order to execute a *syscall* block, the VM must be aware of a program with the specified hash, and that program must belong to the kernel against which the code is compiled. Otherwise, the execution fails. At the end of the *syscall* block, execution returns to the previous context. When executing a *syscall* block, the VM does the following: 1. Checks if a *syscall* is already being executed and fails if so. @@ -107,7 +107,7 @@ Execution of this program would proceed as follows: 8. If the VM does enter the loop, then after operation $d_n$ is executed, the VM will pop the value off the top of the stack again. If the popped value is $1$, the VM will execute block $d$ again, and again until the top of the stack becomes $0$. Once the top of the stack becomes $0$, the VM will exit the loop and will move up the tree executing first block $e$, then $f$. ## Program hash computation -Every Miden VM program can be reduced to a unique hash value. Specifically, it is infeasible to find two Miden VM programs with distinct semantics which hash to the same value. Padding a program with `NOOP`s does not change a program's execution semantics, and thus, programs which differ only in the number and/or placement of `NOOP`s may hash to the same value, although in most cases padding with `NOOP` should not affect program hash. +Every Miden VM program can be reduced to a unique hash value. Specifically, it is infeasible to find two Miden VM programs with distinct semantics which hash to the same value. Padding a program with `NOOP`s does not change a program's execution semantics, and thus, programs which differ only in the number and/or placement of `NOOP`s may hash to the same value, although in most cases padding with `NOOP` should not affect program hash. To prevent program hash collisions we implement domain separation across the variants of control blocks. We define the domain value to be the opcode of the operation that initializes the control block. diff --git a/docs/src/design/range.md b/docs/src/design/range.md index 71147306c5..e1cdab9562 100644 --- a/docs/src/design/range.md +++ b/docs/src/design/range.md @@ -186,7 +186,7 @@ As previously described, the columns have the following meanings: - The 8-bit section of the trace contains the range checks required to ensure internal consistency of the Range Checker. - The 16-bit section of the trace contains the range checks required by other components (e.g. the stack and the memory chiplet). - $s_0$ and $s_1$ are selector columns that are combined into flags to indicate the number of times the value in that row should be range checked (included into the running product). With these flags, values can be included 0, 1, 2, or 4 times per row in the execution trace. (Values can be included more times by having multiple trace rows with the same value). -- $v$ contains the values to be range checked. +- $v$ contains the values to be range checked. - During the 8-bit section of the trace (when $t = 0$), these values go from $0$ to $255$ and must either stay the same or increase by one at each step. - During the 16-bit section of the trace (when $t = 1$), these values go from $0$ to $65535$. Values must either stay the same or increase by less than $256$ at each step. - The final 2 rows of the 16-bit section of the trace must both equal $65535$. The extra value of $65535$ is required in order to [pad the trace](./multiset.md#length-of-running-product-columns) so the [$b_{range}$](#communication-bus) running product bus column can be computed correctly. @@ -236,7 +236,7 @@ In addition to the transition constraints described above, we also need to enfor ### 8-bit range checks table -The 8-bit range checks [virtual table](./multiset.md#virtual-tables) is used to enforce the internal correctness of the 16-bit section of the Range Checker (where range checks for user operations and other components are executed). +The 8-bit range checks [virtual table](./multiset.md#virtual-tables) is used to enforce the internal correctness of the 16-bit section of the Range Checker (where range checks for user operations and other components are executed). This table can be thought of as a virtual table that contains all 8-bit range checks required to ensure correctness of the 16-bit section: @@ -307,4 +307,4 @@ If $b_{range}$ is initialized to $1$ and the values sent to the bus by other VM In addition to the transition constraint described above, we also need to enforce the following boundary constraint: -- The value of $b_{range}$ in the first and last rows is $1$. \ No newline at end of file +- The value of $b_{range}$ in the first and last rows is $1$. diff --git a/docs/src/design/stack/crypto_ops.md b/docs/src/design/stack/crypto_ops.md index 09b99d5803..af7ac440e8 100644 --- a/docs/src/design/stack/crypto_ops.md +++ b/docs/src/design/stack/crypto_ops.md @@ -75,7 +75,7 @@ The effect of this operation on the rest of the stack is: ## MRUPDATE The `MRUPDATE` operation computes a new root of a Merkle tree where a node at the specified position is updated to the specified value. - + The stack is expected to be arranged as follows (from the top): - old value of the node, 4 element ($V$ in the below image) - depth of the node, 1 element ($d$ in the below image) diff --git a/docs/src/design/stack/field_ops.md b/docs/src/design/stack/field_ops.md index 9b44d88176..c4306c7456 100644 --- a/docs/src/design/stack/field_ops.md +++ b/docs/src/design/stack/field_ops.md @@ -249,4 +249,4 @@ s_3' - s_1 \cdot s_3 + 2 \cdot s_0 \cdot s_2 = 0 \text{ | degree} = 2 $$ The effect on the rest of the stack is: -* **No change** starting from position $4$. \ No newline at end of file +* **No change** starting from position $4$. diff --git a/docs/src/design/stack/main.md b/docs/src/design/stack/main.md index 6b4dfa76b8..6f2a930612 100644 --- a/docs/src/design/stack/main.md +++ b/docs/src/design/stack/main.md @@ -204,4 +204,4 @@ $$ In addition to the constraints described above, we also need to enforce the following boundary constraints: * $b_0 = 16$ at the first and at the last row of execution trace. * $b_1 = 0$ at the first and at the last row of execution trace. -* $p_1 = 1$ at the first and at the last row of execution trace. \ No newline at end of file +* $p_1 = 1$ at the first and at the last row of execution trace. diff --git a/docs/src/design/stack/op_constraints.md b/docs/src/design/stack/op_constraints.md index 02089d689b..e3c2a0d80b 100644 --- a/docs/src/design/stack/op_constraints.md +++ b/docs/src/design/stack/op_constraints.md @@ -272,4 +272,4 @@ $$ f_{ctrl} = f_{span,join,split,loop} + f_{end,repeat,respan,halt} + f_{call} + f_{syscall} \text{ | degree} = 4 $$ -Note that the degree of $f_{end,repeat,respan,halt}$ can be reduced to degree 2 using the extra column, but this will not affect the degree of the $f_{ctrl}$ constraint. \ No newline at end of file +Note that the degree of $f_{end,repeat,respan,halt}$ can be reduced to degree 2 using the extra column, but this will not affect the degree of the $f_{ctrl}$ constraint. diff --git a/docs/src/design/stack/stack_ops.md b/docs/src/design/stack/stack_ops.md index 94719ce29e..68e8f77ff8 100644 --- a/docs/src/design/stack/stack_ops.md +++ b/docs/src/design/stack/stack_ops.md @@ -1,5 +1,5 @@ # Stack Manipulation -In this section we describe the AIR constraints for Miden VM stack manipulation operations. +In this section we describe the AIR constraints for Miden VM stack manipulation operations. ## PAD The `PAD` operation pushes a $0$ onto the stack. The diagram below illustrates this graphically. @@ -226,4 +226,4 @@ s_0^2 - s_0 = 0 \text{ | degree} = 2 $$ The effect of this operation on the rest of the stack is: -* **Left shift** starting from position $9$. \ No newline at end of file +* **Left shift** starting from position $9$. diff --git a/docs/src/design/stack/system_ops.md b/docs/src/design/stack/system_ops.md index 46372f11f0..7f2c36b3c5 100644 --- a/docs/src/design/stack/system_ops.md +++ b/docs/src/design/stack/system_ops.md @@ -1,8 +1,8 @@ # System Operations -In this section we describe the AIR constraints for Miden VM system operations. +In this section we describe the AIR constraints for Miden VM system operations. ## NOOP -The `NOOP` operation advances the cycle counter but does not change the state of the operand stack (i.e., the depth of the stack and the values on the stack remain the same). +The `NOOP` operation advances the cycle counter but does not change the state of the operand stack (i.e., the depth of the stack and the values on the stack remain the same). The `NOOP` operation does not impose any constraints besides the ones needed to ensure that the entire state of the stack is copied over. This constraint looks like so: @@ -64,4 +64,4 @@ s_0' - clk = 0 \text{ | degree} = 1 $$ The effect on the rest of the stack is: -* **Right shift** starting from position $0$. \ No newline at end of file +* **Right shift** starting from position $0$. diff --git a/docs/src/design/stack/u32_ops.md b/docs/src/design/stack/u32_ops.md index 775859f8f0..c9404d1e50 100644 --- a/docs/src/design/stack/u32_ops.md +++ b/docs/src/design/stack/u32_ops.md @@ -13,7 +13,7 @@ $$ The above is just a partial constraint as it does not show the range checker's part of the constraint, which multiplies the required values into the bus column. It also omits the [selector flag](./op_constraints.md#operation-flags) which is used to turn this constraint on only when executing relevant operations. ### Checking element validity -Another primitive which is required by most of the operations described below is checking whether four 16-bit values form a valid field element. Assume $t_0$, $t_1$, $t_2$, and $t_3$ are known to be 16-bit values, and we want to verify that $2^{48} \cdot t_3 + 2^{32} \cdot t_2 + 2^{16} \cdot t_1 + t_0$ is a valid field element. +Another primitive which is required by most of the operations described below is checking whether four 16-bit values form a valid field element. Assume $t_0$, $t_1$, $t_2$, and $t_3$ are known to be 16-bit values, and we want to verify that $2^{48} \cdot t_3 + 2^{32} \cdot t_2 + 2^{16} \cdot t_1 + t_0$ is a valid field element. For simplicity, let's denote: diff --git a/docs/src/intro/overview.md b/docs/src/intro/overview.md index 5c73a67cef..00069fb165 100644 --- a/docs/src/intro/overview.md +++ b/docs/src/intro/overview.md @@ -34,7 +34,7 @@ In the future, other ways of providing public inputs and reading public outputs ### Stack depth restrictions For reasons explained [here](../design/stack/main.md), the VM imposes the restriction that the stack depth cannot be smaller than $16$. This has the following effects: -- When initializing a program with fewer than $16$ inputs, the VM will pad the stack with zeros to ensure the depth is $16$ at the beginning of execution. +- When initializing a program with fewer than $16$ inputs, the VM will pad the stack with zeros to ensure the depth is $16$ at the beginning of execution. - If an operation would result in the stack depth dropping below $16$, the VM will insert a zero at the deep end of the stack to make sure the depth stays at $16$. ### Nondeterministic inputs diff --git a/docs/src/intro/usage.md b/docs/src/intro/usage.md index 9e139ebd5c..88d34dbbd1 100644 --- a/docs/src/intro/usage.md +++ b/docs/src/intro/usage.md @@ -119,7 +119,7 @@ repeat.20 pow2 end #### !help -The `!help` command prints out all the available commands in the REPL tool. +The `!help` command prints out all the available commands in the REPL tool. #### !program @@ -215,4 +215,4 @@ The `!undo` command reverts to the previous state of the stack and memory by dro >> !undo 3 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 -``` \ No newline at end of file +``` diff --git a/docs/src/user_docs/assembly/code_organization.md b/docs/src/user_docs/assembly/code_organization.md index 9accd1451e..2a12575698 100644 --- a/docs/src/user_docs/assembly/code_organization.md +++ b/docs/src/user_docs/assembly/code_organization.md @@ -119,4 +119,4 @@ export.foo push.1 end ``` -Documentation comments must precede a procedure declaration. Using them inside a procedure body is an error. +Documentation comments must precede a procedure declaration. Using them inside a procedure body is an error. diff --git a/docs/src/user_docs/assembly/cryptographic_operations.md b/docs/src/user_docs/assembly/cryptographic_operations.md index 85ecf92e59..70afcb9b68 100644 --- a/docs/src/user_docs/assembly/cryptographic_operations.md +++ b/docs/src/user_docs/assembly/cryptographic_operations.md @@ -2,7 +2,7 @@ Miden assembly provides a set of instructions for performing common cryptographic operations. These instructions are listed in the table below. ### Hashing and Merkle trees -[Rescue Prime Optimized](https://eprint.iacr.org/2022/1577) is the native hash function of Miden VM. The parameters of the hash function were chosen to provide 128-bit security level against preimage and collision attacks. The function operates over a state of 12 field elements, and requires 7 rounds for a single permutation. However, due to its special status within the VM, computing Rescue Prime Optimized hashes can be done very efficiently. For example, applying a permutation of the hash function can be done in a single VM cycle. +[Rescue Prime Optimized](https://eprint.iacr.org/2022/1577) is the native hash function of Miden VM. The parameters of the hash function were chosen to provide 128-bit security level against preimage and collision attacks. The function operates over a state of 12 field elements, and requires 7 rounds for a single permutation. However, due to its special status within the VM, computing Rescue Prime Optimized hashes can be done very efficiently. For example, applying a permutation of the hash function can be done in a single VM cycle. | Instruction | Stack_input | Stack_output | Notes | | -------------- | --------------- | -------------- | ------------------------------------------ | diff --git a/docs/src/user_docs/assembly/field_operations.md b/docs/src/user_docs/assembly/field_operations.md index 3686af4b6e..569a5e8cb8 100644 --- a/docs/src/user_docs/assembly/field_operations.md +++ b/docs/src/user_docs/assembly/field_operations.md @@ -54,4 +54,4 @@ For instructions where one or more operands can be provided as immediate paramet | ext2neg
- *(4 cycles)*
| [a, ...] | [b, ...] | $b \leftarrow -a \mod q$ | | ext2inv
- *(8 cycles)*
| [a, ...] | [b, ...] | $b \leftarrow a^{-1} \mod q$
Fails if $a = 0$ | -where $q$ is an irreducible polynomial $x^2 - x + 2$ over $F_p$ for $p = 2^{64} - 2^{32} + 1$ \ No newline at end of file +where $q$ is an irreducible polynomial $x^2 - x + 2$ over $F_p$ for $p = 2^{64} - 2^{32} + 1$ diff --git a/docs/src/user_docs/assembly/io_operations.md b/docs/src/user_docs/assembly/io_operations.md index 9cd40cb2b1..ef00f85021 100644 --- a/docs/src/user_docs/assembly/io_operations.md +++ b/docs/src/user_docs/assembly/io_operations.md @@ -44,7 +44,7 @@ In both case the values must still encode valid field elements. ### Random access memory As mentioned above, there are two ways to access memory in Miden VM. The first way is via memory addresses using the instructions listed below. The addresses are absolute - i.e., they don't depend on the procedure context. Memory addresses can be in the range $[0, 2^{32})$. - + Memory is guaranteed to be initialized to zeros. Thus, when reading from memory address which hasn't been written to previously, zero elements will be returned. | Instruction | Stack_input | Stack_output | Notes | @@ -67,4 +67,3 @@ The second way to access memory is via procedure locals using the instructions l Unlike regular memory, procedure locals are not guaranteed to be initialized to zeros. Thus, when working with locals, one must assume that before a local memory address has been written to, it contains "garbage". Internally in the VM, procedure locals are stored at memory offset stating at $2^{30}$. Thus, every procedure local has an absolute address in regular memory. The `locaddr.i` instruction is provided specifically to map an index of a procedure's local to an absolute address so that it can be passed to downstream procedures, when needed. - diff --git a/docs/src/user_docs/assembly/stack_manipulation.md b/docs/src/user_docs/assembly/stack_manipulation.md index 3e6eda79b6..50f968dcdd 100644 --- a/docs/src/user_docs/assembly/stack_manipulation.md +++ b/docs/src/user_docs/assembly/stack_manipulation.md @@ -25,4 +25,3 @@ In addition to the typical stack manipulation instructions such as `drop`, `dup` | cswapw
- *(1 cycle)* | [c, B, A, ... ] | [E, D, ... ] | $D = \begin{cases} A, & \text{if}\ c = 0 \\ B, & \text{if}\ c = 1\ \end{cases}$
$E = \begin{cases} B, & \text{if}\ c = 0 \\ A, & \text{if}\ c = 1\ \end{cases}$
Fails if $c > 1$ | | cdrop
- *(2 cycles)* | [c, b, a, ... ] | [d, ... ] | $d = \begin{cases} a, & \text{if}\ c = 0 \\ b, & \text{if}\ c = 1\ \end{cases}$
Fails if $c > 1$ | | cdropw
- *(5 cycles)* | [c, B, A, ... ] | [D, ... ] | $D = \begin{cases} A, & \text{if}\ c = 0 \\ B, & \text{if}\ c = 1\ \end{cases}$
Fails if $c > 1$ | - diff --git a/docs/src/user_docs/stdlib/main.md b/docs/src/user_docs/stdlib/main.md index add11bb562..95993eda39 100644 --- a/docs/src/user_docs/stdlib/main.md +++ b/docs/src/user_docs/stdlib/main.md @@ -3,7 +3,7 @@ Miden standard library provides a set of procedures which can be used by any Mid The goals of Miden standard library are: * Provide highly-optimized and battle-tested implementations of commonly-used primitives. -* Reduce the amount of code that needs to be shared between parties for proving and verifying program execution. +* Reduce the amount of code that needs to be shared between parties for proving and verifying program execution. The second goal can be achieved because calls to procedures in the standard library can always be serialized as 32 bytes, regardless of how large the procedure is. diff --git a/docs/src/user_docs/stdlib/sys.md b/docs/src/user_docs/stdlib/sys.md index 28c62cf792..1b3dcc27d9 100644 --- a/docs/src/user_docs/stdlib/sys.md +++ b/docs/src/user_docs/stdlib/sys.md @@ -3,4 +3,4 @@ Module `std::sys` contains a set of system-level utility procedures. | Procedure | Description | | -------------- | ------------- | -| truncate_stack | Removes elements deep in the stack until the depth of the stack is exactly 16. The elements are removed in such a way that the top 16 elements of the stack remain unchanged. If the stack would otherwise contain more than 16 elements at the end of execution, then adding a call to this function at the end will reduce the size of the public inputs that are shared with the verifier.
Input: Stack with 16 or more elements.
Output: Stack with only the original top 16 elements. | \ No newline at end of file +| truncate_stack | Removes elements deep in the stack until the depth of the stack is exactly 16. The elements are removed in such a way that the top 16 elements of the stack remain unchanged. If the stack would otherwise contain more than 16 elements at the end of execution, then adding a call to this function at the end will reduce the size of the public inputs that are shared with the verifier.
Input: Stack with 16 or more elements.
Output: Stack with only the original top 16 elements. | diff --git a/miden/README.md b/miden/README.md index fecdf69ca9..63244ab4a2 100644 --- a/miden/README.md +++ b/miden/README.md @@ -163,7 +163,7 @@ let n = 50; // instantiate the default assembler and compile the program let source = format!( " - begin + begin repeat.{} swap dup.1 add end diff --git a/miden/examples/fib/fib.inputs b/miden/examples/fib/fib.inputs index 0a39de4a53..ac0da580a2 100644 --- a/miden/examples/fib/fib.inputs +++ b/miden/examples/fib/fib.inputs @@ -1,3 +1,3 @@ { "stack_init": ["1", "1"] -} \ No newline at end of file +} diff --git a/miden/examples/fib/fib.masm b/miden/examples/fib/fib.masm index 4755ea23fc..0eafdcc9a3 100644 --- a/miden/examples/fib/fib.masm +++ b/miden/examples/fib/fib.masm @@ -2,4 +2,4 @@ begin repeat.1000 swap dup.1 add end -end \ No newline at end of file +end diff --git a/miden/src/examples/fibonacci.rs b/miden/src/examples/fibonacci.rs index 64aa0bbca0..bf113204ee 100644 --- a/miden/src/examples/fibonacci.rs +++ b/miden/src/examples/fibonacci.rs @@ -34,7 +34,7 @@ fn generate_fibonacci_program(n: usize) -> Program { // the last operation pops top 2 stack items, adds them, and pushes // the result back onto the stack let program = format!( - "begin + "begin repeat.{} swap dup.1 add end diff --git a/miden/src/repl/mod.rs b/miden/src/repl/mod.rs index 741704ccb3..0b89d12556 100644 --- a/miden/src/repl/mod.rs +++ b/miden/src/repl/mod.rs @@ -27,7 +27,7 @@ use rustyline::{error::ReadlineError, Editor}; /// >> push.1 /// >> push.2 /// >> push.3 -/// +/// /// >> push.1 push.2 push.3 /// /// In order to execute a control flow operation, one needs to write the entire flow operation in diff --git a/miden/tests/integration/flow_control/mod.rs b/miden/tests/integration/flow_control/mod.rs index 4a1a55e585..d822e1970d 100644 --- a/miden/tests/integration/flow_control/mod.rs +++ b/miden/tests/integration/flow_control/mod.rs @@ -79,7 +79,7 @@ fn if_in_loop() { dup push.0 movdn.2 neq.0 while.true dup movup.2 dup.1 eq.5 - if.true + if.true mul else add @@ -102,7 +102,7 @@ fn if_in_loop_in_if() { dup push.0 movdn.2 neq.0 while.true dup movup.2 dup.1 eq.5 - if.true + if.true mul else add diff --git a/miden/tests/integration/operations/decorators/advice.rs b/miden/tests/integration/operations/decorators/advice.rs index 01ed8f1afb..cdfcfeadda 100644 --- a/miden/tests/integration/operations/decorators/advice.rs +++ b/miden/tests/integration/operations/decorators/advice.rs @@ -41,9 +41,9 @@ fn advice_inject_u64div_repeat() { // - reads quotient from advice tape to the stack // - push 2_u64 to the stack divided into 2 32 bit limbs // Finally the first 2 elements of the stack are removed - let source = "begin - repeat.7 - adv.u64div + let source = "begin + repeat.7 + adv.u64div drop drop adv_push.2 push.2 @@ -122,7 +122,7 @@ fn advice_inject_u64div_conditional_execution() { fn advice_inject_mem() { let source = "begin # stack: [1, 2, 3, 4, 5, 6, 7, 8] - + # write to memory and drop first word from stack to use second word as the key for advice map. # mem_storew reverses the order of field elements in the word when it's stored in memory. mem_storew.2 dropw mem_storew.3 @@ -130,7 +130,7 @@ fn advice_inject_mem() { # stack: [5, 6, 7, 8] # mem[2]: [4, 3, 2, 1] # mem[3]: [8, 7, 6, 5] - + # copy from memory to advice map # the key used is in the reverse order of the field elements in the word at the top of the # stack. diff --git a/miden/tests/integration/operations/decorators/asmop.rs b/miden/tests/integration/operations/decorators/asmop.rs index f626228b14..93e22fc37b 100644 --- a/miden/tests/integration/operations/decorators/asmop.rs +++ b/miden/tests/integration/operations/decorators/asmop.rs @@ -207,7 +207,7 @@ fn asmop_conditional_execution_test() { push.1 push.2 add else push.3 push.4 add - end + end end"; //if branch diff --git a/miden/tests/integration/operations/io_ops/local_ops.rs b/miden/tests/integration/operations/io_ops/local_ops.rs index 7378a2a512..e995cdf0de 100644 --- a/miden/tests/integration/operations/io_ops/local_ops.rs +++ b/miden/tests/integration/operations/io_ops/local_ops.rs @@ -6,9 +6,9 @@ use super::build_test; #[test] fn push_local() { let source = " - proc.foo.1 + proc.foo.1 loc_load.0 - end + end begin exec.foo end"; @@ -64,9 +64,9 @@ fn pop_local() { #[test] fn loadw_local() { let source = " - proc.foo.1 + proc.foo.1 loc_loadw.0 - end + end begin exec.foo end"; @@ -87,7 +87,7 @@ fn loadw_local() { fn storew_local() { // --- test write to local memory ------------------------------------------------------------- let source = " - proc.foo.2 + proc.foo.2 loc_storew.0 swapw loc_storew.1 @@ -96,7 +96,7 @@ fn storew_local() { loc_loadw.0 push.0.0.0.0 loc_loadw.1 - end + end begin exec.foo end"; @@ -106,9 +106,9 @@ fn storew_local() { // --- test existing memory is not affected --------------------------------------------------- let source = " - proc.foo.1 + proc.foo.1 loc_storew.0 - end + end begin mem_storew.0 dropw @@ -182,10 +182,10 @@ fn inverse_operations() { fn read_after_write() { // --- write to memory first, then test read with push -------------------------------------- let source = " - proc.foo.1 + proc.foo.1 loc_storew.0 loc_load.0 - end + end begin exec.foo end"; @@ -195,11 +195,11 @@ fn read_after_write() { // --- write to memory first, then test read with pushw -------------------------------------- let source = " - proc.foo.1 + proc.foo.1 loc_storew.0 push.0.0.0.0 loc_loadw.0 - end + end begin exec.foo end"; @@ -209,11 +209,11 @@ fn read_after_write() { // --- write to memory first, then test read with loadw -------------------------------------- let source = " - proc.foo.1 + proc.foo.1 loc_storew.0 dropw loc_loadw.0 - end + end begin exec.foo end"; diff --git a/miden/tests/integration/stdlib/crypto/falcon.rs b/miden/tests/integration/stdlib/crypto/falcon.rs index 97b30e3547..a55477daa3 100644 --- a/miden/tests/integration/stdlib/crypto/falcon.rs +++ b/miden/tests/integration/stdlib/crypto/falcon.rs @@ -152,9 +152,9 @@ fn test_falcon512_normalize_poly() { sub.1 end - + drop - + # prepare argument ( absolute memory address ) for normalizing given polynomial locaddr.128 # output @@ -179,7 +179,7 @@ fn test_falcon512_normalize_poly() { push.123 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -193,7 +193,7 @@ fn test_falcon512_normalize_poly() { push.18 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -207,7 +207,7 @@ fn test_falcon512_normalize_poly() { push.187 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -221,7 +221,7 @@ fn test_falcon512_normalize_poly() { push.105 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -235,7 +235,7 @@ fn test_falcon512_normalize_poly() { push.122 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -249,7 +249,7 @@ fn test_falcon512_normalize_poly() { push.241 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -263,7 +263,7 @@ fn test_falcon512_normalize_poly() { push.12 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -277,7 +277,7 @@ fn test_falcon512_normalize_poly() { push.142 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -291,7 +291,7 @@ fn test_falcon512_normalize_poly() { push.70 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -305,7 +305,7 @@ fn test_falcon512_normalize_poly() { push.174 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -319,7 +319,7 @@ fn test_falcon512_normalize_poly() { push.439 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -333,7 +333,7 @@ fn test_falcon512_normalize_poly() { push.562 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -347,7 +347,7 @@ fn test_falcon512_normalize_poly() { push.97 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -361,7 +361,7 @@ fn test_falcon512_normalize_poly() { push.17 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -375,7 +375,7 @@ fn test_falcon512_normalize_poly() { push.243 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -389,7 +389,7 @@ fn test_falcon512_normalize_poly() { push.138 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -403,7 +403,7 @@ fn test_falcon512_normalize_poly() { push.145 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -417,7 +417,7 @@ fn test_falcon512_normalize_poly() { push.186 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -431,7 +431,7 @@ fn test_falcon512_normalize_poly() { push.166 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -445,7 +445,7 @@ fn test_falcon512_normalize_poly() { push.89 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -459,7 +459,7 @@ fn test_falcon512_normalize_poly() { push.218 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -473,7 +473,7 @@ fn test_falcon512_normalize_poly() { push.62 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -487,7 +487,7 @@ fn test_falcon512_normalize_poly() { push.107 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -501,7 +501,7 @@ fn test_falcon512_normalize_poly() { push.147 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -515,7 +515,7 @@ fn test_falcon512_normalize_poly() { push.282 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -529,7 +529,7 @@ fn test_falcon512_normalize_poly() { push.76 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -543,7 +543,7 @@ fn test_falcon512_normalize_poly() { push.42 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -557,7 +557,7 @@ fn test_falcon512_normalize_poly() { push.138 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -571,7 +571,7 @@ fn test_falcon512_normalize_poly() { push.40 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -585,7 +585,7 @@ fn test_falcon512_normalize_poly() { push.75 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -599,7 +599,7 @@ fn test_falcon512_normalize_poly() { push.10 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -613,7 +613,7 @@ fn test_falcon512_normalize_poly() { push.250 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -627,7 +627,7 @@ fn test_falcon512_normalize_poly() { push.216 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -641,7 +641,7 @@ fn test_falcon512_normalize_poly() { push.49 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -655,7 +655,7 @@ fn test_falcon512_normalize_poly() { push.13 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -669,7 +669,7 @@ fn test_falcon512_normalize_poly() { push.72 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -683,7 +683,7 @@ fn test_falcon512_normalize_poly() { push.61 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -697,7 +697,7 @@ fn test_falcon512_normalize_poly() { push.184 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -711,7 +711,7 @@ fn test_falcon512_normalize_poly() { push.1 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -725,7 +725,7 @@ fn test_falcon512_normalize_poly() { push.241 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -739,7 +739,7 @@ fn test_falcon512_normalize_poly() { push.169 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -753,7 +753,7 @@ fn test_falcon512_normalize_poly() { push.223 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -767,7 +767,7 @@ fn test_falcon512_normalize_poly() { push.77 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -781,7 +781,7 @@ fn test_falcon512_normalize_poly() { push.61 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -795,7 +795,7 @@ fn test_falcon512_normalize_poly() { push.315 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -809,7 +809,7 @@ fn test_falcon512_normalize_poly() { push.238 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -823,7 +823,7 @@ fn test_falcon512_normalize_poly() { push.52 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -837,7 +837,7 @@ fn test_falcon512_normalize_poly() { push.146 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -851,7 +851,7 @@ fn test_falcon512_normalize_poly() { push.114 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -865,7 +865,7 @@ fn test_falcon512_normalize_poly() { push.244 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -879,7 +879,7 @@ fn test_falcon512_normalize_poly() { push.21 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -893,7 +893,7 @@ fn test_falcon512_normalize_poly() { push.97 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -907,7 +907,7 @@ fn test_falcon512_normalize_poly() { push.203 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -921,7 +921,7 @@ fn test_falcon512_normalize_poly() { push.192 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -935,7 +935,7 @@ fn test_falcon512_normalize_poly() { push.117 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -949,7 +949,7 @@ fn test_falcon512_normalize_poly() { push.224 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -963,7 +963,7 @@ fn test_falcon512_normalize_poly() { push.9 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -977,7 +977,7 @@ fn test_falcon512_normalize_poly() { push.260 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -991,7 +991,7 @@ fn test_falcon512_normalize_poly() { push.236 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1005,7 +1005,7 @@ fn test_falcon512_normalize_poly() { push.120 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1019,7 +1019,7 @@ fn test_falcon512_normalize_poly() { push.207 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1033,7 +1033,7 @@ fn test_falcon512_normalize_poly() { push.20 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1047,7 +1047,7 @@ fn test_falcon512_normalize_poly() { push.270 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1061,7 +1061,7 @@ fn test_falcon512_normalize_poly() { push.10 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1075,7 +1075,7 @@ fn test_falcon512_normalize_poly() { push.35 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1089,7 +1089,7 @@ fn test_falcon512_normalize_poly() { push.192 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1103,7 +1103,7 @@ fn test_falcon512_normalize_poly() { push.147 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1117,7 +1117,7 @@ fn test_falcon512_normalize_poly() { push.45 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1131,7 +1131,7 @@ fn test_falcon512_normalize_poly() { push.224 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1145,7 +1145,7 @@ fn test_falcon512_normalize_poly() { push.14 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1159,7 +1159,7 @@ fn test_falcon512_normalize_poly() { push.135 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1173,7 +1173,7 @@ fn test_falcon512_normalize_poly() { push.91 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1187,7 +1187,7 @@ fn test_falcon512_normalize_poly() { push.106 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1201,7 +1201,7 @@ fn test_falcon512_normalize_poly() { push.128 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1215,7 +1215,7 @@ fn test_falcon512_normalize_poly() { push.33 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1229,7 +1229,7 @@ fn test_falcon512_normalize_poly() { push.213 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1243,7 +1243,7 @@ fn test_falcon512_normalize_poly() { push.82 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1257,7 +1257,7 @@ fn test_falcon512_normalize_poly() { push.24 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1271,7 +1271,7 @@ fn test_falcon512_normalize_poly() { push.147 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1285,7 +1285,7 @@ fn test_falcon512_normalize_poly() { push.14 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1299,7 +1299,7 @@ fn test_falcon512_normalize_poly() { push.155 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1313,7 +1313,7 @@ fn test_falcon512_normalize_poly() { push.336 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1327,7 +1327,7 @@ fn test_falcon512_normalize_poly() { push.129 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1341,7 +1341,7 @@ fn test_falcon512_normalize_poly() { push.78 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1355,7 +1355,7 @@ fn test_falcon512_normalize_poly() { push.66 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1369,7 +1369,7 @@ fn test_falcon512_normalize_poly() { push.57 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1383,7 +1383,7 @@ fn test_falcon512_normalize_poly() { push.156 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1397,7 +1397,7 @@ fn test_falcon512_normalize_poly() { push.1 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1411,7 +1411,7 @@ fn test_falcon512_normalize_poly() { push.330 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1425,7 +1425,7 @@ fn test_falcon512_normalize_poly() { push.88 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1439,7 +1439,7 @@ fn test_falcon512_normalize_poly() { push.144 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1453,7 +1453,7 @@ fn test_falcon512_normalize_poly() { push.254 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1467,7 +1467,7 @@ fn test_falcon512_normalize_poly() { push.313 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1481,7 +1481,7 @@ fn test_falcon512_normalize_poly() { push.263 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1495,7 +1495,7 @@ fn test_falcon512_normalize_poly() { push.171 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1509,7 +1509,7 @@ fn test_falcon512_normalize_poly() { push.136 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1523,7 +1523,7 @@ fn test_falcon512_normalize_poly() { push.201 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1537,7 +1537,7 @@ fn test_falcon512_normalize_poly() { push.56 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1551,7 +1551,7 @@ fn test_falcon512_normalize_poly() { push.48 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1565,7 +1565,7 @@ fn test_falcon512_normalize_poly() { push.44 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1579,7 +1579,7 @@ fn test_falcon512_normalize_poly() { push.133 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1593,7 +1593,7 @@ fn test_falcon512_normalize_poly() { push.347 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1607,7 +1607,7 @@ fn test_falcon512_normalize_poly() { push.161 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1621,7 +1621,7 @@ fn test_falcon512_normalize_poly() { push.279 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1635,7 +1635,7 @@ fn test_falcon512_normalize_poly() { push.345 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1649,7 +1649,7 @@ fn test_falcon512_normalize_poly() { push.270 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1663,7 +1663,7 @@ fn test_falcon512_normalize_poly() { push.171 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1677,7 +1677,7 @@ fn test_falcon512_normalize_poly() { push.31 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1691,7 +1691,7 @@ fn test_falcon512_normalize_poly() { push.235 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1705,7 +1705,7 @@ fn test_falcon512_normalize_poly() { push.138 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1719,7 +1719,7 @@ fn test_falcon512_normalize_poly() { push.129 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1733,7 +1733,7 @@ fn test_falcon512_normalize_poly() { push.22 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1747,7 +1747,7 @@ fn test_falcon512_normalize_poly() { push.94 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1761,7 +1761,7 @@ fn test_falcon512_normalize_poly() { push.210 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1775,7 +1775,7 @@ fn test_falcon512_normalize_poly() { push.57 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1789,7 +1789,7 @@ fn test_falcon512_normalize_poly() { push.165 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1803,7 +1803,7 @@ fn test_falcon512_normalize_poly() { push.105 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1817,7 +1817,7 @@ fn test_falcon512_normalize_poly() { push.98 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1831,7 +1831,7 @@ fn test_falcon512_normalize_poly() { push.154 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1845,7 +1845,7 @@ fn test_falcon512_normalize_poly() { push.85 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1859,7 +1859,7 @@ fn test_falcon512_normalize_poly() { push.156 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1873,7 +1873,7 @@ fn test_falcon512_normalize_poly() { push.193 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1887,7 +1887,7 @@ fn test_falcon512_normalize_poly() { push.108 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1901,7 +1901,7 @@ fn test_falcon512_normalize_poly() { push.382 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1915,7 +1915,7 @@ fn test_falcon512_normalize_poly() { push.82 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1929,7 +1929,7 @@ fn test_falcon512_normalize_poly() { push.150 assert_eq add.1 - + dup push.0.0.0.0 movup.4 @@ -1943,7 +1943,7 @@ fn test_falcon512_normalize_poly() { push.296 assert_eq add.1 - + push.0.0.0.0 movup.4 mem_loadw @@ -2002,7 +2002,7 @@ fn test_falcon512_vector_squared_norm() { # prepare input polynomial `f` {in_str} - # prepare argument ( absolute memory address ) for computing squared norm + # prepare argument ( absolute memory address ) for computing squared norm # of a vector ( read polynomial ) locaddr.0 @@ -2046,7 +2046,7 @@ fn test_falcon512_verify() { add.1 end - + drop push.18446744069414584303.128.23.18446744069414584303 diff --git a/miden/tests/integration/stdlib/crypto/fri/remainder.rs b/miden/tests/integration/stdlib/crypto/fri/remainder.rs index d188e6d74c..8fd7355a3c 100644 --- a/miden/tests/integration/stdlib/crypto/fri/remainder.rs +++ b/miden/tests/integration/stdlib/crypto/fri/remainder.rs @@ -34,7 +34,7 @@ fn test_decorator_ext2intt(in_poly_len: usize, blowup: usize) { sub.1 end drop - + locaddr.0 push.{} push.{} diff --git a/miden/tests/integration/stdlib/math/ntt512.rs b/miden/tests/integration/stdlib/math/ntt512.rs index 8761eb2983..1f4c7cd12c 100644 --- a/miden/tests/integration/stdlib/math/ntt512.rs +++ b/miden/tests/integration/stdlib/math/ntt512.rs @@ -52,7 +52,7 @@ fn generate_test_script_ntt512() -> String { proc.wrapper.128 # prepare input vector - + {polynomial_script} # place starting absolute memory addresses on stack, where input vector is kept, diff --git a/miden/tests/integration/stdlib/math/secp256k1/group.rs b/miden/tests/integration/stdlib/math/secp256k1/group.rs index 246223c464..acab79dcbe 100644 --- a/miden/tests/integration/stdlib/math/secp256k1/group.rs +++ b/miden/tests/integration/stdlib/math/secp256k1/group.rs @@ -210,7 +210,7 @@ fn test_secp256k1_point_addition(src0: Point, src1: Point, dst: Point) { use.std::math::secp256k1::group # Given two points of secp256k1 elliptic curve ( twice ), this routine first computes - # point addition of them in projective coordinate & then asserts each coordinate + # point addition of them in projective coordinate & then asserts each coordinate # limb-by-limb for ensuring correctness. proc.point_addition_test_wrapper.18 # push X1 -coordinate to memory @@ -461,7 +461,7 @@ fn test_secp256k1_point_multiplication(src_point: Point, scalar: FieldElement, d " use.std::math::secp256k1::group - # Given an elliptic curve point ( in projective coordinate system ) and a 256 -bit scalar + # Given an elliptic curve point ( in projective coordinate system ) and a 256 -bit scalar # in radix-2^32 form ( i.e. 8 limbs, each of 32 -bit width ), this routine first multiplies # the EC point with provided scalar and then asserts for correctness with known answer. proc.point_multiplication_test_wrapper.12 @@ -493,7 +493,7 @@ fn test_secp256k1_point_multiplication(src_point: Point, scalar: FieldElement, d push.{}.{}.{}.{} loc_storew.3 dropw - + push.{}.{}.{}.{} loc_storew.4 dropw @@ -669,8 +669,8 @@ fn test_secp256k1_generator_multiplication(scalar: FieldElement, point: Point) { " use.std::math::secp256k1::group - # Given a 256 -bit scalar in radix-2^32 form ( i.e. 8 limbs, each of 32 -bit width ), - # this routine first multiplies the secp256k1 generator point with provided scalar and + # Given a 256 -bit scalar in radix-2^32 form ( i.e. 8 limbs, each of 32 -bit width ), + # this routine first multiplies the secp256k1 generator point with provided scalar and # then asserts for correctness with known answer. proc.generator_multiplication_test_wrapper.12 # resulting point diff --git a/processor/src/chiplets/bitwise/mod.rs b/processor/src/chiplets/bitwise/mod.rs index e6180aa2ca..d276a13a0f 100644 --- a/processor/src/chiplets/bitwise/mod.rs +++ b/processor/src/chiplets/bitwise/mod.rs @@ -30,7 +30,7 @@ const INIT_TRACE_CAPACITY: usize = 128; /// /// The layout of the table is illustrated below. /// -/// s a b a0 a1 a2 a3 b0 b1 b2 b3 zp z +/// s a b a0 a1 a2 a3 b0 b1 b2 b3 zp z /// ├─────┴─────┴─────┴───────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴─────┴─────┤ /// /// In the above, the meaning of the columns is as follows: diff --git a/processor/src/range/mod.rs b/processor/src/range/mod.rs index 4256a81376..9a07b3b2a2 100644 --- a/processor/src/range/mod.rs +++ b/processor/src/range/mod.rs @@ -54,7 +54,7 @@ pub struct RangeCheckTraceTable { /// /// The layout illustrated below. /// -/// t s0 s1 v +/// t s0 s1 v /// ├─────┴──────┴──────┴─────┤ /// /// In the above, the meaning of the columns is as follows: diff --git a/stdlib/README.md b/stdlib/README.md index b89e67a9d4..18ddbaf76e 100644 --- a/stdlib/README.md +++ b/stdlib/README.md @@ -5,7 +5,7 @@ Miden standard library provides a set of procedures which can be used by any Mid The goals of Miden standard library are: * Provide highly-optimized and battle-tested implementations of commonly-used primitives. -* Reduce the amount of code that needs to be shared between parties for proving and verifying program execution. +* Reduce the amount of code that needs to be shared between parties for proving and verifying program execution. The second goal can be achieved because calls to procedures in the standard library can always be serialized as 32 bytes, regardless of how large the procedure is. diff --git a/stdlib/asm/crypto/dsa/ecdsa/secp256k1.masm b/stdlib/asm/crypto/dsa/ecdsa/secp256k1.masm index b771f5b808..20b7837316 100644 --- a/stdlib/asm/crypto/dsa/ecdsa/secp256k1.masm +++ b/stdlib/asm/crypto/dsa/ecdsa/secp256k1.masm @@ -2,8 +2,8 @@ use.std::math::secp256k1::base_field use.std::math::secp256k1::scalar_field use.std::math::secp256k1::group -#! Given an ECDSA public key ( in projective coordinate system i.e. each secp256k1 curve point -#! is represented in terms of X, Y, Z coordinates ), hashed message h ( a 256 -bit element represented +#! Given an ECDSA public key ( in projective coordinate system i.e. each secp256k1 curve point +#! is represented in terms of X, Y, Z coordinates ), hashed message h ( a 256 -bit element represented #! in Montgomery form ) and an ECDSA signature, represented in terms of (r, s) s.t. each of them are #! represented in Montgomery form, this routine attempts to verify the ECDSA signature. #! diff --git a/stdlib/asm/crypto/dsa/falcon.masm b/stdlib/asm/crypto/dsa/falcon.masm index c229282dec..88e6f4bb2d 100644 --- a/stdlib/asm/crypto/dsa/falcon.masm +++ b/stdlib/asm/crypto/dsa/falcon.masm @@ -1,6 +1,6 @@ use.std::math::poly512 -#! Given an element on stack top, this routine normalizes that element in +#! Given an element on stack top, this routine normalizes that element in #! interval (-q/2, q/2] | q = 12289 #! #! Imagine, a is the provided element, which needs to be normalized @@ -14,7 +14,7 @@ use.std::math::poly512 #! anyway `b` will be squared ( for computing norm of a vector i.e. polynomial, where b is a coefficient ). #! That means we can just drop the sign, and that's what is done in this routine. #! -#! To be more concrete, normalization of 12166 ( = a ) should result into -123, but absolute value +#! To be more concrete, normalization of 12166 ( = a ) should result into -123, but absolute value #! 123 will be kept on stack. While normalization of 21, should result into 21, which has absolute #! value 21 --- that's what is kept on stack. #! @@ -51,7 +51,7 @@ proc.normalize end end -#! Given four elements from Falcon prime field, on stack top, this routine +#! Given four elements from Falcon prime field, on stack top, this routine #! normalizes each of them, using above defined `normalize()` routine. #! #! Expected stack state : @@ -79,8 +79,8 @@ proc.normalize_word movdn.3 end -#! Given a degree 512 polynomial on stack, using its starting (absolute) memory address, -#! this routine normalizes each coefficient of the polynomial, using above defined +#! Given a degree 512 polynomial on stack, using its starting (absolute) memory address, +#! this routine normalizes each coefficient of the polynomial, using above defined #! `normalize()` routine #! #! Imagine, f is the given polynomial of degree 512. It can be normalized using @@ -165,7 +165,7 @@ proc.squared_norm_word add end -#! Given a degree 512 polynomial in coefficient form, as starting (absolute) memory address +#! Given a degree 512 polynomial in coefficient form, as starting (absolute) memory address #! on stack, this routine computes squared norm of that vector, using following formula #! #! Say, f = [a0, a1, a2, ..., a510, a511] @@ -175,7 +175,7 @@ end #! #! [f_start_addr, ...] | f_addr`i` holds f[(i << 2) .. ((i+1) << 2)] #! -#! Consecutive 127 addresses on stack can be computed using `INCR` instruction, because memory +#! Consecutive 127 addresses on stack can be computed using `INCR` instruction, because memory #! addresses are consecutive i.e. monotonically increasing by 1. #! #! Final stack state : @@ -205,7 +205,7 @@ end #! Falcon-512 Digital Signature Verification routine #! -#! Given four degree-511 polynomials, using initial absolute memory addresses on stack, +#! Given four degree-511 polynomials, using initial absolute memory addresses on stack, #! this routine checks whether it's a valid Falcon signature or not. #! #! Four degree-511 polynomials, which are provided ( in order ) @@ -215,7 +215,7 @@ end #! h = [h0, h1, ..., h510, h511] -> input message hashed using SHAKE256 XOF and converted to polynomial #! k = [k0, k1, ..., k510, k511] -> [abs(i) for i in f] | abs(a) = a < 0 ? 0 - a : a #! -#! Each of these polynomials are represented using starting absolute memory address. Contiguous 127 +#! Each of these polynomials are represented using starting absolute memory address. Contiguous 127 #! memory addresses can be computed by repeated application of INCR instruction ( read add.1 ) on previous #! absolute memory address. #! diff --git a/stdlib/asm/crypto/fri/ext2fri.masm b/stdlib/asm/crypto/fri/ext2fri.masm index 9bce4b3e63..4e2548a5ba 100644 --- a/stdlib/asm/crypto/fri/ext2fri.masm +++ b/stdlib/asm/crypto/fri/ext2fri.masm @@ -188,7 +188,7 @@ proc.accumulate_for_even_index end #! Given ω, accumulator ν and τ on stack top, this routine first computes ⍳ , and then accumulates -#! ⍳ into ν, while consuming , only when j is odd | j ∈ [0..64), following formula for +#! ⍳ into ν, while consuming , only when j is odd | j ∈ [0..64), following formula for #! computing β. #! #! Expected stack state: @@ -196,7 +196,7 @@ end #! [ω, a3, a2, ν1, ν0, τ1, τ0, q_ptr, ...] #! #! One computes ⍳ = ω / (τ - ω) -#! which was loaded (during the previous iteration i.e. when j was even) from q_ptr +#! which was loaded (during the previous iteration i.e. when j was even) from q_ptr #! is consumed into ν. #! #! Final stack state: @@ -248,7 +248,7 @@ proc.compute_beta_64 # for j = 0 push.1 exec.accumulate_for_even_index - + # for j = 1 push.549755813888 exec.accumulate_for_odd_index @@ -684,7 +684,7 @@ proc.compute_beta_32 ext2mul end -#! Given τ and end memory address of interpolated polynomial p (with degree at max 7, over +#! Given τ and end memory address of interpolated polynomial p (with degree at max 7, over #! quadratic extension field of Fq) on the top of the stack, this routine computes α. #! #! Expected stack state: @@ -769,7 +769,7 @@ proc.compute_alpha_64 ext2add end -#! Given τ and end memory address of interpolated polynomial p (with degree at max 3, over +#! Given τ and end memory address of interpolated polynomial p (with degree at max 3, over #! quadratic extension field of Fq) on the top of the stack, this routine computes α. #! #! Expected stack state: @@ -856,12 +856,12 @@ export.verify_remainder_64.4 # [β1, β0, τ1, τ0, p_ptr, ...] # - # notice, p_ptr was previously set to locaddr.0, which was used (and dropped) during the + # notice, p_ptr was previously set to locaddr.0, which was used (and dropped) during the # computation of τ. Now p_ptr is set to locaddr.3, because it's easier to compute α, by # accessing coefficients of the interpolated polynomial p in reverse (i.e. descending) order # using Horner's evaluation. exec.compute_alpha_64 - + # assert α == β # # [α1, α0, β1, β0, ...] @@ -916,12 +916,12 @@ export.verify_remainder_32.2 # [β1, β0, τ1, τ0, p_ptr, ...] # - # notice, p_ptr was previously set to locaddr.0, which was used (and dropped) during the + # notice, p_ptr was previously set to locaddr.0, which was used (and dropped) during the # computation of τ. Now p_ptr is set to locaddr.1, because it's easier to compute α, by # accessing coefficients of the interpolated polynomial p in reverse (i.e. descending) order # using Horner's evaluation. exec.compute_alpha_32 - + # assert α == β # # [α1, α0, β1, β0, ...] diff --git a/stdlib/asm/crypto/fri/frie2f4.masm b/stdlib/asm/crypto/fri/frie2f4.masm index 92a70fd913..996b4ed930 100644 --- a/stdlib/asm/crypto/fri/frie2f4.masm +++ b/stdlib/asm/crypto/fri/frie2f4.masm @@ -9,7 +9,7 @@ use.std::crypto::fri::ext2fri #! starting from the original domain size. export.preprocess.4 #adv_push.1 - locaddr.3 + locaddr.3 adv_push.1 #[num_layers, layer_ptr, g, ..] dup neq.0 @@ -26,7 +26,7 @@ export.preprocess.4 dup movdn.6 #[ptr+2, d_size, t_depth, a1, a0, num_layers, ptr+2, ..] mem_storew dropw #[num_layers, ptr+2, ..] u32wrapping_sub.1 dup #[num_layers-1, num_layers-1, ptr+2, ..] - neq.0 #[?, num_layers-1, ptr+2, ..] + neq.0 #[?, num_layers-1, ptr+2, ..] end #[ptr2n', rem_len, rem_ptr, g, ..] @@ -34,7 +34,7 @@ export.preprocess.4 adv_push.1 dup.1 add.1 - swap.2 + swap.2 # value of the remainder codeword @@ -56,14 +56,14 @@ export.preprocess.4 # [ptr_(2n+rem_len/2)', rem_ptr, g] dropw - swap + swap drop - + # number of queries adv_push.1 - # [ptr_(2n+rem_len/2)', num_q, query_start_ptr, rem_ptr, g] - dup.1 + # [ptr_(2n+rem_len/2)', num_q, query_start_ptr, rem_ptr, g] + dup.1 add.1 swap.2 @@ -84,16 +84,16 @@ export.preprocess.4 neq.0 end - - # [ptr_queries_end, query_start_ptr, rem_ptr, g] + + # [ptr_queries_end, query_start_ptr, rem_ptr, g] dropw - swap + swap drop # [ptr_queries_end + 1, query_start_ptr, rem_ptr, g] add.1 - # [query_start_ptr, ptr_queries_end + 1, ptr_c_start, rem_ptr, g] + # [query_start_ptr, ptr_queries_end + 1, ptr_c_start, rem_ptr, g] locaddr.3 add.1 swap.2 @@ -104,10 +104,10 @@ end #! #! Input: [layer_ptr, layer_ptr, poe, p, e1, e0, layer_ptr, rem_ptr, x, x, x, x, x, x, x, x, ...] #! Output: [layer_ptr + 2, layer_ptr + 2, poe^4, f_pos, ne1, ne0, layer_ptr + 2, rem_ptr, x, x, x, x, x, x, x, x, ...] -#! +#! #! Cycles: 76 export.verify_query_layer.3 - + # load layer commitment C as well as [a0, a1, t_depth, d_size] (7 cycles) swapdw movup.8 @@ -143,16 +143,16 @@ export.verify_query_layer.3 # => [T2, T1, T0, ptr, V, f_pos, d_seg, poe, e1, e0, a1, a0, layer_ptr, rem_ptr, ..] # assert T1 == V (16 cycles) - swapw.3 - drop + swapw.3 + drop movup.3 - assert_eq + assert_eq movup.2 assert_eq assert_eq movup.9 - assert_eq - + assert_eq + # load (v7, ..v0) from memory (8 cycles) loc_loadw.1 swapw @@ -182,7 +182,7 @@ end #! Output: [x, x, x, x, x, x, x, x, x, x, ...] #! #! - poe is g^p. -#! - p is a query index at the first layer. +#! - p is a query index at the first layer. #! - (e0, e1) is an extension field element corresponding to the value of the first layer at index p. #! - layer_ptr is the memory address of the layer data (Merkle tree root, alpha etc.) for the next #! layer. @@ -222,10 +222,10 @@ export.verify_query dup.1 add # => [rem_ptr + offset, x, x, x, x, ?, ne1, ne0, rem_ptr, rem_ptr, x, x, x, x, x, x, x, x, ..] - + mem_loadw # => [e1', e0', e1, e0, ?, ne1, ne0, rem_ptr, rem_ptr, x, x, x, x, x, x, x, x, ..] - + # compare (ne0, ne1) to the appropriate tuple from the remainder word (14 cycles) movup.2 swap @@ -285,7 +285,7 @@ export.verify.1 sub movdn.5 # => [query_start_ptr, query_end_ptr, layer_ptr, rem_ptr, g, rem_len] - + # compute number of remaining queries to be verified and assert it is not 0 # (4 cycles) swap diff --git a/stdlib/asm/crypto/hashes/blake3.masm b/stdlib/asm/crypto/hashes/blake3.masm index f946a6d219..33316ddc39 100644 --- a/stdlib/asm/crypto/hashes/blake3.masm +++ b/stdlib/asm/crypto/hashes/blake3.masm @@ -71,7 +71,7 @@ end #! Permutes ordered message words, kept on stack top ( = sixteen 32 -bit BLAKE3 words ) #! -#! Expected stack top: +#! Expected stack top: #! #! [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15] #! @@ -158,7 +158,7 @@ proc.finalize movdn.7 end -#! Given blake3 state matrix ( total 16 elements, each of 32 -bit ) and +#! Given blake3 state matrix ( total 16 elements, each of 32 -bit ) and #! 8 message words ( each of 32 -bit ), this routine performs column-wise mixing #! of message words into blake3 hash state. #! @@ -229,7 +229,7 @@ proc.columnar_mixing.1 dup.4 u32checked_xor u32unchecked_rotr.16 - + swap dup.5 u32checked_xor @@ -276,7 +276,7 @@ proc.columnar_mixing.1 dup.4 u32checked_xor u32unchecked_rotr.12 - + swap dup.5 u32checked_xor @@ -331,7 +331,7 @@ proc.columnar_mixing.1 dup.4 u32checked_xor u32unchecked_rotr.8 - + swap dup.5 u32checked_xor @@ -397,7 +397,7 @@ proc.columnar_mixing.1 movupw.3 end -#! Given blake3 state matrix ( total 16 elements, each of 32 -bit ) and +#! Given blake3 state matrix ( total 16 elements, each of 32 -bit ) and #! 8 message words ( each of 32 -bit ), this routine performs diagonal-wise mixing #! of message words into blake3 hash state. #! @@ -636,7 +636,7 @@ proc.diagonal_mixing.1 movupw.3 end -#! Given blake3 state matrix ( total 16 elements, each of 32 -bit ) and +#! Given blake3 state matrix ( total 16 elements, each of 32 -bit ) and #! 16 message words ( each of 32 -bit ), this routine applies single round of mixing #! of message words into hash state i.e. msg_word[0..8] are mixed into hash state using #! columnar mixing while remaining message words ( msg_word[8..16] ) are mixed into hash state @@ -656,7 +656,7 @@ end #! #! [...] #! -#! i.e. mixed state matrix lives in memory addresses {state0_3_addr, state4_7_addr, state8_11_addr, state12_15_addr}, +#! i.e. mixed state matrix lives in memory addresses {state0_3_addr, state4_7_addr, state8_11_addr, state12_15_addr}, #! which were provided, on stack top, while invoking this routine. proc.round.5 loc_storew.0 @@ -718,7 +718,7 @@ end #! #! [...] #! -#! i.e. 7 -round mixed state matrix lives in memory addresses {state0_3_addr, state4_7_addr, state8_11_addr, state12_15_addr}, +#! i.e. 7 -round mixed state matrix lives in memory addresses {state0_3_addr, state4_7_addr, state8_11_addr, state12_15_addr}, #! which were provided, on stack top, while invoking this routine. So updated state matrix can be read by caller routine, by reading #! the content of memory addresses where state was provided as routine input. proc.compress.1 @@ -765,8 +765,8 @@ export.hash_2to1.4 exec.initialize_2to1 - # Note, chunk compression routine needs to compress only one chunk with one message - # block ( = 64 -bytes ) because what we're doing here is 2-to-1 hashing i.e. 64 -bytes + # Note, chunk compression routine needs to compress only one chunk with one message + # block ( = 64 -bytes ) because what we're doing here is 2-to-1 hashing i.e. 64 -bytes # input being converted to 32 -bytes output locaddr.3 diff --git a/stdlib/asm/crypto/hashes/keccak256.masm b/stdlib/asm/crypto/hashes/keccak256.masm index 428e9bb1b0..07ca58be54 100644 --- a/stdlib/asm/crypto/hashes/keccak256.masm +++ b/stdlib/asm/crypto/hashes/keccak256.masm @@ -1,4 +1,4 @@ -#! Keccak-p[1600, 24] permutation's θ step mapping function, which is implemented +#! Keccak-p[1600, 24] permutation's θ step mapping function, which is implemented #! in terms of 32 -bit word size ( bit interleaved representation ) #! #! See https://github.com/itzmeanjan/merklize-sha/blob/1d35aae9da7fed20127489f362b4bc93242a516c/include/sha3.hpp#L55-L98 for original implementation @@ -129,7 +129,7 @@ proc.theta.3 locaddr.0 mem_load - + # bring S[2], S[3] dup push.0.0.0.0 @@ -353,7 +353,7 @@ proc.theta.3 locaddr.0 mem_load add.1 - + # bring S[6], S[7] dup push.0.0.0.0 @@ -1038,7 +1038,7 @@ proc.theta.3 dropw end -#! Keccak-p[1600, 24] permutation's ρ step mapping function, which is implemented +#! Keccak-p[1600, 24] permutation's ρ step mapping function, which is implemented #! in terms of 32 -bit word size ( bit interleaved representation ) #! #! See https://github.com/itzmeanjan/merklize-sha/blob/1d35aae9da7fed20127489f362b4bc93242a516c/include/sha3.hpp#L115-L147 for original implementation @@ -1223,7 +1223,7 @@ proc.rho.1 add.1 movdn.5 mem_storew - + # rotate state[32..36) dup.4 mem_loadw @@ -1269,7 +1269,7 @@ proc.rho.1 # rotate state[40..44) dup.4 mem_loadw - + u32unchecked_rotl.9 swap u32unchecked_rotl.9 @@ -1323,7 +1323,7 @@ proc.rho.1 dropw end -#! Keccak-p[1600, 24] permutation's π step mapping function, which is implemented +#! Keccak-p[1600, 24] permutation's π step mapping function, which is implemented #! in terms of 32 -bit word size ( bit interleaved representation ) #! #! See https://github.com/itzmeanjan/merklize-sha/blob/1d35aae9da7fed20127489f362b4bc93242a516c/include/sha3.hpp#L169-L207 for original implementation @@ -1742,7 +1742,7 @@ proc.pi.14 drop end -#! Keccak-p[1600, 24] permutation's χ step mapping function, which is implemented +#! Keccak-p[1600, 24] permutation's χ step mapping function, which is implemented #! in terms of 32 -bit word size ( bit interleaved representation ) #! #! See https://github.com/itzmeanjan/merklize-sha/blob/1d35aae9da7fed20127489f362b4bc93242a516c/include/sha3.hpp#L233-L271 for original implementation @@ -2149,7 +2149,7 @@ proc.chi.4 push.0.0.0.0 loc_loadw.2 - + movup.4 u32checked_xor @@ -2180,7 +2180,7 @@ proc.chi.4 push.0.0.0.0 loc_loadw.3 - + movup.4 u32checked_xor @@ -2892,7 +2892,7 @@ proc.chi.4 end #! Keccak-p[1600, 24] permutation's ι ( iota ) function, which is -#! implemented in terms of 32 -bit word size ( bit interleaved form ); +#! implemented in terms of 32 -bit word size ( bit interleaved form ); #! imagine https://github.com/itzmeanjan/merklize-sha/blob/1d35aae9da7fed20127489f362b4bc93242a516c/include/sha3.hpp#L288-L306 #! invoked with (c0, c1) as template arguments #! @@ -2929,10 +2929,10 @@ proc.iota dropw end -#! Keccak-p[1600, 24] permutation round, without `iota` function ( all other +#! Keccak-p[1600, 24] permutation round, without `iota` function ( all other #! functions i.e. `theta`, `rho`, `pi`, `chi` are applied in order ) #! -#! As `iota` function involves xoring constant factors with first lane of state array +#! As `iota` function involves xoring constant factors with first lane of state array #! ( read state[0, 0] ), it's required to invoke them seperately after completion of #! this procedure's execution. #! @@ -2963,7 +2963,7 @@ proc.round exec.chi end -#! Keccak-p[1600, 24] permutation, applying 24 rounds on state array of size 5 x 5 x 64, +#! Keccak-p[1600, 24] permutation, applying 24 rounds on state array of size 5 x 5 x 64, #! where each 64 -bit lane is represented in bit interleaved form ( in terms of two 32 -bit words ). #! #! Expected stack state : @@ -3300,7 +3300,7 @@ export.from_bit_interleaved swap push.1 u32checked_and - + u32unchecked_shl.31 swap u32unchecked_shl.30 @@ -3477,7 +3477,7 @@ end #! #! Expected stack state : #! -#! [iword0, iword1, iword2, iword3, iword4, iword5, iword6, iword7, +#! [iword0, iword1, iword2, iword3, iword4, iword5, iword6, iword7, #! iword8, iword9, iword10, iword11, iword12, iword13, iword14, iword15, ... ] #! #! Final stack state : @@ -3500,4 +3500,4 @@ export.hash.13 push.0.0.0.0 loc_loadw.0 exec.to_digest -end \ No newline at end of file +end diff --git a/stdlib/asm/crypto/hashes/sha256.masm b/stdlib/asm/crypto/hashes/sha256.masm index ace8b77e16..8e33a7a050 100644 --- a/stdlib/asm/crypto/hashes/sha256.masm +++ b/stdlib/asm/crypto/hashes/sha256.masm @@ -121,7 +121,7 @@ proc.maj u32checked_xor end -#! Given [a, b, c, d, ...] on stack top, this routine reverses order of first +#! Given [a, b, c, d, ...] on stack top, this routine reverses order of first #! four elements on stack top such that final stack state looks like [d, c, b, a, ...] proc.rev_element_order swap @@ -136,7 +136,7 @@ end #! t1 = small_sigma_0(c) + d #! return t0 + t1 #! -#! If to be computed message schedule word has index i ∈ [16, 64), then +#! If to be computed message schedule word has index i ∈ [16, 64), then #! a, b, c, d will have following indices in message schedule #! #! a = msg[i - 2] @@ -153,8 +153,8 @@ proc.compute_message_schedule_word u32wrapping_add end -#! Given eight working variables of SHA256 ( i.e. hash state ), a 32 -bit round constant & -#! 32 -bit message word on stack top, this routine consumes constant & message word into +#! Given eight working variables of SHA256 ( i.e. hash state ), a 32 -bit round constant & +#! 32 -bit message word on stack top, this routine consumes constant & message word into #! hash state. #! #! Expected stack state looks like @@ -203,7 +203,7 @@ proc.consume_message_word u32wrapping_add end -#! Given 32 -bytes hash state ( in terms of 8 SHA256 words ) and 64 -bytes input +#! Given 32 -bytes hash state ( in terms of 8 SHA256 words ) and 64 -bytes input #! message ( in terms of 16 SHA256 words ) on stack top, this routine computes #! whole message schedule of 64 message words and consumes them into hash state. #! @@ -1079,13 +1079,13 @@ proc.prepare_message_schedule_and_consume.2 movdn.7 end -#! Given 32 -bytes hash state ( in terms of 8 SHA256 words ) and precomputed message +#! Given 32 -bytes hash state ( in terms of 8 SHA256 words ) and precomputed message #! schedule of padding bytes ( in terms of 64 message words ), this routine consumes #! that into hash state, leaving final hash state, which is 32 -bytes SHA256 digest. #! #! Note, in SHA256 2-to-1 hashing, 64 -bytes are padded, which is processed as second message -#! block ( each SHA256 message block is 64 -bytes wide ). That message block is used for generating -#! message schedule of 64 SHA256 words. That's exactly what can be precomputed & is consumed here +#! block ( each SHA256 message block is 64 -bytes wide ). That message block is used for generating +#! message schedule of 64 SHA256 words. That's exactly what can be precomputed & is consumed here #! ( in this routine ) into provided hash state. #! #! Expected stack state: @@ -1098,7 +1098,7 @@ end #! #! Note, each SHA256 word is 32 -bit wide #! -#! See https://github.com/itzmeanjan/merklize-sha/blob/8a2c006/include/sha2_256.hpp#L148-L187 ( loop +#! See https://github.com/itzmeanjan/merklize-sha/blob/8a2c006/include/sha2_256.hpp#L148-L187 ( loop #! body execution when i = 1 i.e. consuming padding bytes ) proc.consume_padding_message_schedule dupw.1 @@ -1534,7 +1534,7 @@ end #! [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, ...] | m[0,16) = 32 -bit word #! #! Note, each SHA256 word is 32 -bit wide, so that's how input is expected. -#! As you've 64 -bytes, consider packing 4 consecutive bytes into single word, +#! As you've 64 -bytes, consider packing 4 consecutive bytes into single word, #! maintaining big endian byte order. #! #! Final stack state: @@ -1557,7 +1557,7 @@ end #! [m0, m1, m2, m3, m4, m5, m6, m7, ...] | m[0,8) = 32 -bit word #! #! Note, each SHA256 word is 32 -bit wide, so that's how input is expected. -#! As you've 32 -bytes, consider packing 4 consecutive bytes into single word, +#! As you've 32 -bytes, consider packing 4 consecutive bytes into single word, #! maintaining big endian byte order. #! #! Final stack state: @@ -1566,7 +1566,7 @@ end #! #! SHA256 digest is represented in terms of eight 32 -bit words ( big endian byte order ). export.hash_1to1 - # apply padding, see padding rule in section 5.1.1 of + # apply padding, see padding rule in section 5.1.1 of # https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf push.256.0.0.0.0.0.0.2147483648 swapdw diff --git a/stdlib/asm/math/ecgfp5/base_field.masm b/stdlib/asm/math/ecgfp5/base_field.masm index e36187404c..30459ddfb4 100644 --- a/stdlib/asm/math/ecgfp5/base_field.masm +++ b/stdlib/asm/math/ecgfp5/base_field.masm @@ -11,7 +11,7 @@ #! #! See section 3.2 of https://eprint.iacr.org/2022/274.pdf #! -#! For reference implementation in high level language, see +#! For reference implementation in high level language, see #! https://github.com/pornin/ecgfp5/blob/ce059c6/python/ecGFp5.py#L607-L616 export.add repeat.5 @@ -34,7 +34,7 @@ end #! #! See section 3.2 of https://eprint.iacr.org/2022/274.pdf #! -#! For reference implementation in high level language, see +#! For reference implementation in high level language, see #! https://github.com/pornin/ecgfp5/blob/ce059c6/python/ecGFp5.py#L629-L638 export.sub repeat.5 @@ -45,7 +45,7 @@ export.sub end #! Given two GF(p^5) elements on stack, this routine computes modular -#! multiplication ( including reduction by irreducible polynomial ) +#! multiplication ( including reduction by irreducible polynomial ) #! over extension field GF(p^5) s.t. p = 2^64 - 2^32 + 1 #! #! Expected stack state : @@ -58,12 +58,12 @@ end #! #! See section 3.2 of https://eprint.iacr.org/2022/274.pdf #! -#! For reference implementation in high level language, see +#! For reference implementation in high level language, see #! https://github.com/pornin/ecgfp5/blob/ce059c6/python/ecGFp5.py#L676-L689 export.mul # compute {c0, c1, c2, c3, c4} - five coefficients of resulting # degree-4 polynomial - + # compute c4 dup.9 dup.1 @@ -90,7 +90,7 @@ export.mul dup.6 dup.6 mul - + add # compute c3 @@ -101,7 +101,7 @@ export.mul dup.9 dup.4 mul - + add dup.8 @@ -137,7 +137,7 @@ export.mul dup.8 dup.6 mul - + add dup.12 @@ -221,7 +221,7 @@ export.mul end #! Given one GF(p^5) element on stack, this routine computes modular -#! squaring ( including reduction by irreducible polynomial ) +#! squaring ( including reduction by irreducible polynomial ) #! over extension field GF(p^5) s.t. p = 2^64 - 2^32 + 1 #! #! This routine has same effect as calling mul(a, a) | a ∈ GF(p^5) @@ -236,7 +236,7 @@ end #! #! See section 3.2 of https://eprint.iacr.org/2022/274.pdf #! -#! For reference implementation in high level language, see +#! For reference implementation in high level language, see #! https://github.com/pornin/ecgfp5/blob/ce059c6/python/ecGFp5.py#L709-L715 export.square # compute {b0, b1, b2, b3, b4} - five coefficients of resulting @@ -285,7 +285,7 @@ export.square dup.3 dup.4 mul - + dup.5 dup.4 mul @@ -411,7 +411,7 @@ end #! #! See section 3.2 of https://eprint.iacr.org/2022/274.pdf #! -#! For reference implementation in high level language, see +#! For reference implementation in high level language, see #! https://github.com/pornin/ecgfp5/blob/ce059c6/python/ecGFp5.py#L751-L775 #! #! Note, this routine will not panic even when operand `a` is zero. @@ -508,7 +508,7 @@ end #! #! See section 3.2 of https://eprint.iacr.org/2022/274.pdf #! -#! For reference implementation in high level language, see +#! For reference implementation in high level language, see #! https://github.com/pornin/ecgfp5/blob/ce059c6/python/ecGFp5.py#L777-L781 export.div repeat.5 @@ -1446,7 +1446,7 @@ export.legendre movup.5 mul mul.3 - + add movup.4 @@ -1473,7 +1473,7 @@ export.legendre exec.base_legendre end -#! Given an element v ∈ GF(p^5), this routine attempts to compute square root of v, +#! Given an element v ∈ GF(p^5), this routine attempts to compute square root of v, #! if that number is a square. #! #! At beginning stack looks like @@ -1584,11 +1584,11 @@ end #! Given two elements a, b ∈ GF(p^5), this routine produces single field element r, #! denoting whether a == b. #! -#! Expected stack state +#! Expected stack state #! #! [a0, a1, a2, a3, a4, b0, b1, b2, b3, b4, ...] #! -#! Final stack state +#! Final stack state #! #! [r, ...] #! @@ -1628,11 +1628,11 @@ end #! Given two elements a, b ∈ GF(p^5), this routine produces single field element r, #! denoting whether a != b. #! -#! Expected stack state +#! Expected stack state #! #! [a0, a1, a2, a3, a4, b0, b1, b2, b3, b4, ...] #! -#! Final stack state +#! Final stack state #! #! [r, ...] #! diff --git a/stdlib/asm/math/ecgfp5/group.masm b/stdlib/asm/math/ecgfp5/group.masm index 909a70fbef..a9967baffb 100644 --- a/stdlib/asm/math/ecgfp5/group.masm +++ b/stdlib/asm/math/ecgfp5/group.masm @@ -4,18 +4,18 @@ use.std::math::ecgfp5::base_field #! an element ∈ GF(p^5) | p = 2^64 - 2^32 + 1, this routine verifies whether #! given point can be successfully decoded or not #! -#! Expected stack state +#! Expected stack state #! #! [w0, w1, w2, w3, w4, ...] #! -#! Final stack state +#! Final stack state #! #! [flg, ...] #! #! If w can be decoded, flg = 1 #! Else flg = 0 #! -#! Note, if w = (0, 0, 0, 0, 0), it can be successfully decoded to point +#! Note, if w = (0, 0, 0, 0, 0), it can be successfully decoded to point #! at infinity i.e. flg = 1, in that case. #! #! See https://github.com/pornin/ecgfp5/blob/ce059c6/python/ecGFp5.py#L1043-L1052 @@ -32,7 +32,7 @@ export.validate swap sub.1052 swap # = delta - + exec.base_field::legendre eq.1 movdn.5 @@ -52,11 +52,11 @@ end #! it into x, y coordinates, along with boolean field element denoting whether it's #! point-at-infinity or not. #! -#! Expected stack state +#! Expected stack state #! #! [w0, w1, w2, w3, w4, ...] #! -#! Final state state +#! Final state state #! #! [x0, x1, x2, x3, x4, y0, y1, y2, y3, y4, inf, flg, ...] #! @@ -174,14 +174,14 @@ export.decode end #! Given an elliptic curve point as Weierstraß coordinates (X, Y) along with -#! boolean field element `inf`, denoting whether this is point-at-infinity or not, +#! boolean field element `inf`, denoting whether this is point-at-infinity or not, #! this routine encodes it to a single element ∈ GF(p^5) | p = 2^64 - 2^32 + 1 #! -#! Expected stack state +#! Expected stack state #! #! [x0, x1, x2, x3, x4, y0, y1, y2, y3, y4, inf, ...] #! -#! Final stack state +#! Final stack state #! #! [w0, w1, w2, w3, w4, ...] #! @@ -213,7 +213,7 @@ end #! Given two elliptic curve points ( say a, b ) as Weierstraß coordinates (X, Y) on stack, #! this routine computes elliptic curve point c, resulting from a + b. #! -#! Following point addition formula is complete and it works when two points are +#! Following point addition formula is complete and it works when two points are #! same/ different or input operands are point-at-infinity. #! #! Expected stack state @@ -585,7 +585,7 @@ end #! #! Scalar e should be lesser than 1067993516717146951041484916571792702745057740581727230159139685185762082554198619328292418486241 ( prime number ). #! Note, scalar e should be provided as 10 limbs on stack, each of 32 -bit, representing it in radix-2^32 form. -#! +#! #! Given a scalar e ( as arbitrary width big integer ), following python code snippet should convert it to desired input form #! #! [(a >> (32*i)) & 0xffff_ffff for i in range(10)] @@ -715,15 +715,15 @@ export.mul.10 loc_loadw.5 end -#! Given a 319 -bit scalar ( say e ) on stack, this routine computes elliptic curve point +#! Given a 319 -bit scalar ( say e ) on stack, this routine computes elliptic curve point #! b s.t. b = e * G, using double-and-add technique | G = conventional group generator point. #! #! Group generator point https://github.com/pornin/ecgfp5/blob/ce059c6/rust/src/curve.rs#L67-L83 #! #! Scalar e should be lesser than N ( = 1067993516717146951041484916571792702745057740581727230159139685185762082554198619328292418486241 ). #! Note, scalar e should be provided as 10 limbs on stack, each of 32 -bit, representing it in radix-2^32 form. -#! -#! Given a 319 -bit scalar e, following python code snippet should convert it to +#! +#! Given a 319 -bit scalar e, following python code snippet should convert it to #! desired input form i.e. radix-2^32 representation, having ten 32 -bit limbs #! #! [(e >> (32*i)) & 0xffff_ffff for i in range(10)] diff --git a/stdlib/asm/math/ecgfp5/scalar_field.masm b/stdlib/asm/math/ecgfp5/scalar_field.masm index 84fa6aa489..e9a37da8c8 100644 --- a/stdlib/asm/math/ecgfp5/scalar_field.masm +++ b/stdlib/asm/math/ecgfp5/scalar_field.masm @@ -1,7 +1,7 @@ #! Performs raw subtraction of scalar element ( say b ) from another one ( say a ), #! without any reduction i.e. r = a - b #! -#! Expected stack state +#! Expected stack state #! #! [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ...] #! @@ -131,7 +131,7 @@ proc.sub_inner movup.9 movup.10 # = (r0, r1, r2, r3, r4, r5, r6, r7, r8, r9) - movup.10 # = c + movup.10 # = c end #! Selects scalar based on value of c i.e. r = c == 0 ? a : b | c ∈ {0, 0xffff_ffff} @@ -238,11 +238,11 @@ proc.select end #! Montgomery multiplication of two radix-2^32 scalar field elements s.t. each -#! number can be represented using 10 limbs, each of 32 -bit width, returning +#! number can be represented using 10 limbs, each of 32 -bit width, returning #! #! r = (a * b) / 2^320 (mod N) | N = 319 -bit prime ( See https://github.com/itzmeanjan/miden/blob/6a611e693601577864da3e43e745525b83c0030d/miden/tests/integration/stdlib/math/ext5_scalar.rs#L24-L35 ) #! -#! Expected stack state +#! Expected stack state #! #! [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ...] #! @@ -271,7 +271,7 @@ export.mont_mul.8 dup loc_load.0 u32wrapping_mul - u32wrapping_mul.91978719 # more about this literal constant + u32wrapping_mul.91978719 # more about this literal constant loc_store.4 # https://github.com/itzmeanjan/miden/blob/e7038e45865a7032a0629346921a77010e82862d/miden/tests/integration/stdlib/math/ext5_scalar.rs#L46-L54 # cached f @@ -3744,7 +3744,7 @@ end #! #! [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, ...] #! -#! Final stack state +#! Final stack state #! #! [r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, ...] #! @@ -3761,7 +3761,7 @@ end #! #! [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, ...] #! -#! Final stack state +#! Final stack state #! #! [r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, ...] #! @@ -3785,11 +3785,11 @@ proc.sqr repeat.10 dup.9 end - + exec.mont_mul end -#! Given an element ( say a ) of scalar field, this routine computes multiplicative inverse ( say a' ) +#! Given an element ( say a ) of scalar field, this routine computes multiplicative inverse ( say a' ) #! of that element s.t. a * a' = 1 ( mod N ) | N = Scalar field prime #! #! Expected stack state diff --git a/stdlib/asm/math/ntt512.masm b/stdlib/asm/math/ntt512.masm index 3a40017b9b..c3bd0f8ca1 100644 --- a/stdlib/asm/math/ntt512.masm +++ b/stdlib/asm/math/ntt512.masm @@ -1,7 +1,7 @@ #! Applies four NTT butterflies on four different indices, given following stack state #! #! [k0, k1, k2, k3, A0, B0, C0, D0, A1, B1, C1, D1] -#! +#! #! Here k`i` => i-th constant i.e. ω raised to *some* power | ω => 2N -th primitive root of unity, N = 512 #! #! A{0, 1} -> first butterfly will be applied on these two elements @@ -662,7 +662,7 @@ export.forward.128 push.72058693532778496.72058693532778496.72058693532778496.72058693532778496 push.72058693532778496.72058693532778496.72058693532778496.72058693532778496 - + push.0.0.0.0 dupw @@ -1101,7 +1101,7 @@ end #! Applies four inverse NTT butterflies on four different indices, given following stack state #! #! [k0, k1, k2, k3, A0, B0, C0, D0, A1, B1, C1, D1] -#! +#! #! Here k`i` => i-th constant i.e. negative of ω raised to *some* power | ω => 2N -th primitive root of unity, N = 512 #! #! A{0, 1} -> first inverse butterfly will be applied on these two elements @@ -1218,7 +1218,7 @@ proc.mul_by_invN end #! Applies inverse NTT on a vector of length 512, where each element ∈ Zp | p = 2^64 − 2^32 + 1, -#! producing elements in time domain in standard order, while input vector is expected to be in +#! producing elements in time domain in standard order, while input vector is expected to be in #! bit-reversed order. #! #! Expected stack state as input: @@ -1231,7 +1231,7 @@ end #! addr{i} holds values V[(i << 2) .. ((i+1) << 2)] | i ∈ [0, 128) and addr0 = start_addr #! #! After applying iNTT, normal order vector is returned back as single absolute memory -#! addresses on stack, where it begins storing the polynomial. Consecutive 127 addresses should +#! addresses on stack, where it begins storing the polynomial. Consecutive 127 addresses should #! similarly be computable using `add.1` instruction. #! #! [start_addr', ...] | Single absolute memory address, where resulting polynomial starts diff --git a/stdlib/asm/math/poly512.masm b/stdlib/asm/math/poly512.masm index 42d6eb3bbe..c94397ea41 100644 --- a/stdlib/asm/math/poly512.masm +++ b/stdlib/asm/math/poly512.masm @@ -1,7 +1,7 @@ use.std::math::ntt512 use.std::math::u64 -#! Given two consecutive words on stack, this routine performs +#! Given two consecutive words on stack, this routine performs #! element wise multiplication, while keeping resulting single #! word on stack. #! @@ -33,7 +33,7 @@ proc.mul_word movdn.3 end -#! Given two consecutive words on stack, this routine performs +#! Given two consecutive words on stack, this routine performs #! element wise addition, while keeping resulting single #! word on stack. #! @@ -201,7 +201,7 @@ proc.neg_word end #! Given a field element, this routine does centered reduction using Miden VM -#! prime ( say Q ) and then reduces it using Falcon Post Quantum Digital +#! prime ( say Q ) and then reduces it using Falcon Post Quantum Digital #! Signature Algorithm prime ( say Q' ) #! #! Q = 2 ^ 64 - 2 ^ 32 + 1 @@ -300,7 +300,7 @@ end #! #! [ ... ] #! -#! Consecutive 127 memory addresses can be computed from starting memory address ( living on stack top ) by +#! Consecutive 127 memory addresses can be computed from starting memory address ( living on stack top ) by #! continuing to apply `INCR` ( = add.1 ) instruction on previous absolute memory address. #! #! Note, input memory addresses are considered to be read-only, they are not mutated. @@ -414,7 +414,7 @@ end #! #! [ ... ] #! -#! Consecutive 127 memory addresses can be computed from starting memory address ( living on stack top ) by +#! Consecutive 127 memory addresses can be computed from starting memory address ( living on stack top ) by #! continuing to apply `INCR` ( = add.1 ) instruction on previous absolute memory address. #! #! Note, input memory addresses are considered to be read-only, they are not mutated. @@ -477,7 +477,7 @@ end #! #! [ ... ] #! -#! Consecutive 127 memory addresses can be computed from starting memory address ( living on stack top ) by +#! Consecutive 127 memory addresses can be computed from starting memory address ( living on stack top ) by #! continuing to apply `INCR` ( = add.1 ) instruction on previous absolute memory address. #! #! Note, input memory addresses are considered to be read-only, they are not mutated. @@ -527,7 +527,7 @@ end #! #! [ ... ] #! -#! Consecutive 127 memory addresses can be computed from starting memory address ( living on stack top ) by +#! Consecutive 127 memory addresses can be computed from starting memory address ( living on stack top ) by #! continuing to apply `INCR` ( = add.1 ) instruction on previous absolute memory address. #! #! Note, input memory addresses are considered to be read-only, they are not mutated. diff --git a/stdlib/asm/math/secp256k1/base_field.masm b/stdlib/asm/math/secp256k1/base_field.masm index 088dd65880..5b5fde8250 100644 --- a/stdlib/asm/math/secp256k1/base_field.masm +++ b/stdlib/asm/math/secp256k1/base_field.masm @@ -32,7 +32,7 @@ proc.sbb u32overflowing_sub end -#! Given a secp256k1 base field element in radix-2^32 representation ( Montgomery form ) +#! Given a secp256k1 base field element in radix-2^32 representation ( Montgomery form ) #! and 32 -bit unsigned integer, this routine computes a 288 -bit number. #! #! Input via stack is expected in this form @@ -46,12 +46,12 @@ end #! See https://github.com/itzmeanjan/secp256k1/blob/6e5e654823a073add7d62b21ed88e9de9bb06869/field/base_field_utils.py#L65-L83 proc.u256xu32 movup.8 - + push.0 dup.1 movup.3 u32overflowing_madd - + dup.2 movup.4 u32overflowing_madd @@ -169,7 +169,7 @@ end proc.u288_reduce dup push.3525653809 - u32wrapping_mul + u32wrapping_mul # q at stack top # push.0 @@ -231,9 +231,9 @@ proc.u288_reduce movup.8 end -#! Given two 256 -bit numbers ( elements belonging to secp256k1 base field ) on stack, +#! Given two 256 -bit numbers ( elements belonging to secp256k1 base field ) on stack, #! where each number is represented in radix-2^32 form ( i.e. each number having eight -#! 32 -bit limbs ), following function computes modular multiplication of those two +#! 32 -bit limbs ), following function computes modular multiplication of those two #! operands, computing 256 -bit result, which belongs to secp256k1 base field. #! #! Stack expected as below, holding input @@ -371,9 +371,9 @@ proc.sqr exec.mul end -#! Given two 256 -bit numbers ( elements belonging to secp256k1 base field ) on stack, -#! where each number is represented in radix-2^32 form ( i.e. each number having eight -#! 32 -bit limbs ), following function computes modular addition of those two operands, +#! Given two 256 -bit numbers ( elements belonging to secp256k1 base field ) on stack, +#! where each number is represented in radix-2^32 form ( i.e. each number having eight +#! 32 -bit limbs ), following function computes modular addition of those two operands, #! in secp256k1 base field. #! #! Stack expected as below, holding input @@ -440,7 +440,7 @@ export.add movup.7 end -#! Given a secp256k1 base field element ( say a ) on stack, represented in Montgomery form +#! Given a secp256k1 base field element ( say a ) on stack, represented in Montgomery form #! ( i.e. number having eight 32 -bit limbs ), following function negates it to #! field element a' | a' + a = 0 #! @@ -488,7 +488,7 @@ export.neg exec.sbb drop - + swap movup.2 movup.3 @@ -519,7 +519,7 @@ export.sub exec.add end -#! Given a 256 -bit number on stack, represented in radix-2^32 form i.e. eight 32 -bit limbs, +#! Given a 256 -bit number on stack, represented in radix-2^32 form i.e. eight 32 -bit limbs, #! this routine computes Montgomery representation of provided radix-2^32 number. #! #! Stack expected in form @@ -541,7 +541,7 @@ export.to_mont exec.mul end -#! Given a 256 -bit number on stack, represented in Montgomery form i.e. eight 32 -bit limbs, +#! Given a 256 -bit number on stack, represented in Montgomery form i.e. eight 32 -bit limbs, #! this routine computes radix-2^32 representation of provided u256 number. #! #! Stack expected as diff --git a/stdlib/asm/math/secp256k1/group.masm b/stdlib/asm/math/secp256k1/group.masm index add96c64f5..5478223347 100644 --- a/stdlib/asm/math/secp256k1/group.masm +++ b/stdlib/asm/math/secp256k1/group.masm @@ -1,11 +1,11 @@ use.std::math::secp256k1::base_field #! Given a secp256k1 point in projective coordinate system ( i.e. with x, y, z -coordinates -#! as secp256k1 prime field elements, represented in Montgomery form ), this routine adds -#! that point with self i.e. does point doubling on elliptic curve, using exception-free -#! doubling formula from algorithm 9 of https://eprint.iacr.org/2015/1060.pdf, while +#! as secp256k1 prime field elements, represented in Montgomery form ), this routine adds +#! that point with self i.e. does point doubling on elliptic curve, using exception-free +#! doubling formula from algorithm 9 of https://eprint.iacr.org/2015/1060.pdf, while #! following prototype implementation https://github.com/itzmeanjan/secp256k1/blob/ec3652a/point.py#L131-L165 -#! +#! #! Input: #! #! 12 memory addresses on stack such that first 6 memory addresses are for input point & @@ -20,7 +20,7 @@ use.std::math::secp256k1::base_field #! #! Expected stack during invocation of this routine: #! -#! [x_addr[0..4], x_addr[4..8], y_addr[0..4], y_addr[4..8], z_addr[0..4], z_addr[4..8], +#! [x_addr[0..4], x_addr[4..8], y_addr[0..4], y_addr[4..8], z_addr[0..4], z_addr[4..8], #! x3_addr[0..4], x3_addr[4..8], y3_addr[0..4], y3_addr[4..8], z3_addr[0..4], z3_addr[4..8]] #! #! Note, (X, Y, Z) => input point @@ -71,7 +71,7 @@ export.double.12 exec.base_field::add # = z3 loc_storew.2 - dropw + dropw loc_storew.3 dropw # cache z3 @@ -96,7 +96,7 @@ export.double.12 exec.base_field::mul # = t1 loc_storew.4 - dropw + dropw loc_storew.5 dropw # cache t1 @@ -132,7 +132,7 @@ export.double.12 exec.base_field::mul # = x3 loc_storew.8 - dropw + dropw loc_storew.9 dropw # cache x3 @@ -149,7 +149,7 @@ export.double.12 exec.base_field::add # = y3 loc_storew.10 - dropw + dropw loc_storew.11 dropw # cache y3 @@ -166,7 +166,7 @@ export.double.12 exec.base_field::mul # = z3 loc_storew.2 - dropw + dropw loc_storew.3 dropw # cache z3 @@ -214,7 +214,7 @@ export.double.12 exec.base_field::add # = y3 loc_storew.10 - dropw + dropw loc_storew.11 dropw # cache y3 @@ -251,7 +251,7 @@ export.double.12 exec.base_field::add # = x3 loc_storew.8 - dropw + dropw loc_storew.9 dropw # cache x3 @@ -307,16 +307,16 @@ end #! this routine adds those two points on elliptic curve, using exception-free addition formula from #! algorithm 7 of https://eprint.iacr.org/2015/1060.pdf, while following prototype #! implementation https://github.com/itzmeanjan/secp256k1/blob/ec3652a/point.py#L60-L115 -#! +#! #! Input: #! #! 18 memory addresses on stack such that first 6 memory addresses are for first input point, next 6 -#! memory addresses holding x, y, z -coordinates of second input point & last 6 addresses are for storing +#! memory addresses holding x, y, z -coordinates of second input point & last 6 addresses are for storing #! resulting point ( addition of two input points ). #! #! Expected stack during invocation of this routine: #! -#! [x1_addr[0..4], x1_addr[4..8], y1_addr[0..4], y1_addr[4..8], z1_addr[0..4], z1_addr[4..8], +#! [x1_addr[0..4], x1_addr[4..8], y1_addr[0..4], y1_addr[4..8], z1_addr[0..4], z1_addr[4..8], #! x2_addr[0..4], x2_addr[4..8], y2_addr[0..4], y2_addr[4..8], z2_addr[0..4], z2_addr[4..8], #! x3_addr[0..4], x3_addr[4..8], y3_addr[0..4], y3_addr[4..8], z3_addr[0..4], z3_addr[4..8]] #! @@ -359,7 +359,7 @@ export.add.16 exec.base_field::mul # = t0 loc_storew.0 - dropw + dropw loc_storew.1 dropw # cache t0 @@ -388,7 +388,7 @@ export.add.16 exec.base_field::mul # = t1 loc_storew.2 - dropw + dropw loc_storew.3 dropw # cache t1 @@ -417,7 +417,7 @@ export.add.16 exec.base_field::mul # = t2 loc_storew.4 - dropw + dropw loc_storew.5 dropw # cache t2 @@ -446,7 +446,7 @@ export.add.16 exec.base_field::add # = t3 loc_storew.6 - dropw + dropw loc_storew.7 dropw # cache t3 @@ -472,7 +472,7 @@ export.add.16 push.0.0.0.0 movup.4 mem_loadw # x2 on stack top - + exec.base_field::add # = t4 push.0.0.0.0 @@ -483,7 +483,7 @@ export.add.16 exec.base_field::mul # = t3 loc_storew.6 - dropw + dropw loc_storew.7 dropw # cache t3 @@ -507,7 +507,7 @@ export.add.16 exec.base_field::sub # = t3 loc_storew.6 - dropw + dropw loc_storew.7 dropw # cache t3 @@ -536,7 +536,7 @@ export.add.16 exec.base_field::add # = t4 loc_storew.8 - dropw + dropw loc_storew.9 dropw # cache t4 @@ -575,7 +575,7 @@ export.add.16 exec.base_field::mul # = t4 loc_storew.8 - dropw + dropw loc_storew.9 dropw # cache t4 @@ -599,7 +599,7 @@ export.add.16 exec.base_field::sub # = t4 loc_storew.8 - dropw + dropw loc_storew.9 dropw # cache t4 @@ -628,7 +628,7 @@ export.add.16 exec.base_field::add # = x3 loc_storew.10 - dropw + dropw loc_storew.11 dropw # cache x3 @@ -665,7 +665,7 @@ export.add.16 exec.base_field::mul # = x3 loc_storew.10 - dropw + dropw loc_storew.11 dropw # cache x3 @@ -689,7 +689,7 @@ export.add.16 exec.base_field::sub # = y3 loc_storew.12 - dropw + dropw loc_storew.13 dropw # cache y3 @@ -716,7 +716,7 @@ export.add.16 exec.base_field::add # = t0 loc_storew.0 - dropw + dropw loc_storew.1 dropw # cache t0 @@ -743,7 +743,7 @@ export.add.16 exec.base_field::add # = z3 loc_storew.14 - dropw + dropw loc_storew.15 dropw # cache z3 @@ -760,7 +760,7 @@ export.add.16 exec.base_field::sub # = t1 loc_storew.2 - dropw + dropw loc_storew.3 dropw # cache t1 @@ -787,7 +787,7 @@ export.add.16 exec.base_field::mul # = x3 loc_storew.10 - dropw + dropw loc_storew.11 dropw # cache x3 @@ -812,7 +812,7 @@ export.add.16 exec.base_field::add # = x3 loc_storew.10 - dropw + dropw loc_storew.11 dropw # cache x3 @@ -829,7 +829,7 @@ export.add.16 exec.base_field::mul # = y3 loc_storew.12 - dropw + dropw loc_storew.13 dropw # cache y3 @@ -853,7 +853,7 @@ export.add.16 exec.base_field::add # = y3 loc_storew.12 - dropw + dropw loc_storew.13 dropw # cache y3 @@ -870,7 +870,7 @@ export.add.16 exec.base_field::mul # = t0 loc_storew.0 - dropw + dropw loc_storew.1 dropw # cache t0 @@ -894,7 +894,7 @@ export.add.16 exec.base_field::add # = z3 loc_storew.14 - dropw + dropw loc_storew.15 dropw # cache z3 @@ -939,11 +939,11 @@ export.add.16 dropw # write z3[4..8] to memory end -#! Given an elliptic curve point in projective coordinate system ( total 24 field elements -#! required for representing x, y, z coordinate values s.t. they are provided by 6 distinct -#! memory addresses ) and a 256 -bit scalar, in radix-2^32 representation ( such that it -#! takes 8 stack elements to represent whole scalar, where each limb is of 32 -bit width ), -#! this routine multiplies elliptic curve point by given scalar, producing another point +#! Given an elliptic curve point in projective coordinate system ( total 24 field elements +#! required for representing x, y, z coordinate values s.t. they are provided by 6 distinct +#! memory addresses ) and a 256 -bit scalar, in radix-2^32 representation ( such that it +#! takes 8 stack elements to represent whole scalar, where each limb is of 32 -bit width ), +#! this routine multiplies elliptic curve point by given scalar, producing another point #! on secp256k1 curve, which will also be presented in projective coordinate system. #! #! Input: @@ -971,8 +971,8 @@ end #! Z_addr_0, Z_addr_1 -> Resulting secp256k1 point's Z -coordinate written, in Montgomery form, in given addresses #! #! One interested in resulting point, should read from provided addresses on stack. -#! -#! This routine implements double-and-add algorithm, while following +#! +#! This routine implements double-and-add algorithm, while following #! https://github.com/itzmeanjan/secp256k1/blob/d23ea7d/point.py#L174-L186 #! #! If base point being multiplied is secp256k1 curve generator point, one should use `gen_point` routine, @@ -1137,7 +1137,7 @@ export.mul.18 u32unchecked_shr.1 end - + drop end @@ -1199,9 +1199,9 @@ end #! Z_addr_0, Z_addr_1 -> Resulting secp256k1 point's Z -coordinate written, in Montgomery form, in given addresses #! #! One interested in resulting point, should read from provided address on stack. -#! -#! This routine implements double-and-add algorithm, while following -#! https://github.com/itzmeanjan/secp256k1/blob/d23ea7d/point.py#L174-L186 +#! +#! This routine implements double-and-add algorithm, while following +#! https://github.com/itzmeanjan/secp256k1/blob/d23ea7d/point.py#L174-L186 #! #! Note, this routine is a specialised instantiation of secp256k1 point multiplication, where we know what the base #! point is, so we enjoy faster computation ( because all point doublings can be precomputed, saving us 256 point doublings ! ). @@ -1210,29 +1210,29 @@ export.gen_mul.20 # see https://github.com/itzmeanjan/secp256k1/blob/d23ea7d/point.py#L40-L45 push.0.0.0.0 loc_storew.0 - dropw + dropw push.0.0.0.0 loc_storew.1 dropw # init & cache res_X push.0.0.1.977 loc_storew.2 - dropw + dropw push.0.0.0.0 loc_storew.3 dropw # init & cache res_Y push.0.0.0.0 loc_storew.4 - dropw + dropw push.0.0.0.0 loc_storew.5 dropw # init & cache res_Z loc_storew.18 - dropw + dropw loc_storew.19 - dropw + dropw # push (2^255)G into stack push.1767015067.3527058907.3725831105.456741272 @@ -3293,21 +3293,21 @@ export.gen_mul.20 movdn.4 u32unchecked_shr.1 loc_storew.18 - dropw + dropw if.true loc_storew.12 - dropw + dropw loc_storew.13 - dropw + dropw loc_storew.14 - dropw + dropw loc_storew.15 - dropw + dropw loc_storew.16 - dropw + dropw loc_storew.17 - dropw + dropw locaddr.11 locaddr.10 @@ -3362,13 +3362,13 @@ export.gen_mul.20 loc_loadw.18 movdn.3 loc_storew.18 - dropw + dropw end push.0.0.0.0 loc_loadw.19 loc_storew.18 - dropw + dropw end dup diff --git a/stdlib/asm/math/secp256k1/scalar_field.masm b/stdlib/asm/math/secp256k1/scalar_field.masm index 40a8519ab8..6347b5816c 100644 --- a/stdlib/asm/math/secp256k1/scalar_field.masm +++ b/stdlib/asm/math/secp256k1/scalar_field.masm @@ -32,7 +32,7 @@ proc.sbb u32overflowing_sub end -#! Given a secp256k1 scalar field element in radix-2^32 representation ( Montgomery form ) +#! Given a secp256k1 scalar field element in radix-2^32 representation ( Montgomery form ) #! and 32 -bit unsigned integer, this routine computes a 288 -bit number. #! #! Input via stack is expected in this form @@ -46,12 +46,12 @@ end #! See https://github.com/itzmeanjan/secp256k1/blob/6e5e654823a073add7d62b21ed88e9de9bb06869/field/scalar_field_utils.py#L65-L83 proc.u256xu32 movup.8 - + push.0 dup.1 movup.3 u32overflowing_madd - + dup.2 movup.4 u32overflowing_madd @@ -169,7 +169,7 @@ end proc.u288_reduce dup push.1435021631 - u32wrapping_mul + u32wrapping_mul # q at stack top # push.0 @@ -231,9 +231,9 @@ proc.u288_reduce movup.8 end -#! Given two 256 -bit numbers ( elements belonging to secp256k1 scalar field ) on stack, +#! Given two 256 -bit numbers ( elements belonging to secp256k1 scalar field ) on stack, #! where each number is represented in radix-2^32 form ( i.e. each number having eight -#! 32 -bit limbs ), following function computes modular multiplication of those two +#! 32 -bit limbs ), following function computes modular multiplication of those two #! operands, computing 256 -bit result, which belongs to secp256k1 scalar field. #! #! Stack expected as below, holding input @@ -403,7 +403,7 @@ proc.sqr exec.mul end -#! Given an element of secp256k1 scalar field, represented in Montgomery form i.e. eight 32 -bit limbs, +#! Given an element of secp256k1 scalar field, represented in Montgomery form i.e. eight 32 -bit limbs, #! this routine computes radix-2^32 representation of provided u256 number. #! #! Stack expected as diff --git a/stdlib/asm/math/u256.masm b/stdlib/asm/math/u256.masm index a1d93f0c3d..9dd5c49086 100644 --- a/stdlib/asm/math/u256.masm +++ b/stdlib/asm/math/u256.masm @@ -578,4 +578,4 @@ export.mul_unsafe.6 push.0.0.0.0 loc_loadw.4 swapw -end \ No newline at end of file +end diff --git a/stdlib/asm/sys.masm b/stdlib/asm/sys.masm index f677779880..727e8cf43b 100644 --- a/stdlib/asm/sys.masm +++ b/stdlib/asm/sys.masm @@ -1,6 +1,6 @@ #! Removes elements deep in the stack until the depth of the stack is exactly 16. The elements #! are removed in such a way that the top 16 elements of the stack remain unchanged. If the stack -#! would otherwise contain more than 16 elements at the end of execution, then adding a call to this +#! would otherwise contain more than 16 elements at the end of execution, then adding a call to this #! function at the end will reduce the size of the public inputs that are shared with the verifier. #! Input: Stack with 16 or more elements. #! Output: Stack with only the original top 16 elements. From 5605fd65915d60a28b3dfe50c760857b8876fa12 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Wed, 1 Mar 2023 12:44:13 +0100 Subject: [PATCH 04/47] test: test for long procedure names --- assembly/src/procedures/mod.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/assembly/src/procedures/mod.rs b/assembly/src/procedures/mod.rs index 9d95967765..e772f97bae 100644 --- a/assembly/src/procedures/mod.rs +++ b/assembly/src/procedures/mod.rs @@ -311,3 +311,19 @@ impl ops::Deref for CallSet { &self.0 } } + +#[cfg(test)] +mod test { + use super::{LabelError, ProcedureName, MAX_LABEL_LEN}; + + #[test] + fn test_procedure_name_max_len() { + assert!(ProcedureName::try_from("a".to_owned()).is_ok()); + + let long = "a".repeat(256); + assert_eq!( + ProcedureName::try_from(long.clone()), + Err(LabelError::LabelTooLong(long, MAX_LABEL_LEN)) + ); + } +} From 04ca47a88055497f3a6d01edce4f5b98b417aa13 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Wed, 1 Mar 2023 13:20:18 +0100 Subject: [PATCH 05/47] chore: using fallible/infallible conversions instead of casts This is a small readability change, that makes it clear that truncating of data is take care of --- assembly/src/parsers/io_ops.rs | 20 ++++++++++---------- assembly/src/parsers/u32_ops.rs | 6 +++--- assembly/src/procedures/mod.rs | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/assembly/src/parsers/io_ops.rs b/assembly/src/parsers/io_ops.rs index 317d660022..dc55892d62 100644 --- a/assembly/src/parsers/io_ops.rs +++ b/assembly/src/parsers/io_ops.rs @@ -262,7 +262,7 @@ fn parse_param_list(op: &Token, constants: &LocalConstMap) -> Result>( op: &Token, @@ -353,12 +353,12 @@ fn parse_hex_value(op: &Token, param_str: &str, param_idx: usize) -> Result Result { - if value <= u8::MAX as u64 { - Ok(Instruction(PushU8(value as u8))) - } else if value <= u16::MAX as u64 { - Ok(Instruction(PushU16(value as u16))) - } else if value <= u32::MAX as u64 { - Ok(Instruction(PushU32(value as u32))) + if let Ok(data) = u8::try_from(value) { + Ok(Instruction(PushU8(data))) + } else if let Ok(data) = u16::try_from(value) { + Ok(Instruction(PushU16(data))) + } else if let Ok(data) = u32::try_from(value) { + Ok(Instruction(PushU32(data))) } else if value < Felt::MODULUS { Ok(Instruction(PushFelt(Felt::new(value)))) } else { @@ -374,13 +374,13 @@ where { assert!(values_iter.len() != 0); let max_value = values_iter.clone().try_fold(0, |max, value| Ok(value?.max(max)))?; - if max_value <= u8::MAX as u64 { + if u8::try_from(max_value).is_ok() { let values_u8 = values_iter.map(|v| Ok(v? as u8)).collect::, _>>()?; Ok(Instruction(PushU8List(values_u8))) - } else if max_value <= u16::MAX as u64 { + } else if u16::try_from(max_value).is_ok() { let values_u16 = values_iter.map(|v| Ok(v? as u16)).collect::, _>>()?; Ok(Instruction(PushU16List(values_u16))) - } else if max_value <= u32::MAX as u64 { + } else if u32::try_from(max_value).is_ok() { let values_u32 = values_iter.map(|v| Ok(v? as u32)).collect::, _>>()?; Ok(Instruction(PushU32List(values_u32))) } else if max_value < Felt::MODULUS { diff --git a/assembly/src/parsers/u32_ops.rs b/assembly/src/parsers/u32_ops.rs index 36f39f8682..43b789db4f 100644 --- a/assembly/src/parsers/u32_ops.rs +++ b/assembly/src/parsers/u32_ops.rs @@ -221,7 +221,7 @@ pub fn parse_u32_div(op: &Token, checked: bool) -> Result { } 2 => { let value = parse_param::(op, 1)?; - check_div_by_zero(value as u64, op, 1)?; + check_div_by_zero(value.into(), op, 1)?; if checked { Ok(Instruction(U32CheckedDivImm(value))) } else { @@ -253,7 +253,7 @@ pub fn parse_u32_mod(op: &Token, checked: bool) -> Result { } 2 => { let value = parse_param::(op, 1)?; - check_div_by_zero(value as u64, op, 1)?; + check_div_by_zero(value.into(), op, 1)?; if checked { Ok(Instruction(U32CheckedModImm(value))) } else { @@ -285,7 +285,7 @@ pub fn parse_u32_divmod(op: &Token, checked: bool) -> Result } 2 => { let value = parse_param::(op, 1)?; - check_div_by_zero(value as u64, op, 1)?; + check_div_by_zero(value.into(), op, 1)?; if checked { Ok(Instruction(U32CheckedDivModImm(value))) } else { diff --git a/assembly/src/procedures/mod.rs b/assembly/src/procedures/mod.rs index 9d95967765..0a06fd4dec 100644 --- a/assembly/src/procedures/mod.rs +++ b/assembly/src/procedures/mod.rs @@ -176,7 +176,7 @@ impl Serializable for ProcedureName { impl Deserializable for ProcedureName { fn read_from(bytes: &mut ByteReader) -> Result { let num_bytes = bytes.read_u8()?; - let name_bytes = bytes.read_bytes(num_bytes as usize)?; + let name_bytes = bytes.read_bytes(num_bytes.into())?; let name = from_utf8(name_bytes).map_err(|_| SerializationError::InvalidUtf8)?; let name = ProcedureName::try_from(name.to_string())?; Ok(name) From f201208638afe613e6ec3c7ef25c0fc577f0b333 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Wed, 1 Mar 2023 12:31:27 +0100 Subject: [PATCH 06/47] bugfix: type cast must happen after comparison Language reference: > Casting from a larger integer to a smaller integer (e.g. u32 -> u8) will truncate https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions The above means only the lower bits are used for the comparison, if the length of the name was `257`, the cast would convert it to `1` and the value would be wrongly considered inside the range. --- assembly/src/errors.rs | 4 ++-- assembly/src/lib.rs | 2 +- assembly/src/parsers/labels.rs | 4 ++-- assembly/src/procedures/mod.rs | 16 +++++++++------- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/assembly/src/errors.rs b/assembly/src/errors.rs index e8cb856dfa..b40173a600 100644 --- a/assembly/src/errors.rs +++ b/assembly/src/errors.rs @@ -525,7 +525,7 @@ pub enum LabelError { EmptyLabel, InvalidFirstLetter(String), InvalidChars(String), - LabelTooLong(String, u8), + LabelTooLong(String, usize), Uppercase(String), } @@ -542,7 +542,7 @@ impl LabelError { Self::InvalidFirstLetter(label.to_string()) } - pub fn label_too_long(label: &str, max_len: u8) -> Self { + pub fn label_too_long(label: &str, max_len: usize) -> Self { Self::LabelTooLong(label.to_string(), max_len) } diff --git a/assembly/src/lib.rs b/assembly/src/lib.rs index db70a79d8c..a3d1f8e6b4 100644 --- a/assembly/src/lib.rs +++ b/assembly/src/lib.rs @@ -68,7 +68,7 @@ const MAX_U32_ROTATE_VALUE: u8 = 31; const MAX_EXP_BITS: u8 = 64; /// The maximum length of a constant or procedure's label. -const MAX_LABEL_LEN: u8 = 100; +const MAX_LABEL_LEN: usize = 100; /// The required length of the hexadecimal representation for an input value when more than one hex /// input is provided to `push` masm operation without period separators. diff --git a/assembly/src/parsers/labels.rs b/assembly/src/parsers/labels.rs index c6c9d54824..e1c9a53ec4 100644 --- a/assembly/src/parsers/labels.rs +++ b/assembly/src/parsers/labels.rs @@ -25,7 +25,7 @@ pub const PROCEDURE_LABEL_PARSER: LabelParser = LabelParser { /// Struct that specifies the rules for parsing labels. pub struct LabelParser { pub caps: bool, - pub max_len: u8, + pub max_len: usize, pub numbers_letters_underscore: bool, pub start_with_letter: bool, } @@ -39,7 +39,7 @@ impl LabelParser { if label.is_empty() { // label cannot be empty return Err(LabelError::empty_label()); - } else if label.len() > self.max_len as usize { + } else if label.len() > self.max_len { // label cannot be more than 100 characters long return Err(LabelError::label_too_long(&label, self.max_len)); } else if self.start_with_letter && !label.chars().next().unwrap().is_ascii_alphabetic() { diff --git a/assembly/src/procedures/mod.rs b/assembly/src/procedures/mod.rs index e772f97bae..7b7d175fcc 100644 --- a/assembly/src/procedures/mod.rs +++ b/assembly/src/procedures/mod.rs @@ -1,6 +1,6 @@ use super::{ crypto::hash::Blake3_192, AbsolutePath, BTreeSet, ByteReader, ByteWriter, CodeBlock, - Deserializable, LabelError, Serializable, SerializationError, String, ToString, MAX_LABEL_LEN, + Deserializable, LabelError, Serializable, SerializationError, String, ToString, MODULE_PATH_DELIM, PROCEDURE_LABEL_PARSER, }; use core::{ @@ -162,12 +162,14 @@ impl AsRef for ProcedureName { impl Serializable for ProcedureName { fn write_into(&self, target: &mut ByteWriter) -> Result<(), SerializationError> { let name_bytes = self.name.as_bytes(); - let num_bytes = name_bytes.len() as u8; - if num_bytes > MAX_LABEL_LEN { - return Err(SerializationError::LengthTooLong); - } + let num_bytes = name_bytes.len(); + + debug_assert!( + PROCEDURE_LABEL_PARSER.parse_label(self.name.clone()).is_ok(), + "The constructor should ensure the length is within limits" + ); - target.write_u8(num_bytes); + target.write_u8(num_bytes as u8); target.write_bytes(name_bytes); Ok(()) } @@ -314,7 +316,7 @@ impl ops::Deref for CallSet { #[cfg(test)] mod test { - use super::{LabelError, ProcedureName, MAX_LABEL_LEN}; + use super::{super::MAX_LABEL_LEN, LabelError, ProcedureName}; #[test] fn test_procedure_name_max_len() { From 1f00bef95af893694a180cdb437d34f576ff0b14 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Wed, 1 Mar 2023 18:24:16 +0100 Subject: [PATCH 07/47] chore: ignore pre-commit rev --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..7be126c62a --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# initial run of pre-commit +7e025f9b5d0feccfc2c9b1630f951a4256024906 From fe983d8847671cb2b5696de18270db0843ac6e59 Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Tue, 28 Feb 2023 11:10:21 +0100 Subject: [PATCH 08/47] feat: improve ux of debug cli closes #725 --- miden/src/cli/debug/command.rs | 141 +++++++++++++++++++++----------- miden/src/cli/debug/executor.rs | 49 +++++++---- miden/src/cli/debug/mod.rs | 19 ++++- 3 files changed, 138 insertions(+), 71 deletions(-) diff --git a/miden/src/cli/debug/command.rs b/miden/src/cli/debug/command.rs index 634f0bd1e6..70465a3687 100644 --- a/miden/src/cli/debug/command.rs +++ b/miden/src/cli/debug/command.rs @@ -1,7 +1,8 @@ /// debug commands supported by the debugger +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum DebugCommand { - PlayAll, - Play(usize), + Continue, + Next(usize), RewindAll, Rewind(usize), PrintState, @@ -21,68 +22,110 @@ impl DebugCommand { /// /// # Errors /// Returns an error if the command cannot be parsed. - pub fn parse(command: &str) -> Result { - match command { - "!next" => Ok(Self::Play(1)), - "!play" => Ok(Self::PlayAll), - "!prev" => Ok(Self::Rewind(1)), - "!rewind" => Ok(Self::RewindAll), - "!print" => Ok(Self::PrintState), - "!mem" => Ok(Self::PrintMem), - "!stack" => Ok(Self::PrintStack), - "!clock" => Ok(Self::Clock), - "!quit" => Ok(Self::Quit), - "!help" => Ok(Self::Help), - x if x.starts_with("!rewind.") => Self::parse_rewind(x), - x if x.starts_with("!play.") => Self::parse_play(command), - x if x.starts_with("!stack[") && x.ends_with("]") => Self::parse_print_stack(x), - x if x.starts_with("!mem[") && x.ends_with(']') => Self::parse_print_memory(x), + pub fn parse(command: &str) -> Result, String> { + let mut tokens = command.split_whitespace(); + + // fetch the identifier + let identifier = match tokens.next() { + Some(id) => id, + None => return Ok(None), + }; + + // parse the appropriate command + let command = match identifier { + "n" | "next" => Self::parse_next(tokens.by_ref())?, + "c" | "continue" => Self::Continue, + "b" | "back" => Self::parse_back(tokens.by_ref())?, + "r" | "rewind" => Self::RewindAll, + "p" | "print" => Self::parse_print(tokens.by_ref())?, + "l" | "clock" => Self::Clock, + "h" | "?" | "help" => Self::Help, + "q" | "quit" => Self::Quit, _ => { - Err(format!("malformed command - does not match any known command: `{}`", command)) + return Err(format!( + "malformed command - does not match any known command: `{}`", + command + )) } + }; + + // command is fully parsed and shouldn't contain further tokens + if let Some(t) = tokens.next() { + return Err(format!("malformed command - unexpected token `{t}`")); } + + Ok(Some(command)) } // HELPERS // -------------------------------------------------------------------------------------------- - /// parse play command - !play.num_cycles - fn parse_play(command: &str) -> Result { - // parse number of cycles - let num_cycles = command[6..].parse::().map_err(|err| { - format!("malformed command - failed to parse number of cycles: `{}` {}", command, err) - })?; - - Ok(Self::Play(num_cycles)) + /// parse next command - next num_cycles + fn parse_next<'a, I>(mut tokens: I) -> Result + where + I: Iterator, + { + let num_cycles = match tokens.next() { + Some(n) => n.parse::().map_err(|err| { + format!( + "malformed `next` command - failed to parse number of cycles: `{}` {}", + n, err + ) + })?, + None => return Ok(Self::Next(1)), + }; + Ok(Self::Next(num_cycles)) } - /// parse rewind command - !rewind.num_cycles - fn parse_rewind(command: &str) -> Result { - // parse number of cycles - let num_cycles = command[8..].parse::().map_err(|err| { - format!("malformed command - failed to parse number of cycles: `{}` {}", command, err) - })?; - + /// parse back command - back num_cycles + fn parse_back<'a, I>(mut tokens: I) -> Result + where + I: Iterator, + { + let num_cycles = match tokens.next() { + Some(n) => n.parse::().map_err(|err| { + format!( + "malformed `back` command - failed to parse number of cycles: `{}` {}", + n, err + ) + })?, + None => return Ok(Self::Rewind(1)), + }; Ok(Self::Rewind(num_cycles)) } - /// parse print memory command - !mem[address] - fn parse_print_memory(command: &str) -> Result { - // parse address - let address = command[5..command.len() - 1].parse::().map_err(|err| { - format!("malformed command - failed to parse address parameter: `{}` {}", command, err) - })?; + /// parse print command - p [m|s] [addr] + fn parse_print<'a, I>(mut tokens: I) -> Result + where + I: Iterator, + { + let command = match tokens.next() { + Some(c) => c, + None => return Ok(Self::PrintState), + }; - Ok(Self::PrintMemAddress(address)) - } + // match the command variant + let command = match command { + "m" | "mem" => Self::PrintMem, + "s" | "stack" => Self::PrintStack, + _ => { + return Err(format!( + "malformed `print` command - unexpected subcommand: `{command}`" + )) + } + }; - /// parse print stack command - !stack[index] - fn parse_print_stack(command: &str) -> Result { - // parse stack index - let index = command[7..command.len() - 1].parse::().map_err(|err| { - format!("malformed command - failed to parse stack index: `{}` {}", command, err) - })?; + // parse the subcommand argument, if present + let argument = + tokens.next().map(|t| t.parse::()).transpose().map_err(|err| { + format!("malformed command - failed to parse print argument: {err}") + })?; - Ok(Self::PrintStackItem(index)) + match (command, argument) { + (Self::PrintMem, Some(arg)) => Ok(Self::PrintMemAddress(arg)), + (Self::PrintStack, Some(arg)) => Ok(Self::PrintStackItem(arg as usize)), + (_, Some(_)) => unreachable!("the command was previously parsed within this block"), + (_, None) => Ok(command), + } } } diff --git a/miden/src/cli/debug/executor.rs b/miden/src/cli/debug/executor.rs index 26dc91a170..b081aff807 100644 --- a/miden/src/cli/debug/executor.rs +++ b/miden/src/cli/debug/executor.rs @@ -42,13 +42,13 @@ impl DebugExecutor { /// executes a debug command against the vm in it's current state. pub fn execute(&mut self, command: DebugCommand) -> bool { match command { - DebugCommand::PlayAll => { + DebugCommand::Continue => { while let Some(new_vm_state) = self.next_vm_state() { self.vm_state = new_vm_state; } self.print_vm_state(); } - DebugCommand::Play(cycles) => { + DebugCommand::Next(cycles) => { for _cycle in 0..cycles { match self.next_vm_state() { Some(next_vm_state) => { @@ -180,23 +180,36 @@ impl DebugExecutor { /// print help message fn print_help() { - let message = "---------------------------------------------------------\n\ + let message = "---------------------------------------------------------------------\n\ Miden Assembly Debug CLI\n\ - ---------------------------------------------------------\n\ - !next steps to the next clock cycle\n\ - !play executes program until completion or failure\n\ - !play.n executes n clock cycles\n\ - !prev steps to the previous clock cycle\n\ - !rewind rewinds program until beginning\n\ - !rewind.n rewinds n clock cycles\n\ - !print displays the complete state of the virtual machine\n\ - !stack displays the complete state of the stack\n\ - !stack[i] displays the stack element at index i\n\ - !mem displays the complete state of memory\n\ - !mem[i] displays memory at address i\n\ - !clock displays the current clock cycle\n\ - !quit quits the debugger\n\ - !help displays this message"; + ---------------------------------------------------------------------\n\ + next moves to the next clock cycle\n\ + next moves `c` clock cycles forward\n\ + continue executes program until completion or failure\n\ + back rewinds `1` clock cycles\n\ + back rewinds `c` clock cycles\n\ + rewind rewinds program until beginning\n\ + print displays the complete state of the virtual machine\n\ + print mem displays the complete state of memory\n\ + print mem displays memory at address `i`\n\ + print stack displays the complete state of the stack\n\ + print stack displays the stack element at index `i`\n\ + clock displays the current clock cycle\n\ + quit quits the debugger\n\ + help displays this message\n\ + \n\ + The following mappings are also available:\n\ + n -> next\n\ + c -> continue\n\ + b -> back\n\ + r -> rewind\n\ + p -> print\n\ + m -> mem\n\ + s -> stack\n\ + l -> clock\n\ + q -> quit\n\ + h -> help\n\ + ? -> help"; println!("{}", message); } diff --git a/miden/src/cli/debug/mod.rs b/miden/src/cli/debug/mod.rs index ab70cd5090..cc538e98d5 100644 --- a/miden/src/cli/debug/mod.rs +++ b/miden/src/cli/debug/mod.rs @@ -1,5 +1,5 @@ use super::data::{InputFile, ProgramFile}; -use rustyline::{Config, EditMode, Editor}; +use rustyline::{error::ReadlineError, Config, EditMode, Editor}; use std::path::PathBuf; use structopt::StructOpt; @@ -56,18 +56,29 @@ impl DebugCmd { let mut rl = Editor::<()>::with_config(rl_config).expect("Readline couldn't be initialized"); + println!("Welcome! Enter `h` for help."); + loop { match rl.readline(">> ") { Ok(command) => match DebugCommand::parse(&command) { - Ok(command) => { + Ok(Some(command)) => { if !debug_executor.execute(command) { println!("Debugging complete"); break; } } - Err(err) => println!("{err}"), + Ok(None) => (), + Err(err) => eprintln!("{err}"), }, - Err(err) => println!("malformed command - failed to read user input: {}", err), + Err(ReadlineError::Interrupted) => { + // ctrl+c is a transparent interruption and should provide not feedback or + // action. + } + Err(ReadlineError::Eof) => { + eprintln!("CTRL-D"); + break; + } + Err(err) => eprintln!("malformed command - failed to read user input: {}", err), } } From b7941f3c93fbc0909572aa2b1b5d3a0587842dd2 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Thu, 2 Mar 2023 14:01:19 +0100 Subject: [PATCH 09/47] feat: added assert_eqw instruction --- .../src/assembler/instruction/field_ops.rs | 10 + assembly/src/assembler/instruction/mod.rs | 1 + assembly/src/parsers/context.rs | 1 + assembly/src/parsers/nodes.rs | 2 + assembly/src/parsers/serde/deserialization.rs | 1 + assembly/src/parsers/serde/mod.rs | 477 +++++++++--------- assembly/src/parsers/serde/serialization.rs | 1 + .../user_docs/assembly/field_operations.md | 1 + 8 files changed, 256 insertions(+), 238 deletions(-) diff --git a/assembly/src/assembler/instruction/field_ops.rs b/assembly/src/assembler/instruction/field_ops.rs index c3d1156e01..f72bcb167b 100644 --- a/assembly/src/assembler/instruction/field_ops.rs +++ b/assembly/src/assembler/instruction/field_ops.rs @@ -7,6 +7,16 @@ use crate::MAX_EXP_BITS; /// Field element representing TWO in the base field of the VM. const TWO: Felt = Felt::new(2); +// ASSERTIONS +// ================================================================================================ + +/// Asserts that the top two words in the stack are equal. +/// +/// VM cycles: 11 cycles +pub fn assertw(span: &mut SpanBuilder) -> Result, AssemblyError> { + span.add_ops([MovUp4, Eq, Assert, MovUp3, Eq, Assert, MovUp2, Eq, Assert, Eq, Assert]) +} + // BASIC ARITHMETIC OPERATIONS // ================================================================================================ diff --git a/assembly/src/assembler/instruction/mod.rs b/assembly/src/assembler/instruction/mod.rs index b87e91939c..559ad71425 100644 --- a/assembly/src/assembler/instruction/mod.rs +++ b/assembly/src/assembler/instruction/mod.rs @@ -40,6 +40,7 @@ impl Assembler { let result = match instruction { Instruction::Assert => span.add_op(Assert), Instruction::AssertEq => span.add_ops([Eq, Assert]), + Instruction::AssertEqw => field_ops::assertw(span), Instruction::Assertz => span.add_ops([Eqz, Assert]), Instruction::Add => span.add_op(Add), diff --git a/assembly/src/parsers/context.rs b/assembly/src/parsers/context.rs index 1f5a9019be..eb3066ef9e 100644 --- a/assembly/src/parsers/context.rs +++ b/assembly/src/parsers/context.rs @@ -335,6 +335,7 @@ impl ParserContext { "assert" => simple_instruction(op, Assert), "assertz" => simple_instruction(op, Assertz), "assert_eq" => simple_instruction(op, AssertEq), + "assert_eqw" => simple_instruction(op, AssertEqw), "add" => field_ops::parse_add(op), "sub" => field_ops::parse_sub(op), diff --git a/assembly/src/parsers/nodes.rs b/assembly/src/parsers/nodes.rs index f3e302f6a4..64ad76a371 100644 --- a/assembly/src/parsers/nodes.rs +++ b/assembly/src/parsers/nodes.rs @@ -20,6 +20,7 @@ pub enum Node { pub enum Instruction { Assert, AssertEq, + AssertEqw, Assertz, Add, AddImm(Felt), @@ -282,6 +283,7 @@ impl fmt::Display for Instruction { match self { Self::Assert => write!(f, "assert"), Self::AssertEq => write!(f, "assert_eq"), + Self::AssertEqw => write!(f, "assert_eqw"), Self::Assertz => write!(f, "assertz"), Self::Add => write!(f, "add"), Self::AddImm(value) => write!(f, "add.{value}"), diff --git a/assembly/src/parsers/serde/deserialization.rs b/assembly/src/parsers/serde/deserialization.rs index 59a7ab8fed..02630763d0 100644 --- a/assembly/src/parsers/serde/deserialization.rs +++ b/assembly/src/parsers/serde/deserialization.rs @@ -38,6 +38,7 @@ impl Deserializable for Instruction { match opcode { OpCode::Assert => Ok(Instruction::Assert), OpCode::AssertEq => Ok(Instruction::AssertEq), + OpCode::AssertEqw => Ok(Instruction::AssertEqw), OpCode::Assertz => Ok(Instruction::Assertz), OpCode::Add => Ok(Instruction::Add), OpCode::AddImm => Ok(Instruction::AddImm(bytes.read_felt()?)), diff --git a/assembly/src/parsers/serde/mod.rs b/assembly/src/parsers/serde/mod.rs index 4b290efa01..bb387be984 100644 --- a/assembly/src/parsers/serde/mod.rs +++ b/assembly/src/parsers/serde/mod.rs @@ -16,261 +16,262 @@ mod serialization; pub enum OpCode { Assert = 0, AssertEq = 1, - Assertz = 2, - Add = 3, - AddImm = 4, - Sub = 5, - SubImm = 6, - Mul = 7, - MulImm = 8, - Div = 9, - DivImm = 10, - Neg = 11, - Inv = 12, - Incr = 13, - Pow2 = 14, - Exp = 15, - ExpImm = 16, - ExpBitLength = 17, - Not = 18, - And = 19, - Or = 20, - Xor = 21, - Eq = 22, - EqImm = 23, - Neq = 24, - NeqImm = 25, - Eqw = 26, - Lt = 27, - Lte = 28, - Gt = 29, - Gte = 30, + AssertEqw = 2, + Assertz = 3, + Add = 4, + AddImm = 5, + Sub = 6, + SubImm = 7, + Mul = 8, + MulImm = 9, + Div = 10, + DivImm = 11, + Neg = 12, + Inv = 13, + Incr = 14, + Pow2 = 15, + Exp = 16, + ExpImm = 17, + ExpBitLength = 18, + Not = 19, + And = 20, + Or = 21, + Xor = 22, + Eq = 23, + EqImm = 24, + Neq = 25, + NeqImm = 26, + Eqw = 27, + Lt = 28, + Lte = 29, + Gt = 30, + Gte = 31, // ----- ext2 operations ---------------------------------------------------------------------- - Ext2Add = 31, - Ext2Sub = 32, - Ext2Mul = 33, - Ext2Div = 34, - Ext2Neg = 35, - Ext2Inv = 36, + Ext2Add = 32, + Ext2Sub = 33, + Ext2Mul = 34, + Ext2Div = 35, + Ext2Neg = 36, + Ext2Inv = 37, // ----- u32 manipulation --------------------------------------------------------------------- - U32Test = 37, - U32TestW = 38, - U32Assert = 39, - U32Assert2 = 40, - U32AssertW = 41, - U32Split = 42, - U32Cast = 43, - U32CheckedAdd = 44, - U32CheckedAddImm = 45, - U32WrappingAdd = 46, - U32WrappingAddImm = 47, - U32OverflowingAdd = 48, - U32OverflowingAddImm = 49, - U32OverflowingAdd3 = 50, - U32WrappingAdd3 = 51, - U32CheckedSub = 52, - U32CheckedSubImm = 53, - U32WrappingSub = 54, - U32WrappingSubImm = 55, - U32OverflowingSub = 56, - U32OverflowingSubImm = 57, - U32CheckedMul = 58, - U32CheckedMulImm = 59, - U32WrappingMul = 60, - U32WrappingMulImm = 61, - U32OverflowingMul = 62, - U32OverflowingMulImm = 63, - U32OverflowingMadd = 64, - U32WrappingMadd = 65, - U32CheckedDiv = 66, - U32CheckedDivImm = 67, - U32UncheckedDiv = 68, - U32UncheckedDivImm = 69, - U32CheckedMod = 70, - U32CheckedModImm = 71, - U32UncheckedMod = 72, - U32UncheckedModImm = 73, - U32CheckedDivMod = 74, - U32CheckedDivModImm = 75, - U32UncheckedDivMod = 76, - U32UncheckedDivModImm = 77, - U32CheckedAnd = 78, - U32CheckedOr = 79, - U32CheckedXor = 80, - U32CheckedNot = 81, - U32CheckedShr = 82, - U32CheckedShrImm = 83, - U32UncheckedShr = 84, - U32UncheckedShrImm = 85, - U32CheckedShl = 86, - U32CheckedShlImm = 87, - U32UncheckedShl = 88, - U32UncheckedShlImm = 89, - U32CheckedRotr = 90, - U32CheckedRotrImm = 91, - U32UncheckedRotr = 92, - U32UncheckedRotrImm = 93, - U32CheckedRotl = 94, - U32CheckedRotlImm = 95, - U32UncheckedRotl = 96, - U32UncheckedRotlImm = 97, - U32CheckedPopcnt = 98, - U32UncheckedPopcnt = 99, - U32CheckedEq = 100, - U32CheckedEqImm = 101, - U32CheckedNeq = 102, - U32CheckedNeqImm = 103, - U32CheckedLt = 104, - U32UncheckedLt = 105, - U32CheckedLte = 106, - U32UncheckedLte = 107, - U32CheckedGt = 108, - U32UncheckedGt = 109, - U32CheckedGte = 110, - U32UncheckedGte = 111, - U32CheckedMin = 112, - U32UncheckedMin = 113, - U32CheckedMax = 114, - U32UncheckedMax = 115, + U32Test = 38, + U32TestW = 39, + U32Assert = 40, + U32Assert2 = 41, + U32AssertW = 42, + U32Split = 43, + U32Cast = 44, + U32CheckedAdd = 45, + U32CheckedAddImm = 46, + U32WrappingAdd = 47, + U32WrappingAddImm = 48, + U32OverflowingAdd = 49, + U32OverflowingAddImm = 50, + U32OverflowingAdd3 = 51, + U32WrappingAdd3 = 52, + U32CheckedSub = 53, + U32CheckedSubImm = 54, + U32WrappingSub = 55, + U32WrappingSubImm = 56, + U32OverflowingSub = 57, + U32OverflowingSubImm = 58, + U32CheckedMul = 59, + U32CheckedMulImm = 60, + U32WrappingMul = 61, + U32WrappingMulImm = 62, + U32OverflowingMul = 63, + U32OverflowingMulImm = 64, + U32OverflowingMadd = 65, + U32WrappingMadd = 66, + U32CheckedDiv = 67, + U32CheckedDivImm = 68, + U32UncheckedDiv = 69, + U32UncheckedDivImm = 70, + U32CheckedMod = 71, + U32CheckedModImm = 72, + U32UncheckedMod = 73, + U32UncheckedModImm = 74, + U32CheckedDivMod = 75, + U32CheckedDivModImm = 76, + U32UncheckedDivMod = 77, + U32UncheckedDivModImm = 78, + U32CheckedAnd = 79, + U32CheckedOr = 80, + U32CheckedXor = 81, + U32CheckedNot = 82, + U32CheckedShr = 83, + U32CheckedShrImm = 84, + U32UncheckedShr = 85, + U32UncheckedShrImm = 86, + U32CheckedShl = 87, + U32CheckedShlImm = 88, + U32UncheckedShl = 89, + U32UncheckedShlImm = 90, + U32CheckedRotr = 91, + U32CheckedRotrImm = 92, + U32UncheckedRotr = 93, + U32UncheckedRotrImm = 94, + U32CheckedRotl = 95, + U32CheckedRotlImm = 96, + U32UncheckedRotl = 97, + U32UncheckedRotlImm = 98, + U32CheckedPopcnt = 99, + U32UncheckedPopcnt = 100, + U32CheckedEq = 101, + U32CheckedEqImm = 102, + U32CheckedNeq = 103, + U32CheckedNeqImm = 104, + U32CheckedLt = 105, + U32UncheckedLt = 106, + U32CheckedLte = 107, + U32UncheckedLte = 108, + U32CheckedGt = 109, + U32UncheckedGt = 110, + U32CheckedGte = 111, + U32UncheckedGte = 112, + U32CheckedMin = 113, + U32UncheckedMin = 114, + U32CheckedMax = 115, + U32UncheckedMax = 116, // ----- stack manipulation ------------------------------------------------------------------- - Drop = 116, - DropW = 117, - PadW = 118, - Dup0 = 119, - Dup1 = 120, - Dup2 = 121, - Dup3 = 122, - Dup4 = 123, - Dup5 = 124, - Dup6 = 125, - Dup7 = 126, - Dup8 = 127, - Dup9 = 128, - Dup10 = 129, - Dup11 = 130, - Dup12 = 131, - Dup13 = 132, - Dup14 = 133, - Dup15 = 134, - DupW0 = 135, - DupW1 = 136, - DupW2 = 137, - DupW3 = 138, - Swap1 = 139, - Swap2 = 140, - Swap3 = 141, - Swap4 = 142, - Swap5 = 143, - Swap6 = 144, - Swap7 = 145, - Swap8 = 146, - Swap9 = 147, - Swap10 = 148, - Swap11 = 149, - Swap12 = 150, - Swap13 = 151, - Swap14 = 152, - Swap15 = 153, - SwapW1 = 154, - SwapW2 = 155, - SwapW3 = 156, - SwapDW = 157, - MovUp2 = 158, - MovUp3 = 159, - MovUp4 = 160, - MovUp5 = 161, - MovUp6 = 162, - MovUp7 = 163, - MovUp8 = 164, - MovUp9 = 165, - MovUp10 = 166, - MovUp11 = 167, - MovUp12 = 168, - MovUp13 = 169, - MovUp14 = 170, - MovUp15 = 171, - MovUpW2 = 172, - MovUpW3 = 173, - MovDn2 = 174, - MovDn3 = 175, - MovDn4 = 176, - MovDn5 = 177, - MovDn6 = 178, - MovDn7 = 179, - MovDn8 = 180, - MovDn9 = 181, - MovDn10 = 182, - MovDn11 = 183, - MovDn12 = 184, - MovDn13 = 185, - MovDn14 = 186, - MovDn15 = 187, - MovDnW2 = 188, - MovDnW3 = 189, - CSwap = 190, - CSwapW = 191, - CDrop = 192, - CDropW = 193, + Drop = 117, + DropW = 118, + PadW = 119, + Dup0 = 120, + Dup1 = 121, + Dup2 = 122, + Dup3 = 123, + Dup4 = 124, + Dup5 = 125, + Dup6 = 126, + Dup7 = 127, + Dup8 = 128, + Dup9 = 129, + Dup10 = 130, + Dup11 = 131, + Dup12 = 132, + Dup13 = 133, + Dup14 = 134, + Dup15 = 135, + DupW0 = 136, + DupW1 = 137, + DupW2 = 138, + DupW3 = 139, + Swap1 = 140, + Swap2 = 141, + Swap3 = 142, + Swap4 = 143, + Swap5 = 144, + Swap6 = 145, + Swap7 = 146, + Swap8 = 147, + Swap9 = 148, + Swap10 = 149, + Swap11 = 150, + Swap12 = 151, + Swap13 = 152, + Swap14 = 153, + Swap15 = 154, + SwapW1 = 155, + SwapW2 = 156, + SwapW3 = 157, + SwapDW = 158, + MovUp2 = 159, + MovUp3 = 160, + MovUp4 = 161, + MovUp5 = 162, + MovUp6 = 163, + MovUp7 = 164, + MovUp8 = 165, + MovUp9 = 166, + MovUp10 = 167, + MovUp11 = 168, + MovUp12 = 169, + MovUp13 = 170, + MovUp14 = 171, + MovUp15 = 172, + MovUpW2 = 173, + MovUpW3 = 174, + MovDn2 = 175, + MovDn3 = 176, + MovDn4 = 177, + MovDn5 = 178, + MovDn6 = 179, + MovDn7 = 180, + MovDn8 = 181, + MovDn9 = 182, + MovDn10 = 183, + MovDn11 = 184, + MovDn12 = 185, + MovDn13 = 186, + MovDn14 = 187, + MovDn15 = 188, + MovDnW2 = 189, + MovDnW3 = 190, + CSwap = 191, + CSwapW = 192, + CDrop = 193, + CDropW = 194, // ----- input / output operations ------------------------------------------------------------ - PushU8 = 194, - PushU16 = 195, - PushU32 = 196, - PushFelt = 197, - PushWord = 198, - PushU8List = 199, - PushU16List = 200, - PushU32List = 201, - PushFeltList = 202, + PushU8 = 195, + PushU16 = 196, + PushU32 = 197, + PushFelt = 198, + PushWord = 199, + PushU8List = 200, + PushU16List = 201, + PushU32List = 202, + PushFeltList = 203, - Locaddr = 203, - Sdepth = 204, - Caller = 205, - Clk = 206, + Locaddr = 204, + Sdepth = 205, + Caller = 206, + Clk = 207, - MemLoad = 207, - MemLoadImm = 208, - MemLoadW = 209, - MemLoadWImm = 210, - LocLoad = 211, - LocLoadW = 212, - MemStore = 213, - MemStoreImm = 214, - LocStore = 215, - MemStoreW = 216, - MemStoreWImm = 217, - LocStoreW = 218, + MemLoad = 208, + MemLoadImm = 209, + MemLoadW = 210, + MemLoadWImm = 211, + LocLoad = 212, + LocLoadW = 213, + MemStore = 214, + MemStoreImm = 215, + LocStore = 216, + MemStoreW = 217, + MemStoreWImm = 218, + LocStoreW = 219, - MemStream = 219, - AdvPipe = 220, + MemStream = 220, + AdvPipe = 221, - AdvPush = 221, - AdvLoadW = 222, + AdvPush = 222, + AdvLoadW = 223, - AdvU64Div = 223, - AdvKeyval = 224, - AdvMem = 225, - AdvExt2Inv = 226, - AdvExt2INTT = 227, + AdvU64Div = 224, + AdvKeyval = 225, + AdvMem = 226, + AdvExt2Inv = 227, + AdvExt2INTT = 228, // ----- cryptographic operations ------------------------------------------------------------- - Hash = 228, - HMerge = 229, - HPerm = 230, - MTreeGet = 231, - MTreeSet = 232, - MTreeCwm = 233, - FriExt2Fold4 = 234, + Hash = 229, + HMerge = 230, + HPerm = 231, + MTreeGet = 232, + MTreeSet = 233, + MTreeCwm = 234, + FriExt2Fold4 = 235, // ----- exec / call -------------------------------------------------------------------------- - ExecLocal = 235, - ExecImported = 236, - CallLocal = 237, - CallImported = 238, - SysCall = 239, + ExecLocal = 236, + ExecImported = 237, + CallLocal = 238, + CallImported = 239, + SysCall = 240, // ----- control flow ------------------------------------------------------------------------- IfElse = 253, diff --git a/assembly/src/parsers/serde/serialization.rs b/assembly/src/parsers/serde/serialization.rs index b2de8a8835..4118af984d 100644 --- a/assembly/src/parsers/serde/serialization.rs +++ b/assembly/src/parsers/serde/serialization.rs @@ -33,6 +33,7 @@ impl Serializable for Instruction { match self { Self::Assert => OpCode::Assert.write_into(target)?, Self::AssertEq => OpCode::AssertEq.write_into(target)?, + Self::AssertEqw => OpCode::AssertEqw.write_into(target)?, Self::Assertz => OpCode::Assertz.write_into(target)?, Self::Add => OpCode::Add.write_into(target)?, Self::AddImm(v) => { diff --git a/docs/src/user_docs/assembly/field_operations.md b/docs/src/user_docs/assembly/field_operations.md index 569a5e8cb8..7c5e0a2807 100644 --- a/docs/src/user_docs/assembly/field_operations.md +++ b/docs/src/user_docs/assembly/field_operations.md @@ -12,6 +12,7 @@ For instructions where one or more operands can be provided as immediate paramet | assert
- *(1 cycle)* | [a, ...] | [...] | If $a = 1$, removes it from the stack.
Fails if $a \ne 1$ | | assertz
- *(2 cycles)* | [ a, ...] | [...] | if $a = 0$, removes it from the stack,
Fails if $a \ne 0$ | | assert_eq
- *(2 cycles)* | [b, a, ...] | [...] | If $a = b$, removes them from the stack.
Fails if $a \ne b$ | +| assert_eqw
- *(11 cycles)* | [B, A, ...] | [...] | If $A = B$, removes them from the stack.
Fails if $A \ne B$ | ### Arithmetic and Boolean operations From f867febd70434f96fb2e97db886392fd8d06e096 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Thu, 2 Mar 2023 22:23:57 +0100 Subject: [PATCH 10/47] docs: Added a note about unrolled of repeat blocks --- docs/src/user_docs/assembly/flow_control.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/user_docs/assembly/flow_control.md b/docs/src/user_docs/assembly/flow_control.md index 2359ee5ba2..8d68bf2ae6 100644 --- a/docs/src/user_docs/assembly/flow_control.md +++ b/docs/src/user_docs/assembly/flow_control.md @@ -35,6 +35,8 @@ where: * `instructions` can be a sequence of any instructions, including nested control structures. * `count` is the number of times the `instructions` sequence should be repeated (e.g. `repeat.10`). `count` must be an integer greater than $0$. +> **Note**: During compilation the `repeat.` blocks are unrolled and expanded into `` copies of its inner block, there is no additional cost for counting variables in this case. + ### Condition-controlled loops Executing a sequence of instructions zero or more times based on some condition can be accomplished with *while loop* expressions. These expressions look like so: ``` From 25bd98e241c5b928f424f21c6efb50708c3dafcf Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Thu, 2 Mar 2023 22:49:14 +0100 Subject: [PATCH 11/47] feat: added is_odd instruction --- .../src/assembler/instruction/field_ops.rs | 7 + assembly/src/assembler/instruction/mod.rs | 1 + assembly/src/parsers/context.rs | 1 + assembly/src/parsers/nodes.rs | 2 + assembly/src/parsers/serde/deserialization.rs | 1 + assembly/src/parsers/serde/mod.rs | 419 +++++++++--------- assembly/src/parsers/serde/serialization.rs | 1 + .../user_docs/assembly/field_operations.md | 1 + 8 files changed, 224 insertions(+), 209 deletions(-) diff --git a/assembly/src/assembler/instruction/field_ops.rs b/assembly/src/assembler/instruction/field_ops.rs index f72bcb167b..98b239a729 100644 --- a/assembly/src/assembler/instruction/field_ops.rs +++ b/assembly/src/assembler/instruction/field_ops.rs @@ -367,6 +367,13 @@ pub fn gte(span: &mut SpanBuilder) -> Result, AssemblyError> { Ok(None) } +/// Checks if the top element in the stack is an odd number or not. +/// +/// Vm cycles: 5 +pub fn is_odd(span: &mut SpanBuilder) -> Result, AssemblyError> { + span.add_ops([U32split, Drop, Pad, Incr, U32and]) +} + // COMPARISON OPERATION HELPER FUNCTIONS // ================================================================================================ diff --git a/assembly/src/assembler/instruction/mod.rs b/assembly/src/assembler/instruction/mod.rs index 559ad71425..25eadb7b64 100644 --- a/assembly/src/assembler/instruction/mod.rs +++ b/assembly/src/assembler/instruction/mod.rs @@ -74,6 +74,7 @@ impl Assembler { Instruction::Lte => field_ops::lte(span), Instruction::Gt => field_ops::gt(span), Instruction::Gte => field_ops::gte(span), + Instruction::IsOdd => field_ops::is_odd(span), // ----- ext2 instructions ------------------------------------------------------------ Instruction::Ext2Add => ext2_ops::ext2_add(span), diff --git a/assembly/src/parsers/context.rs b/assembly/src/parsers/context.rs index eb3066ef9e..5e8b5a2d99 100644 --- a/assembly/src/parsers/context.rs +++ b/assembly/src/parsers/context.rs @@ -358,6 +358,7 @@ impl ParserContext { "lte" => simple_instruction(op, Lte), "gt" => simple_instruction(op, Gt), "gte" => simple_instruction(op, Gte), + "is_odd" => simple_instruction(op, IsOdd), "eqw" => simple_instruction(op, Eqw), // ----- ext2 operations ----------------------------------------------------- diff --git a/assembly/src/parsers/nodes.rs b/assembly/src/parsers/nodes.rs index 64ad76a371..9884e1d750 100644 --- a/assembly/src/parsers/nodes.rs +++ b/assembly/src/parsers/nodes.rs @@ -50,6 +50,7 @@ pub enum Instruction { Lte, Gt, Gte, + IsOdd, // ----- ext2 operations ---------------------------------------------------------------------- Ext2Add, @@ -313,6 +314,7 @@ impl fmt::Display for Instruction { Self::Lte => write!(f, "lte"), Self::Gt => write!(f, "gt"), Self::Gte => write!(f, "gte"), + Self::IsOdd => write!(f, "is_odd"), // ----- ext2 operations -------------------------------------------------------------- Self::Ext2Add => write!(f, "ext2add"), diff --git a/assembly/src/parsers/serde/deserialization.rs b/assembly/src/parsers/serde/deserialization.rs index 02630763d0..d75587974b 100644 --- a/assembly/src/parsers/serde/deserialization.rs +++ b/assembly/src/parsers/serde/deserialization.rs @@ -68,6 +68,7 @@ impl Deserializable for Instruction { OpCode::Lte => Ok(Instruction::Lte), OpCode::Gt => Ok(Instruction::Gt), OpCode::Gte => Ok(Instruction::Gte), + OpCode::IsOdd => Ok(Instruction::IsOdd), // ----- ext2 operations -------------------------------------------------------------- OpCode::Ext2Add => Ok(Instruction::Ext2Add), diff --git a/assembly/src/parsers/serde/mod.rs b/assembly/src/parsers/serde/mod.rs index bb387be984..ea4d1a7db7 100644 --- a/assembly/src/parsers/serde/mod.rs +++ b/assembly/src/parsers/serde/mod.rs @@ -46,232 +46,233 @@ pub enum OpCode { Lte = 29, Gt = 30, Gte = 31, + IsOdd = 32, // ----- ext2 operations ---------------------------------------------------------------------- - Ext2Add = 32, - Ext2Sub = 33, - Ext2Mul = 34, - Ext2Div = 35, - Ext2Neg = 36, - Ext2Inv = 37, + Ext2Add = 33, + Ext2Sub = 34, + Ext2Mul = 35, + Ext2Div = 36, + Ext2Neg = 37, + Ext2Inv = 38, // ----- u32 manipulation --------------------------------------------------------------------- - U32Test = 38, - U32TestW = 39, - U32Assert = 40, - U32Assert2 = 41, - U32AssertW = 42, - U32Split = 43, - U32Cast = 44, - U32CheckedAdd = 45, - U32CheckedAddImm = 46, - U32WrappingAdd = 47, - U32WrappingAddImm = 48, - U32OverflowingAdd = 49, - U32OverflowingAddImm = 50, - U32OverflowingAdd3 = 51, - U32WrappingAdd3 = 52, - U32CheckedSub = 53, - U32CheckedSubImm = 54, - U32WrappingSub = 55, - U32WrappingSubImm = 56, - U32OverflowingSub = 57, - U32OverflowingSubImm = 58, - U32CheckedMul = 59, - U32CheckedMulImm = 60, - U32WrappingMul = 61, - U32WrappingMulImm = 62, - U32OverflowingMul = 63, - U32OverflowingMulImm = 64, - U32OverflowingMadd = 65, - U32WrappingMadd = 66, - U32CheckedDiv = 67, - U32CheckedDivImm = 68, - U32UncheckedDiv = 69, - U32UncheckedDivImm = 70, - U32CheckedMod = 71, - U32CheckedModImm = 72, - U32UncheckedMod = 73, - U32UncheckedModImm = 74, - U32CheckedDivMod = 75, - U32CheckedDivModImm = 76, - U32UncheckedDivMod = 77, - U32UncheckedDivModImm = 78, - U32CheckedAnd = 79, - U32CheckedOr = 80, - U32CheckedXor = 81, - U32CheckedNot = 82, - U32CheckedShr = 83, - U32CheckedShrImm = 84, - U32UncheckedShr = 85, - U32UncheckedShrImm = 86, - U32CheckedShl = 87, - U32CheckedShlImm = 88, - U32UncheckedShl = 89, - U32UncheckedShlImm = 90, - U32CheckedRotr = 91, - U32CheckedRotrImm = 92, - U32UncheckedRotr = 93, - U32UncheckedRotrImm = 94, - U32CheckedRotl = 95, - U32CheckedRotlImm = 96, - U32UncheckedRotl = 97, - U32UncheckedRotlImm = 98, - U32CheckedPopcnt = 99, - U32UncheckedPopcnt = 100, - U32CheckedEq = 101, - U32CheckedEqImm = 102, - U32CheckedNeq = 103, - U32CheckedNeqImm = 104, - U32CheckedLt = 105, - U32UncheckedLt = 106, - U32CheckedLte = 107, - U32UncheckedLte = 108, - U32CheckedGt = 109, - U32UncheckedGt = 110, - U32CheckedGte = 111, - U32UncheckedGte = 112, - U32CheckedMin = 113, - U32UncheckedMin = 114, - U32CheckedMax = 115, - U32UncheckedMax = 116, + U32Test = 39, + U32TestW = 40, + U32Assert = 41, + U32Assert2 = 42, + U32AssertW = 43, + U32Split = 44, + U32Cast = 45, + U32CheckedAdd = 46, + U32CheckedAddImm = 47, + U32WrappingAdd = 48, + U32WrappingAddImm = 49, + U32OverflowingAdd = 50, + U32OverflowingAddImm = 51, + U32OverflowingAdd3 = 52, + U32WrappingAdd3 = 53, + U32CheckedSub = 54, + U32CheckedSubImm = 55, + U32WrappingSub = 56, + U32WrappingSubImm = 57, + U32OverflowingSub = 58, + U32OverflowingSubImm = 59, + U32CheckedMul = 60, + U32CheckedMulImm = 61, + U32WrappingMul = 62, + U32WrappingMulImm = 63, + U32OverflowingMul = 64, + U32OverflowingMulImm = 65, + U32OverflowingMadd = 66, + U32WrappingMadd = 67, + U32CheckedDiv = 68, + U32CheckedDivImm = 69, + U32UncheckedDiv = 70, + U32UncheckedDivImm = 71, + U32CheckedMod = 72, + U32CheckedModImm = 73, + U32UncheckedMod = 74, + U32UncheckedModImm = 75, + U32CheckedDivMod = 76, + U32CheckedDivModImm = 77, + U32UncheckedDivMod = 78, + U32UncheckedDivModImm = 79, + U32CheckedAnd = 80, + U32CheckedOr = 81, + U32CheckedXor = 82, + U32CheckedNot = 83, + U32CheckedShr = 84, + U32CheckedShrImm = 85, + U32UncheckedShr = 86, + U32UncheckedShrImm = 87, + U32CheckedShl = 88, + U32CheckedShlImm = 89, + U32UncheckedShl = 90, + U32UncheckedShlImm = 91, + U32CheckedRotr = 92, + U32CheckedRotrImm = 93, + U32UncheckedRotr = 94, + U32UncheckedRotrImm = 95, + U32CheckedRotl = 96, + U32CheckedRotlImm = 97, + U32UncheckedRotl = 98, + U32UncheckedRotlImm = 99, + U32CheckedPopcnt = 100, + U32UncheckedPopcnt = 101, + U32CheckedEq = 102, + U32CheckedEqImm = 103, + U32CheckedNeq = 104, + U32CheckedNeqImm = 105, + U32CheckedLt = 106, + U32UncheckedLt = 107, + U32CheckedLte = 108, + U32UncheckedLte = 109, + U32CheckedGt = 110, + U32UncheckedGt = 111, + U32CheckedGte = 112, + U32UncheckedGte = 113, + U32CheckedMin = 114, + U32UncheckedMin = 115, + U32CheckedMax = 116, + U32UncheckedMax = 117, // ----- stack manipulation ------------------------------------------------------------------- - Drop = 117, - DropW = 118, - PadW = 119, - Dup0 = 120, - Dup1 = 121, - Dup2 = 122, - Dup3 = 123, - Dup4 = 124, - Dup5 = 125, - Dup6 = 126, - Dup7 = 127, - Dup8 = 128, - Dup9 = 129, - Dup10 = 130, - Dup11 = 131, - Dup12 = 132, - Dup13 = 133, - Dup14 = 134, - Dup15 = 135, - DupW0 = 136, - DupW1 = 137, - DupW2 = 138, - DupW3 = 139, - Swap1 = 140, - Swap2 = 141, - Swap3 = 142, - Swap4 = 143, - Swap5 = 144, - Swap6 = 145, - Swap7 = 146, - Swap8 = 147, - Swap9 = 148, - Swap10 = 149, - Swap11 = 150, - Swap12 = 151, - Swap13 = 152, - Swap14 = 153, - Swap15 = 154, - SwapW1 = 155, - SwapW2 = 156, - SwapW3 = 157, - SwapDW = 158, - MovUp2 = 159, - MovUp3 = 160, - MovUp4 = 161, - MovUp5 = 162, - MovUp6 = 163, - MovUp7 = 164, - MovUp8 = 165, - MovUp9 = 166, - MovUp10 = 167, - MovUp11 = 168, - MovUp12 = 169, - MovUp13 = 170, - MovUp14 = 171, - MovUp15 = 172, - MovUpW2 = 173, - MovUpW3 = 174, - MovDn2 = 175, - MovDn3 = 176, - MovDn4 = 177, - MovDn5 = 178, - MovDn6 = 179, - MovDn7 = 180, - MovDn8 = 181, - MovDn9 = 182, - MovDn10 = 183, - MovDn11 = 184, - MovDn12 = 185, - MovDn13 = 186, - MovDn14 = 187, - MovDn15 = 188, - MovDnW2 = 189, - MovDnW3 = 190, - CSwap = 191, - CSwapW = 192, - CDrop = 193, - CDropW = 194, + Drop = 118, + DropW = 119, + PadW = 120, + Dup0 = 121, + Dup1 = 122, + Dup2 = 123, + Dup3 = 124, + Dup4 = 125, + Dup5 = 126, + Dup6 = 127, + Dup7 = 128, + Dup8 = 129, + Dup9 = 130, + Dup10 = 131, + Dup11 = 132, + Dup12 = 133, + Dup13 = 134, + Dup14 = 135, + Dup15 = 136, + DupW0 = 137, + DupW1 = 138, + DupW2 = 139, + DupW3 = 140, + Swap1 = 141, + Swap2 = 142, + Swap3 = 143, + Swap4 = 144, + Swap5 = 145, + Swap6 = 146, + Swap7 = 147, + Swap8 = 148, + Swap9 = 149, + Swap10 = 150, + Swap11 = 151, + Swap12 = 152, + Swap13 = 153, + Swap14 = 154, + Swap15 = 155, + SwapW1 = 156, + SwapW2 = 157, + SwapW3 = 158, + SwapDW = 159, + MovUp2 = 160, + MovUp3 = 161, + MovUp4 = 162, + MovUp5 = 163, + MovUp6 = 164, + MovUp7 = 165, + MovUp8 = 166, + MovUp9 = 167, + MovUp10 = 168, + MovUp11 = 169, + MovUp12 = 170, + MovUp13 = 171, + MovUp14 = 172, + MovUp15 = 173, + MovUpW2 = 174, + MovUpW3 = 175, + MovDn2 = 176, + MovDn3 = 177, + MovDn4 = 178, + MovDn5 = 179, + MovDn6 = 180, + MovDn7 = 181, + MovDn8 = 182, + MovDn9 = 183, + MovDn10 = 184, + MovDn11 = 185, + MovDn12 = 186, + MovDn13 = 187, + MovDn14 = 188, + MovDn15 = 189, + MovDnW2 = 190, + MovDnW3 = 191, + CSwap = 192, + CSwapW = 193, + CDrop = 194, + CDropW = 195, // ----- input / output operations ------------------------------------------------------------ - PushU8 = 195, - PushU16 = 196, - PushU32 = 197, - PushFelt = 198, - PushWord = 199, - PushU8List = 200, - PushU16List = 201, - PushU32List = 202, - PushFeltList = 203, + PushU8 = 196, + PushU16 = 197, + PushU32 = 198, + PushFelt = 199, + PushWord = 200, + PushU8List = 201, + PushU16List = 202, + PushU32List = 203, + PushFeltList = 204, - Locaddr = 204, - Sdepth = 205, - Caller = 206, - Clk = 207, + Locaddr = 205, + Sdepth = 206, + Caller = 207, + Clk = 208, - MemLoad = 208, - MemLoadImm = 209, - MemLoadW = 210, - MemLoadWImm = 211, - LocLoad = 212, - LocLoadW = 213, - MemStore = 214, - MemStoreImm = 215, - LocStore = 216, - MemStoreW = 217, - MemStoreWImm = 218, - LocStoreW = 219, + MemLoad = 209, + MemLoadImm = 210, + MemLoadW = 211, + MemLoadWImm = 212, + LocLoad = 213, + LocLoadW = 214, + MemStore = 215, + MemStoreImm = 216, + LocStore = 217, + MemStoreW = 218, + MemStoreWImm = 219, + LocStoreW = 220, - MemStream = 220, - AdvPipe = 221, + MemStream = 221, + AdvPipe = 222, - AdvPush = 222, - AdvLoadW = 223, + AdvPush = 223, + AdvLoadW = 224, - AdvU64Div = 224, - AdvKeyval = 225, - AdvMem = 226, - AdvExt2Inv = 227, - AdvExt2INTT = 228, + AdvU64Div = 225, + AdvKeyval = 226, + AdvMem = 227, + AdvExt2Inv = 228, + AdvExt2INTT = 229, // ----- cryptographic operations ------------------------------------------------------------- - Hash = 229, - HMerge = 230, - HPerm = 231, - MTreeGet = 232, - MTreeSet = 233, - MTreeCwm = 234, - FriExt2Fold4 = 235, + Hash = 230, + HMerge = 231, + HPerm = 232, + MTreeGet = 233, + MTreeSet = 234, + MTreeCwm = 235, + FriExt2Fold4 = 236, // ----- exec / call -------------------------------------------------------------------------- - ExecLocal = 236, - ExecImported = 237, - CallLocal = 238, - CallImported = 239, - SysCall = 240, + ExecLocal = 237, + ExecImported = 238, + CallLocal = 239, + CallImported = 240, + SysCall = 241, // ----- control flow ------------------------------------------------------------------------- IfElse = 253, diff --git a/assembly/src/parsers/serde/serialization.rs b/assembly/src/parsers/serde/serialization.rs index 4118af984d..3a6eaed5b2 100644 --- a/assembly/src/parsers/serde/serialization.rs +++ b/assembly/src/parsers/serde/serialization.rs @@ -87,6 +87,7 @@ impl Serializable for Instruction { Self::Lte => OpCode::Lte.write_into(target)?, Self::Gt => OpCode::Gt.write_into(target)?, Self::Gte => OpCode::Gte.write_into(target)?, + Self::IsOdd => OpCode::IsOdd.write_into(target)?, // ----- ext2 operations -------------------------------------------------------------- Self::Ext2Add => OpCode::Ext2Add.write_into(target)?, diff --git a/docs/src/user_docs/assembly/field_operations.md b/docs/src/user_docs/assembly/field_operations.md index 7c5e0a2807..db7cda821b 100644 --- a/docs/src/user_docs/assembly/field_operations.md +++ b/docs/src/user_docs/assembly/field_operations.md @@ -42,6 +42,7 @@ For instructions where one or more operands can be provided as immediate paramet | lte
- *(18 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a \le b \\ 0, & \text{otherwise}\ \end{cases}$ | | gt
- *(18 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a > b \\ 0, & \text{otherwise}\ \end{cases}$ | | gte
- *(19 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a \ge b \\ 0, & \text{otherwise}\ \end{cases}$ | +| is_odd
- *(5 cycles)* | [a, ...] | [b, ...] | Pushes $1$ to the stack if the number $a$ is an odd number $0$ otherwise | | eqw
- *(15 cycles)* | [A, B, ...] | [c, A, B, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a_i = b_i \; \forall i \in \{0, 1, 2, 3\} \\ 0, & \text{otherwise}\ \end{cases}$ | ### Extension Field Operations From cb0a00f0b53d343986b03b4dd7841450b028306e Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Thu, 2 Mar 2023 23:39:34 +0100 Subject: [PATCH 12/47] chore: fix parser expect comments --- assembly/src/parsers/context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assembly/src/parsers/context.rs b/assembly/src/parsers/context.rs index 5e8b5a2d99..ca5972709f 100644 --- a/assembly/src/parsers/context.rs +++ b/assembly/src/parsers/context.rs @@ -101,14 +101,14 @@ impl ParserContext { // consume the `end` token match tokens.read() { None => { - let token = tokens.read_at(while_start).expect("no if token"); + let token = tokens.read_at(while_start).expect("no while token"); Err(ParsingError::unmatched_while(token)) } Some(token) => match token.parts()[0] { Token::END => token.validate_end(), Token::ELSE => Err(ParsingError::dangling_else(token)), _ => { - let token = tokens.read_at(while_start).expect("no if token"); + let token = tokens.read_at(while_start).expect("no while token"); Err(ParsingError::unmatched_while(token)) } }, From a87b259a1e665eb0eec2c4c9f367827c0249fded Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Thu, 2 Mar 2023 23:41:59 +0100 Subject: [PATCH 13/47] docs: updated changelog with new instructions --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b62390b50..808b50260f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Next + +#### Assembly +- Added new instructions: `is_odd`, `assert_eqw`. + ## 0.4.0 (2023-02-27) #### Advice provider From 3f53fe0fc1981cbcde01bfc8441c3149305ea599 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Thu, 2 Mar 2023 22:01:10 +0100 Subject: [PATCH 14/47] chore: Added docs to Token --- assembly/src/tokens/mod.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/assembly/src/tokens/mod.rs b/assembly/src/tokens/mod.rs index cb86a778e0..2d3f4f01d2 100644 --- a/assembly/src/tokens/mod.rs +++ b/assembly/src/tokens/mod.rs @@ -8,29 +8,36 @@ pub use stream::TokenStream; // TOKEN // ================================================================================================ -/// TODO: add comments +/// Token type used to represent a token in the Miden assembly source. +/// +/// This struct is intended to be mutated in place by the tokenizer through the `update` method, +/// which updates the token position and splits the token into its composing parts. #[derive(Clone, Debug, Default)] pub struct Token<'a> { + /// The dot-separated parts of a token, e.g. `push.1` is split into `['push', '1']`. parts: Vec<&'a str>, + /// The token position in the token stream pos: usize, } impl<'a> Token<'a> { + // DEFINITION TOKENS + // -------------------------------------------------------------------------------------------- pub const BEGIN: &'static str = "begin"; + pub const CONST: &'static str = "const"; + pub const END: &'static str = "end"; + pub const EXPORT: &'static str = "export"; + pub const PROC: &'static str = "proc"; + pub const USE: &'static str = "use"; + + // CONTROL FLOW TOKENS + // -------------------------------------------------------------------------------------------- pub const CALL: &'static str = "call"; pub const ELSE: &'static str = "else"; - pub const END: &'static str = "end"; pub const EXEC: &'static str = "exec"; - pub const EXPORT: &'static str = "export"; pub const IF: &'static str = "if"; - pub const PROC: &'static str = "proc"; pub const REPEAT: &'static str = "repeat"; pub const SYSCALL: &'static str = "syscall"; - // CONTROL TOKENS - // -------------------------------------------------------------------------------------------- - - pub const CONST: &'static str = "const"; - pub const USE: &'static str = "use"; pub const WHILE: &'static str = "while"; // CONSTRUCTOR From d6102c65f34e1f6c7dfe0b0751574850cad4275f Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Fri, 3 Mar 2023 02:11:47 +0100 Subject: [PATCH 15/47] change: remove unecessary column from runtime It is not necessary to track the changes to the `row_addr` column during the runtime, it is sufficient to construct the column when the trace is being filled. This is because the value of the column can be inferred from the any of the other columns in the hasher. The advantage here is that the HasherTrace is slightly simpler, one fewer attribute. And the column can be allocated and filled with a single loop, which should be more efficient --- processor/src/chiplets/hasher/trace.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/processor/src/chiplets/hasher/trace.rs b/processor/src/chiplets/hasher/trace.rs index 1698d889c2..82dbbd78b9 100644 --- a/processor/src/chiplets/hasher/trace.rs +++ b/processor/src/chiplets/hasher/trace.rs @@ -14,7 +14,6 @@ use vm_core::chiplets::hasher::{apply_round, NUM_ROUNDS, NUM_SELECTORS}; #[derive(Default)] pub struct HasherTrace { selectors: [Vec; 3], - row_addr: Vec, hasher_state: [Vec; STATE_WIDTH], node_index: Vec, } @@ -25,7 +24,7 @@ impl HasherTrace { /// Returns current length of this execution trace. pub fn trace_len(&self) -> usize { - self.row_addr.len() + self.selectors[0].len() } /// Returns next row address. The address is equal to the current trace length + 1. @@ -93,7 +92,6 @@ impl HasherTrace { /// Appends a new row to the execution trace based on the supplied parameters. fn append_row(&mut self, selectors: Selectors, state: &HasherState, index: Felt) { - self.row_addr.push(self.next_row_addr()); for (trace_col, selector_val) in self.selectors.iter_mut().zip(selectors) { trace_col.push(selector_val); } @@ -136,11 +134,17 @@ impl HasherTrace { // make sure fragment dimensions are consistent with the dimensions of this trace debug_assert_eq!(self.trace_len(), trace.len(), "inconsistent trace lengths"); debug_assert_eq!(TRACE_WIDTH, trace.width(), "inconsistent trace widths"); + let size: u64 = self.trace_len() as u64; // collect all trace columns into a single vector let mut columns = Vec::new(); self.selectors.into_iter().for_each(|c| columns.push(c)); - columns.push(self.row_addr); + + // collects the row_addr column, this column is a strictly monotonically increasing column, + // starting at one and going up to the trace length. + let row_addr = (1..=size).map(Felt::new).collect(); + columns.push(row_addr); + self.hasher_state.into_iter().for_each(|c| columns.push(c)); columns.push(self.node_index); From ddd3aced0b0dc4c2076a32a8d9dc98bed36f1ebd Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Fri, 3 Mar 2023 14:34:39 +0100 Subject: [PATCH 16/47] feat: implement Deserializable for ProgramInfo Currently, `ProgramInfo` implements only `Serializable`. It is so to keep the coupling between winter-utils and miden minimal, as the serialization traits are defined in winterfell. However, this might create some challenges for consumers of miden-vm as they might have to deserialize the struct, such as when representing the type within other targets (i.e. WASM). This commit introduces Deserializable for Kernel and ProgramInfo. related issue: #731 --- core/Cargo.toml | 4 ++++ core/src/program/info.rs | 16 +++++++++++++++- core/src/program/mod.rs | 13 ++++++++++++- core/src/program/tests.rs | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 core/src/program/tests.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index f50e22bd71..b63f816f84 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -23,3 +23,7 @@ std = ["math/std", "winter-utils/std"] math = { package = "winter-math", version = "0.5", default-features = false } crypto = { package = "miden-crypto", version = "0.1", default-features = false } winter-utils = { package = "winter-utils", version = "0.5", default-features = false } + +[dev-dependencies] +proptest = "1.1.0" +rand_utils = { version = "0.5", package = "winter-rand-utils" } diff --git a/core/src/program/info.rs b/core/src/program/info.rs index 9c0b62390f..53d069d5a7 100644 --- a/core/src/program/info.rs +++ b/core/src/program/info.rs @@ -1,4 +1,7 @@ -use super::{ByteWriter, Digest, Kernel, Program, Serializable}; +use super::{ + ByteReader, ByteWriter, Deserializable, DeserializationError, Digest, Kernel, Program, + Serializable, +}; // PROGRAM INFO // ================================================================================================ @@ -66,3 +69,14 @@ impl Serializable for ProgramInfo { ::write_into(&self.kernel, target); } } + +impl Deserializable for ProgramInfo { + fn read_from(source: &mut R) -> Result { + let program_hash = source.read()?; + let kernel = source.read()?; + Ok(Self { + program_hash, + kernel, + }) + } +} diff --git a/core/src/program/mod.rs b/core/src/program/mod.rs index 2b9ba8029c..b9a06dee41 100644 --- a/core/src/program/mod.rs +++ b/core/src/program/mod.rs @@ -7,7 +7,7 @@ use super::{ Felt, FieldElement, Operation, }; use core::fmt; -use winter_utils::{ByteWriter, Serializable}; +use winter_utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; pub mod blocks; use blocks::CodeBlock; @@ -15,6 +15,9 @@ use blocks::CodeBlock; mod info; pub use info::ProgramInfo; +#[cfg(test)] +mod tests; + // PROGRAM // ================================================================================================ /// A program which can be executed by the VM. @@ -164,3 +167,11 @@ impl Serializable for Kernel { Digest::write_batch_into(&self.0, target) } } + +impl Deserializable for Kernel { + fn read_from(source: &mut R) -> Result { + let len = source.read_u16()?; + let kernel = (0..len).map(|_| source.read::()).collect::>()?; + Ok(Self(kernel)) + } +} diff --git a/core/src/program/tests.rs b/core/src/program/tests.rs new file mode 100644 index 0000000000..5e7efe4cfd --- /dev/null +++ b/core/src/program/tests.rs @@ -0,0 +1,39 @@ +use super::{Deserializable, Digest, Felt, Kernel, ProgramInfo, Serializable}; +use crate::Word; +use proptest::prelude::*; +use rand_utils::prng_array; + +proptest! { + #[test] + fn arbitrary_program_info_serialization_works( + kernel_count in prop::num::u8::ANY, + ref seed in any::<[u8; 32]>() + ) { + let program_hash = digest_from_seed(*seed); + let kernel: Vec = (0..kernel_count) + .scan(*seed, |seed, _| { + *seed = prng_array(*seed); + Some(digest_from_seed(*seed)) + }) + .collect(); + let kernel = Kernel::new(&kernel); + let program_info = ProgramInfo::new(program_hash, kernel); + let bytes = program_info.to_bytes(); + let deser = ProgramInfo::read_from_bytes(&bytes).unwrap(); + assert_eq!(program_info, deser); + } +} + +// HELPER FUNCTIONS +// -------------------------------------------------------------------------------------------- + +fn digest_from_seed(seed: [u8; 32]) -> Digest { + let mut digest = Word::default(); + digest.iter_mut().enumerate().for_each(|(i, d)| { + *d = <[u8; 8]>::try_from(&seed[i * 8..(i + 1) * 8]) + .map(u64::from_le_bytes) + .map(Felt::new) + .unwrap() + }); + digest.into() +} From ac5989f34f69e3d74e911757f84d4766a3439bf1 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Fri, 3 Mar 2023 16:43:48 +0100 Subject: [PATCH 17/47] change: simplify copy_trace The previous version converted column to row form, and then saved the row into the columns, an unecessary round trip. This just copies the data in the columns directly to its end using the `Vec::extend_within`. --- processor/src/chiplets/hasher/trace.rs | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/processor/src/chiplets/hasher/trace.rs b/processor/src/chiplets/hasher/trace.rs index 82dbbd78b9..42d8e36ce7 100644 --- a/processor/src/chiplets/hasher/trace.rs +++ b/processor/src/chiplets/hasher/trace.rs @@ -1,6 +1,6 @@ use super::{Felt, HasherState, Selectors, TraceFragment, Vec, STATE_WIDTH, TRACE_WIDTH, ZERO}; use core::ops::Range; -use vm_core::chiplets::hasher::{apply_round, NUM_ROUNDS, NUM_SELECTORS}; +use vm_core::chiplets::hasher::{apply_round, NUM_ROUNDS}; // HASHER TRACE // ================================================================================================ @@ -104,25 +104,19 @@ impl HasherTrace { /// Copies section of trace from the given range of start and end rows at the end of the trace. /// The hasher state of the last row is copied to the provided state input. pub fn copy_trace(&mut self, state: &mut [Felt; STATE_WIDTH], range: Range) { - let mut hasher_state: [Felt; STATE_WIDTH] = Default::default(); - let mut selectors: [Felt; NUM_SELECTORS] = Default::default(); - - for row in range { - for (col, selector) in selectors.iter_mut().enumerate() { - *selector = self.selectors[col][row]; - } - - for (col, state) in hasher_state.iter_mut().enumerate() { - *state = self.hasher_state[col][row]; - } + for selector in self.selectors.iter_mut() { + selector.extend_from_within(range.clone()); + } - let node_index = self.node_index[row]; - self.append_row(selectors, &hasher_state, node_index); + for hasher in self.hasher_state.iter_mut() { + hasher.extend_from_within(range.clone()); } + self.node_index.extend_from_within(range.clone()); + // copy the latest hasher state to the provided state slice - for (state_col, hasher_col) in state.iter_mut().zip(hasher_state.iter()) { - *state_col = *hasher_col + for (col, hasher) in self.hasher_state.iter().enumerate() { + state[col] = hasher[range.end - 1]; } } From 8f7ad92de2308bf557cc09b3fb2e440cdcc06ab5 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Fri, 3 Mar 2023 17:36:35 +0100 Subject: [PATCH 18/47] change: renamed processor_flag to hasher_flag --- air/src/chiplets/hasher/mod.rs | 45 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/air/src/chiplets/hasher/mod.rs b/air/src/chiplets/hasher/mod.rs index 3db0213fb3..67ad49efdd 100644 --- a/air/src/chiplets/hasher/mod.rs +++ b/air/src/chiplets/hasher/mod.rs @@ -107,33 +107,32 @@ pub fn get_transition_constraint_count() -> usize { NUM_CONSTRAINTS } -/// Enforces constraints for the hash chiplet. +/// Enforces constraints for the hasher chiplet. /// -/// - The `processor_flag` indicates whether the current row is in the section of the chiplets -/// module that contains this processor's trace. -/// - The `transition_flag` indicates whether or not the constraints should be enforced for this -/// transition. It is expected to be false when the next row will be the last row of this -/// processor's execution trace. +/// - The `hasher_flag` is the relevant selector flag for the hasher chiplet, it is set to `0` when +/// the hasher chiplet is in use +/// - The `transition_flag` indicates whether this is the last row this chiplet's execution trace, +/// and therefore the constraints should not be enforced. pub fn enforce_constraints>( frame: &EvaluationFrame, periodic_values: &[E], result: &mut [E], - processor_flag: E, + hasher_flag: E, transition_flag: E, ) { // Enforce that the row address increases by 1 at each step when the transition flag is set. result.agg_constraint( 0, - processor_flag * transition_flag, + hasher_flag * transition_flag, frame.row_next() - frame.row() - E::ONE, ); let mut index = 1; - index += enforce_selectors(frame, periodic_values, &mut result[index..], processor_flag); + index += enforce_selectors(frame, periodic_values, &mut result[index..], hasher_flag); - index += enforce_node_index(frame, periodic_values, &mut result[index..], processor_flag); + index += enforce_node_index(frame, periodic_values, &mut result[index..], hasher_flag); - enforce_hasher_state(frame, periodic_values, &mut result[index..], processor_flag); + enforce_hasher_state(frame, periodic_values, &mut result[index..], hasher_flag); } // TRANSITION CONSTRAINT HELPERS @@ -150,17 +149,17 @@ fn enforce_selectors( frame: &EvaluationFrame, periodic_values: &[E], result: &mut [E], - processor_flag: E, + hasher_flag: E, ) -> usize { // Ensure the selectors are all binary values. for (idx, result) in result.iter_mut().take(NUM_SELECTORS).enumerate() { - *result = processor_flag * is_binary(frame.s(idx)); + *result = hasher_flag * is_binary(frame.s(idx)); } let mut constraint_offset = NUM_SELECTORS; // Ensure the values in s1 and s2 in the current row are copied to the next row when f_out != 1 // and f_out' != 1. - let copy_selectors_flag = processor_flag + let copy_selectors_flag = hasher_flag * binary_not(frame.f_out(periodic_values)) * binary_not(frame.f_out_next(periodic_values)); result[constraint_offset] = copy_selectors_flag * (frame.s_next(1) - frame.s(1)); @@ -171,7 +170,7 @@ fn enforce_selectors( // s0 should be unconstrained except in the last row of the cycle if any of f_abp, f_mpa, f_mva, // or f_mua are 1, in which case the next value of s0 must be zero. - result[constraint_offset] = processor_flag + result[constraint_offset] = hasher_flag * periodic_values[0] * frame.s_next(0) * (frame.f_abp() + frame.f_mpa() + frame.f_mva() + frame.f_mua()); @@ -179,7 +178,7 @@ fn enforce_selectors( // Prevent an invalid combinations of flags. result[constraint_offset] = - processor_flag * periodic_values[0] * binary_not(frame.s(0)) * frame.s(1); + hasher_flag * periodic_values[0] * binary_not(frame.s(0)) * frame.s(1); constraint_offset += 1; constraint_offset @@ -197,22 +196,22 @@ fn enforce_node_index( frame: &EvaluationFrame, periodic_values: &[E], result: &mut [E], - processor_flag: E, + hasher_flag: E, ) -> usize { let mut constraint_offset = 0; // Enforce that the node index is 0 when a computation is finished. - result[constraint_offset] = processor_flag * frame.f_out(periodic_values) * frame.i(); + result[constraint_offset] = hasher_flag * frame.f_out(periodic_values) * frame.i(); constraint_offset += 1; // When a new node is being absorbed into the hasher state, ensure that the shift to the right // was performed correctly by enforcing that the discarded bit is a binary value. - result[constraint_offset] = processor_flag * frame.f_an(periodic_values) * is_binary(frame.b()); + result[constraint_offset] = hasher_flag * frame.f_an(periodic_values) * is_binary(frame.b()); constraint_offset += 1; // When we are not absorbing a new row and the computation is not finished, make sure the value // of i is copied to the next row. - result[constraint_offset] = processor_flag + result[constraint_offset] = hasher_flag * (E::ONE - frame.f_an(periodic_values) - frame.f_out(periodic_values)) * (frame.i_next() - frame.i()); constraint_offset += 1; @@ -233,13 +232,13 @@ fn enforce_hasher_state>( frame: &EvaluationFrame, periodic_values: &[E], result: &mut [E], - processor_flag: E, + hasher_flag: E, ) -> usize { let mut constraint_offset = 0; // Get the constraint flags and the RPO round constants from the periodic values. - let hash_flag = processor_flag * binary_not(periodic_values[0]); - let last_row = processor_flag * periodic_values[0]; + let hash_flag = hasher_flag * binary_not(periodic_values[0]); + let last_row = hasher_flag * periodic_values[0]; let ark = &periodic_values[NUM_PERIODIC_SELECTOR_COLUMNS..]; // Enforce the RPO round constraints. From 0d2df6ebd253ba4e75f0e3d2555d562ff709c080 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Fri, 3 Mar 2023 17:40:21 +0100 Subject: [PATCH 19/47] change: replace mentions of co-processor with chiplet --- air/src/chiplets/hasher/mod.rs | 4 ++-- air/src/chiplets/hasher/tests.rs | 2 +- air/src/chiplets/mod.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/air/src/chiplets/hasher/mod.rs b/air/src/chiplets/hasher/mod.rs index 67ad49efdd..4ea8a0b5d5 100644 --- a/air/src/chiplets/hasher/mod.rs +++ b/air/src/chiplets/hasher/mod.rs @@ -22,7 +22,7 @@ pub const NUM_CONSTRAINTS: usize = 31; /// The number of periodic columns which are used as selectors to specify a particular row or rows /// within the hash cycle. pub const NUM_PERIODIC_SELECTOR_COLUMNS: usize = 3; -/// The total number of periodic columns used by the hash processor, which is the sum of the number +/// The total number of periodic columns used by the hasher chiplet, which is the sum of the number /// of periodic selector columns plus the columns of round constants for the Rescue Prime Optimized /// hash permutation. pub const NUM_PERIODIC_COLUMNS: usize = STATE_WIDTH * 2 + NUM_PERIODIC_SELECTOR_COLUMNS; @@ -30,7 +30,7 @@ pub const NUM_PERIODIC_COLUMNS: usize = STATE_WIDTH * 2 + NUM_PERIODIC_SELECTOR_ // PERIODIC COLUMNS // ================================================================================================ -/// Returns the set of periodic columns required by the Hash processor. +/// Returns the set of periodic columns required by the hasher chiplet. /// /// The columns consist of: /// - k0 column, which has a repeating pattern of 7 zeros followed by a single one. diff --git a/air/src/chiplets/hasher/tests.rs b/air/src/chiplets/hasher/tests.rs index 66844ffa46..a7cb532119 100644 --- a/air/src/chiplets/hasher/tests.rs +++ b/air/src/chiplets/hasher/tests.rs @@ -31,7 +31,7 @@ fn hash_round() { // TEST HELPER FUNCTIONS // ================================================================================================ -/// Returns the result of Hash processor's constraint evaluations on the provided frame starting at +/// Returns the result of hasher chiplet's constraint evaluations on the provided frame starting at /// the specified row. fn get_constraint_evaluation( frame: EvaluationFrame, diff --git a/air/src/chiplets/mod.rs b/air/src/chiplets/mod.rs index 3b17590366..e8336f93f2 100644 --- a/air/src/chiplets/mod.rs +++ b/air/src/chiplets/mod.rs @@ -142,7 +142,7 @@ trait EvaluationFrameExt { /// index is a valid selector index. fn s_next(&self, idx: usize) -> E; - // --- Co-processor selector flags ------------------------------------------------------------ + // --- Chiplet selector flags ----------------------------------------------------------------- /// Flag to indicate whether the frame is in the hasher portion of the Chiplets trace. fn hasher_flag(&self) -> E; @@ -169,7 +169,7 @@ impl EvaluationFrameExt for &EvaluationFrame { self.next()[CHIPLETS_OFFSET + idx] } - // --- Co-processor selector flags ------------------------------------------------------------ + // --- Chiplet selector flags ----------------------------------------------------------------- #[inline(always)] fn hasher_flag(&self) -> E { From 00097b3f1901e06fdd5ce418f4ae798f3fb91b7a Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Fri, 3 Mar 2023 17:50:33 +0100 Subject: [PATCH 20/47] change: clarify which selectors are enforced --- air/src/chiplets/hasher/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/air/src/chiplets/hasher/mod.rs b/air/src/chiplets/hasher/mod.rs index 4ea8a0b5d5..673ca93742 100644 --- a/air/src/chiplets/hasher/mod.rs +++ b/air/src/chiplets/hasher/mod.rs @@ -128,7 +128,7 @@ pub fn enforce_constraints>( ); let mut index = 1; - index += enforce_selectors(frame, periodic_values, &mut result[index..], hasher_flag); + index += enforce_hasher_selectors(frame, periodic_values, &mut result[index..], hasher_flag); index += enforce_node_index(frame, periodic_values, &mut result[index..], hasher_flag); @@ -138,14 +138,14 @@ pub fn enforce_constraints>( // TRANSITION CONSTRAINT HELPERS // ================================================================================================ -/// Enforces that all selectors and selector transitions are valid. +/// Enforces validity of the internal selectors of the hasher chiplet. /// /// - All selectors must contain binary values. /// - s1 and s2 must be copied to the next row unless f_out is set in the current or next row. /// - When a cycle ends by absorbing more elements or a Merkle path node, ensure the next value of /// s0 is always zero. Otherwise, s0 should be unconstrained. /// - Prevent an invalid combination of flags where s_0 = 0 and s_1 = 1. -fn enforce_selectors( +fn enforce_hasher_selectors( frame: &EvaluationFrame, periodic_values: &[E], result: &mut [E], From e3cddf16b19d2044aafc32510932c91ffcf80e76 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Fri, 3 Mar 2023 17:53:23 +0100 Subject: [PATCH 21/47] fixup! change: renamed processor_flag to hasher_flag --- air/src/chiplets/hasher/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/air/src/chiplets/hasher/mod.rs b/air/src/chiplets/hasher/mod.rs index 673ca93742..f84401f77c 100644 --- a/air/src/chiplets/hasher/mod.rs +++ b/air/src/chiplets/hasher/mod.rs @@ -109,8 +109,8 @@ pub fn get_transition_constraint_count() -> usize { /// Enforces constraints for the hasher chiplet. /// -/// - The `hasher_flag` is the relevant selector flag for the hasher chiplet, it is set to `0` when -/// the hasher chiplet is in use +/// - The `hasher_flag` determines if the hasher chiplet is currently enabled. It should be +/// computed by the caller and set to `Felt::ONE` /// - The `transition_flag` indicates whether this is the last row this chiplet's execution trace, /// and therefore the constraints should not be enforced. pub fn enforce_constraints>( From f12d465376fef0798b0e2d1ee5f2ca820b81913e Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Fri, 3 Mar 2023 17:12:30 +0100 Subject: [PATCH 22/47] chore: renamed ProgramInfo to ExecutionDetails fix #743 --- CHANGELOG.md | 3 +++ miden/src/tools/mod.rs | 53 +++++++++++++++++++++--------------------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 808b50260f..b1ff573e0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Next +#### CLI +- Renamed `ProgramInfo` to `ExecutionDetails` since there is another `ProgramInfo` struct in the source code. + #### Assembly - Added new instructions: `is_odd`, `assert_eqw`. diff --git a/miden/src/tools/mod.rs b/miden/src/tools/mod.rs index dabdb5e681..a5fe8a0886 100644 --- a/miden/src/tools/mod.rs +++ b/miden/src/tools/mod.rs @@ -34,32 +34,31 @@ impl Analyze { let stack_inputs = input_data.parse_stack_inputs()?; let advice_provider = input_data.parse_advice_provider()?; - let program_info: ProgramInfo = analyze(program.as_str(), stack_inputs, advice_provider) - .expect("Could not retrieve program info"); + let execution_details: ExecutionDetails = + analyze(program.as_str(), stack_inputs, advice_provider) + .expect("Could not retrieve execution details"); - println!("{}", program_info); + println!("{}", execution_details); Ok(()) } } -// PROGRAM INFO +// EXECUTION DETAILS // ================================================================================================ -/// Contains info of a program. Used for program analysis. Contains the following fields: -/// - total_vm_cycles: vm cycles it takes to execute the entire program -/// - total_noops: total noops executed as part of a program -/// - asm_op_stats: vector of [AsmOpStats] that contains assembly instructions and -/// the number of vm cycles it takes to execute the instruction and the number of times the -/// instruction is run as part of the given program. +/// Contains details of executing a program, used for program analysis. #[derive(Debug, Default, Eq, PartialEq)] -pub struct ProgramInfo { +pub struct ExecutionDetails { + /// Number of VM cycles it took to execute the entire program. total_vm_cycles: u32, + /// Number of noops executed as part of a program. total_noops: usize, + /// Statistics about individual assembly operations executed by the VM, see [AsmOpStats]. asm_op_stats: Vec, } -impl ProgramInfo { +impl ExecutionDetails { /// Returns total vm cycles to execute a program pub fn total_vm_cycles(&self) -> u32 { self.total_vm_cycles @@ -119,7 +118,7 @@ impl ProgramInfo { } } -impl fmt::Display for ProgramInfo { +impl fmt::Display for ExecutionDetails { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let total_vm_cycles = self.total_vm_cycles(); let total_noops = self.total_noops(); @@ -150,7 +149,7 @@ pub fn analyze( program: &str, stack_inputs: StackInputs, advice_provider: A, -) -> Result +) -> Result where A: AdviceProvider, { @@ -161,20 +160,20 @@ where .compile(program) .map_err(ProgramError::AssemblyError)?; let vm_state_iterator = processor::execute_iter(&program, stack_inputs, advice_provider); - let mut program_info = ProgramInfo::default(); + let mut execution_details = ExecutionDetails::default(); for state in vm_state_iterator { let vm_state = state.map_err(ProgramError::ExecutionError)?; if matches!(vm_state.op, Some(Operation::Noop)) { - program_info.incr_noop_count(); + execution_details.incr_noop_count(); } if let Some(asmop_info) = vm_state.asmop { - program_info.record_asmop(asmop_info); + execution_details.record_asmop(asmop_info); } - program_info.set_total_vm_cycles(vm_state.clk); + execution_details.set_total_vm_cycles(vm_state.clk); } - Ok(program_info) + Ok(execution_details) } // ASMOP STATS @@ -233,7 +232,7 @@ impl AsmOpStats { #[cfg(test)] mod tests { - use super::{AsmOpStats, ProgramInfo, StackInputs}; + use super::{AsmOpStats, ExecutionDetails, StackInputs}; use miden::MemAdviceProvider; #[test] @@ -242,9 +241,9 @@ mod tests { "proc.foo.1 loc_store.0 end begin mem_storew.1 dropw push.17 push.1 movdn.2 exec.foo end"; let stack_inputs = StackInputs::default(); let advice_provider = MemAdviceProvider::default(); - let program_info = super::analyze(source, stack_inputs, advice_provider) + let execution_details = super::analyze(source, stack_inputs, advice_provider) .expect("analyze_test: Unexpected Error"); - let expected_program_info = ProgramInfo { + let expected_details = ExecutionDetails { total_vm_cycles: 23, total_noops: 2, asm_op_stats: vec![ @@ -255,7 +254,7 @@ mod tests { AsmOpStats::new("push".to_string(), 2, 3), ], }; - assert_eq!(program_info, expected_program_info); + assert_eq!(execution_details, expected_details); } #[test] @@ -264,9 +263,9 @@ mod tests { let stack_inputs = vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let stack_inputs = StackInputs::try_from_values(stack_inputs).unwrap(); let advice_provider = MemAdviceProvider::default(); - let program_info = super::analyze(source, stack_inputs, advice_provider); + let execution_details = super::analyze(source, stack_inputs, advice_provider); let expected_error = "Execution Error: DivideByZero(1)"; - assert_eq!(program_info.err().unwrap().to_string(), expected_error); + assert_eq!(execution_details.err().unwrap().to_string(), expected_error); } #[test] @@ -274,8 +273,8 @@ mod tests { let source = "proc.foo.1 loc_store.0 end mem_storew.1 dropw push.17 exec.foo end"; let stack_inputs = StackInputs::default(); let advice_provider = MemAdviceProvider::default(); - let program_info = super::analyze(source, stack_inputs, advice_provider); + let execution_details = super::analyze(source, stack_inputs, advice_provider); let expected_error = "Assembly Error: ParsingError(\"unexpected token: expected 'begin' but was 'mem_storew.1'\")"; - assert_eq!(program_info.err().unwrap().to_string(), expected_error); + assert_eq!(execution_details.err().unwrap().to_string(), expected_error); } } From e83ebcbe397fbc4c9ecc1df2bf1181b449f802bf Mon Sep 17 00:00:00 2001 From: frisitano Date: Sun, 5 Mar 2023 14:23:10 +0400 Subject: [PATCH 23/47] feat: enable debug mode on assembler for debugger --- miden/src/cli/compile.rs | 2 +- miden/src/cli/data.rs | 3 ++- miden/src/cli/debug/mod.rs | 2 +- miden/src/cli/prove.rs | 2 +- miden/src/cli/run.rs | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/miden/src/cli/compile.rs b/miden/src/cli/compile.rs index 37ff2693e4..9844f3dc59 100644 --- a/miden/src/cli/compile.rs +++ b/miden/src/cli/compile.rs @@ -17,7 +17,7 @@ impl CompileCmd { println!("============================================================"); // load and compile program file - let program = ProgramFile::read(&self.assembly_file)?; + let program = ProgramFile::read(&self.assembly_file, false)?; // report program hash to user let program_hash: [u8; 32] = program.hash().into(); diff --git a/miden/src/cli/data.rs b/miden/src/cli/data.rs index 8b0d805387..7044ecd946 100644 --- a/miden/src/cli/data.rs +++ b/miden/src/cli/data.rs @@ -164,7 +164,7 @@ pub struct ProgramFile; /// Helper methods to interact with masm program file impl ProgramFile { - pub fn read(path: &PathBuf) -> Result { + pub fn read(path: &PathBuf, debug: bool) -> Result { println!("Reading program file `{}`", path.display()); // read program file to string @@ -178,6 +178,7 @@ impl ProgramFile { let program = Assembler::default() .with_library(&StdLibrary::default()) .map_err(|err| format!("Failed to load stdlib - {}", err))? + .with_debug_mode(debug) .compile(&program_file) .map_err(|err| format!("Failed to compile program - {}", err))?; diff --git a/miden/src/cli/debug/mod.rs b/miden/src/cli/debug/mod.rs index cc538e98d5..f6edbb09b2 100644 --- a/miden/src/cli/debug/mod.rs +++ b/miden/src/cli/debug/mod.rs @@ -30,7 +30,7 @@ impl DebugCmd { println!("============================================================"); // load program from file and compile - let program = ProgramFile::read(&self.assembly_file)?; + let program = ProgramFile::read(&self.assembly_file, true)?; let program_hash: [u8; 32] = program.hash().into(); println!("Debugging program with hash {}... ", hex::encode(program_hash)); diff --git a/miden/src/cli/prove.rs b/miden/src/cli/prove.rs index 6101bc45f7..404ade2e29 100644 --- a/miden/src/cli/prove.rs +++ b/miden/src/cli/prove.rs @@ -48,7 +48,7 @@ impl ProveCmd { .init(); // load program from file and compile - let program = ProgramFile::read(&self.assembly_file)?; + let program = ProgramFile::read(&self.assembly_file, false)?; // load input data from file let input_data = InputFile::read(&self.input_file, &self.assembly_file)?; diff --git a/miden/src/cli/run.rs b/miden/src/cli/run.rs index d3eb20cfcc..6a92af17e6 100644 --- a/miden/src/cli/run.rs +++ b/miden/src/cli/run.rs @@ -26,7 +26,7 @@ impl RunCmd { println!("============================================================"); // load program from file and compile - let program = ProgramFile::read(&self.assembly_file)?; + let program = ProgramFile::read(&self.assembly_file, false)?; // load input data from file let input_data = InputFile::read(&self.input_file, &self.assembly_file)?; From b3f1154a7cc413214cf10951188556b4bf8153dc Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Tue, 7 Mar 2023 22:22:51 +0100 Subject: [PATCH 24/47] docs: fixed inline comments The VM has migrated to the Rescue Prime Optimized sponge function, which does not use the capacity to count the number of elements. Only to signal if padding is necessary. The user documentation is up-to-date and contains more details. --- processor/src/chiplets/hasher/mod.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/processor/src/chiplets/hasher/mod.rs b/processor/src/chiplets/hasher/mod.rs index fb8867e175..8e2f1b2cbc 100644 --- a/processor/src/chiplets/hasher/mod.rs +++ b/processor/src/chiplets/hasher/mod.rs @@ -29,7 +29,7 @@ mod tests; // HASH PROCESSOR // ================================================================================================ -/// Hash processor for the VM. +/// Hash chiplet for the VM. /// /// This component is responsible for performing all hash-related computations for the VM, as well /// as building an execution trace for these computations. These computations include: @@ -49,12 +49,10 @@ mod tests; /// column start at 1 and are incremented by one with every subsequent row. /// * Hasher state columns h0 through h11 used to hold the hasher state for each round of hash /// computation. The state is laid out as follows: -/// - The first four columns are reserved for capacity elements of the state. When the state -/// is initialized for hash computations, h0 should be set to the number of elements to be -/// hashed. All other capacity elements should be set to 0s. -/// - The next eight columns are reserved for the rate elements of the state. These are used -/// to absorb the values to be hashed. Once a permutation is complete, hash output is located -/// in the first four rate columns (h4, h5, h6, h7). +/// - The first four columns represent the capacity state of the sponge function. +/// - The next eight columns represent the rate elements of the state. These are used to absorb +/// the values to be hashed. Once a permutation is complete, hash output is located in the first +/// four rate columns (h4, h5, h6, h7). /// * Node index column idx used to help with Merkle path verification and Merkle root update /// computations. For all other computations the values in this column are set to 0s. /// @@ -62,7 +60,7 @@ mod tests; /// path verification, number of rows added to the trace is 8 * path.len(), and for Merkle root /// update it is 16 * path.len(), since we need to perform two path verifications for each update. /// -/// In addition to the execution trace, the hash processor also maintains: +/// In addition to the execution trace, the hash chiplet also maintains: /// - an auxiliary trace builder, which can be used to construct a running product column describing /// the state of the sibling table (used in Merkle root update operations). /// - a vector of [HasherLookup]s, each of which specifies the data for one of the lookup rows which From 8f7fc16dc06217f83271d071992767017cf9233e Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Tue, 7 Mar 2023 23:03:47 +0100 Subject: [PATCH 25/47] docs: clarify the label definition --- core/src/chiplets/bitwise.rs | 12 +++++------ core/src/chiplets/hasher.rs | 36 ++++++++++++++++---------------- core/src/chiplets/memory.rs | 16 +++++++------- docs/src/design/chiplets/main.md | 2 +- 4 files changed, 32 insertions(+), 34 deletions(-) diff --git a/core/src/chiplets/bitwise.rs b/core/src/chiplets/bitwise.rs index eaa5f5fd50..b8092fdea2 100644 --- a/core/src/chiplets/bitwise.rs +++ b/core/src/chiplets/bitwise.rs @@ -16,15 +16,15 @@ pub const OP_CYCLE_LEN: usize = 8; /// Specifies a bitwise AND operation. pub const BITWISE_AND: Felt = ZERO; -/// Unique label for the bitwise AND operation. Computed as 1 more than the binary composition of -/// the chiplet and operation selectors [1, 0, 0]. -pub const BITWISE_AND_LABEL: Felt = Felt::new(2); +/// Unique label computed as 1 plus the full chiplet selector with the bits reversed. +/// bitwise_and selector=[1, 0, 0] rev(selector)=[0, 0, 1] +1=[0, 1, 0] +pub const BITWISE_AND_LABEL: Felt = Felt::new(0b010); /// Specifies a bitwise XOR operation. pub const BITWISE_XOR: Felt = ONE; -/// Unique label for the bitwise XOR operation. Computed as 1 more than the binary composition of -/// the chiplet and operation selectors [1, 0, 1]. -pub const BITWISE_XOR_LABEL: Felt = Felt::new(6); +/// Unique label computed as 1 plus the full chiplet selector with the bits reversed. +/// bitwise_xor selector=[1, 0, 1] rev(selector)=[1, 0, 1] +1=[1, 1, 0] +pub const BITWISE_XOR_LABEL: Felt = Felt::new(0b110); // --- INPUT DECOMPOSITION ------------------------------------------------------------------------ diff --git a/core/src/chiplets/hasher.rs b/core/src/chiplets/hasher.rs index 484963a74a..624850c959 100644 --- a/core/src/chiplets/hasher.rs +++ b/core/src/chiplets/hasher.rs @@ -80,44 +80,44 @@ pub const TRACE_WIDTH: usize = NUM_SELECTORS + STATE_WIDTH + 2; /// executing linear hash computation. These selectors can also be used for a simple 2-to-1 hash /// computation. pub const LINEAR_HASH: Selectors = [ONE, ZERO, ZERO]; -/// Unique label for the linear hash operation. Computed as 1 more than the binary composition of -/// the chiplet and operation selectors [0, 1, 0, 0]. -pub const LINEAR_HASH_LABEL: u8 = 3; +/// Unique label computed as 1 plus the full chiplet selector with the bits reversed. +/// linear_hash selector=[0, 1, 0, 0] rev(selector)=[0, 0, 1, 0] +1=[0, 0, 1, 1] +pub const LINEAR_HASH_LABEL: u8 = 0b0011; /// Specifies a start of Merkle path verification computation or absorption of a new path node /// into the hasher state. pub const MP_VERIFY: Selectors = [ONE, ZERO, ONE]; -/// Unique label for the merkle path verification operation. Computed as 1 more than the binary -/// composition of the chiplet and operation selectors [0, 1, 0, 1]. -pub const MP_VERIFY_LABEL: u8 = 11; +/// Unique label computed as 1 plus the full chiplet selector with the bits reversed. +/// mp_verify selector=[0, 1, 0, 1] rev(selector)=[1, 0, 1, 0] +1=[1, 0, 1, 1] +pub const MP_VERIFY_LABEL: u8 = 0b1011; /// Specifies a start of Merkle path verification or absorption of a new path node into the hasher /// state for the "old" node value during Merkle root update computation. pub const MR_UPDATE_OLD: Selectors = [ONE, ONE, ZERO]; -/// Unique label for the merkle path update operation for an "old" node. Computed as 1 more than the -/// binary composition of the chiplet and operation selectors [0, 1, 1, 0]. -pub const MR_UPDATE_OLD_LABEL: u8 = 7; +/// Unique label computed as 1 plus the full chiplet selector with the bits reversed. +/// mr_update selector=[0, 1, 1, 0] rev(selector)=[0, 1, 1, 0] +1=[0, 1, 1, 1] +pub const MR_UPDATE_OLD_LABEL: u8 = 0b0111; /// Specifies a start of Merkle path verification or absorption of a new path node into the hasher /// state for the "new" node value during Merkle root update computation. pub const MR_UPDATE_NEW: Selectors = [ONE, ONE, ONE]; -/// Unique label for the merkle path update operation for a "new" node. Computed as 1 more than the -/// binary composition of the chiplet and operation selectors [0, 1, 1, 1]. -pub const MR_UPDATE_NEW_LABEL: u8 = 15; +/// Unique label computed as 1 plus the full chiplet selector with the bits reversed. +/// mr_update selector=[0, 1, 1, 1] rev(selector)=[1, 1, 1, 0] +1=[1, 1, 1, 1] +pub const MR_UPDATE_NEW_LABEL: u8 = 0b1111; /// Specifies a completion of a computation such that only the hash result (values in h0, h1, h2 /// h3) is returned. pub const RETURN_HASH: Selectors = [ZERO, ZERO, ZERO]; -/// Unique label for specifying the return of a hash result. Computed as 1 more than the binary -/// composition of the chiplet and operation selectors [0, 0, 0, 0]. -pub const RETURN_HASH_LABEL: u8 = 1; +/// Unique label computed as 1 plus the full chiplet selector with the bits reversed. +/// return_hash selector=[0, 0, 0, 0] rev(selector)=[0, 0, 0, 0] +1=[0, 0, 0, 1] +pub const RETURN_HASH_LABEL: u8 = 0b0001; /// Specifies a completion of a computation such that the entire hasher state (values in h0 through /// h11) is returned. pub const RETURN_STATE: Selectors = [ZERO, ZERO, ONE]; -/// Unique label for specifying the return of the entire hasher state. Computed as 1 more than the -/// binary composition of the chiplet and operation selectors [0, 0, 0, 1] -pub const RETURN_STATE_LABEL: u8 = 9; +/// Unique label computed as 1 plus the full chiplet selector with the bits reversed. +/// return_state selector=[0, 0, 0, 1] rev(selector)=[1, 0, 0, 0] +1=[1, 0, 0, 1] +pub const RETURN_STATE_LABEL: u8 = 0b1001; // --- Column accessors in the auxiliary trace ---------------------------------------------------- diff --git a/core/src/chiplets/memory.rs b/core/src/chiplets/memory.rs index dac84f3644..9990e1219b 100644 --- a/core/src/chiplets/memory.rs +++ b/core/src/chiplets/memory.rs @@ -25,15 +25,13 @@ pub const MEMORY_COPY_READ: Selectors = [ONE, ONE]; /// Specifies a memory write operation. pub const MEMORY_WRITE: Selectors = [ZERO, ZERO]; -/// Unique label for memory read operations. Computed as 1 more than the binary composition of the -/// chiplet and operation selectors [1, 1, 0, 1], where the memory operation selector is the first -/// of the memory trace selector columns. -pub const MEMORY_READ_LABEL: u8 = 12; - -/// Unique label for memory write operations. Computed as 1 more than the binary composition of the -/// chiplet and operation selectors [1, 1, 0, 0], where the memory operation selector is the first -/// of the memory trace selector columns. -pub const MEMORY_WRITE_LABEL: u8 = 4; +/// Unique label computed as 1 plus the full chiplet selector with the bits reversed. +/// mem_read selector=[1, 1, 0, 1], rev(selector)=[1, 0, 1, 1], +1=[1, 1, 0, 0] +pub const MEMORY_READ_LABEL: u8 = 0b1100; + +/// Unique label computed as 1 plus the full chiplet selector with the bits reversed. +/// mem_write selector=[1, 1, 0, 0] rev(selector)=[0, 0, 1, 1] +1=[0, 1, 0, 0] +pub const MEMORY_WRITE_LABEL: u8 = 0b0100; // --- COLUMN ACCESSOR INDICES WITHIN THE CHIPLET ------------------------------------------------- diff --git a/docs/src/design/chiplets/main.md b/docs/src/design/chiplets/main.md index 1135918ae4..e864d97cc0 100644 --- a/docs/src/design/chiplets/main.md +++ b/docs/src/design/chiplets/main.md @@ -85,7 +85,7 @@ This requires the following adjustments for each chiplet. Each operation supported by the chiplets is given a unique identifier to ensure that the requests and responses sent to $b_{chip}$ are indeed processed by the intended chiplet for that operation and that chiplets which support more than one operation execute the correct one. -The labels are composed from the flag values of the chiplet selector(s) and internal operation selectors (if applicable). The unique label of the operation is computed as the binary aggregation of the combined selectors plus $1$. +The labels are composed from the flag values of the chiplet selector(s) and internal operation selectors (if applicable). The unique label of the operation is computed as the binary aggregation of the combined selectors plus $1$, note that the combined flag is represented in big-endian, so the bit representation below is reverted. | Operation | Chiplet Selector Flag | Internal Selector Flag | Combined Flag | Label | | ---------------------- | --------------------- | :--------------------: | ---------------- | :---: | From ab304177a01f55143a0440e3878f508a264c9c4a Mon Sep 17 00:00:00 2001 From: frisitano Date: Mon, 13 Mar 2023 16:22:09 +0400 Subject: [PATCH 26/47] feat: expose stack trace_state() via internals flag --- Makefile | 2 +- miden/Cargo.toml | 5 ++--- processor/src/stack/mod.rs | 2 +- processor/src/stack/trace.rs | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 4d20e0114e..6c9f7111e6 100644 --- a/Makefile +++ b/Makefile @@ -2,4 +2,4 @@ exec: cargo build --release --features concurrent,executable test: - RUSTFLAGS="-C debug-assertions -C overflow-checks -C debuginfo=2" cargo test --release --features internals + RUSTFLAGS="-C debug-assertions -C overflow-checks -C debuginfo=2" cargo test --release diff --git a/miden/Cargo.toml b/miden/Cargo.toml index 3801940aab..3c5b7eccba 100644 --- a/miden/Cargo.toml +++ b/miden/Cargo.toml @@ -35,13 +35,11 @@ harness = false [[test]] name = "miden" path = "tests/integration/main.rs" -required-features = ["internals"] [features] concurrent = ["prover/concurrent", "std"] default = ["std"] -executable = ["env_logger", "hex/std", "internals", "std", "serde/std", "serde_derive", "serde_json/std", "structopt", "rustyline"] -internals = ["processor/internals"] +executable = ["env_logger", "hex/std", "processor/internals", "std", "serde/std", "serde_derive", "serde_json/std", "structopt", "rustyline"] std = ["assembly/std", "log/std", "processor/std", "prover/std", "verifier/std"] [dependencies] @@ -66,6 +64,7 @@ criterion = "0.4" escargot = "0.5.7" num-bigint = "0.4" predicates = "2.1.5" +processor = { package = "miden-processor", path = "../processor", version = "0.5", features = ["internals"], default-features = false } proptest = "1.0.0" rand-utils = { package = "winter-rand-utils", version = "0.5" } sha2 = "0.10" diff --git a/processor/src/stack/mod.rs b/processor/src/stack/mod.rs index 086f317490..f9e995309b 100644 --- a/processor/src/stack/mod.rs +++ b/processor/src/stack/mod.rs @@ -311,7 +311,7 @@ impl Stack { /// Returns state of stack item columns at the current clock cycle. This does not include stack /// values in the overflow table. - #[cfg(test)] + #[cfg(any(test, feature = "internals"))] pub fn trace_state(&self) -> [Felt; STACK_TOP_SIZE] { self.trace.get_stack_state_at(self.clk) } diff --git a/processor/src/stack/trace.rs b/processor/src/stack/trace.rs index 02d9391331..d45d5c9f79 100644 --- a/processor/src/stack/trace.rs +++ b/processor/src/stack/trace.rs @@ -201,7 +201,7 @@ impl StackTrace { // -------------------------------------------------------------------------------------------- /// Returns the stack trace state at the specified clock cycle. - #[cfg(test)] + #[cfg(any(test, feature = "internals"))] pub fn get_stack_state_at(&self, clk: u32) -> [Felt; STACK_TOP_SIZE] { let mut result = [ZERO; STACK_TOP_SIZE]; for (result, column) in result.iter_mut().zip(self.stack.iter()) { From 09700b13e7aca62dfc51131aa15b88f965ad3b31 Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Mon, 6 Mar 2023 20:25:45 +0100 Subject: [PATCH 27/47] feat: add breakpoint instruction This commit introduces `breakpoint`, a transparent instruction that will cause a debug execution to break when reached. This instruction will not be serialized into libraries, and will not have an opcode or be part of the code block tree. For debug executions, it will produce a `NOOP`, so the internal clock of the VM will be affected. The decision of not including this as part of the code block is to avoid further consuming variants of opcodes as they are already scarce. related issue: #580 --- assembly/src/assembler/context.rs | 16 +- assembly/src/assembler/instruction/mod.rs | 11 +- assembly/src/assembler/span_builder.rs | 12 +- assembly/src/parsers/context.rs | 3 + assembly/src/parsers/nodes.rs | 13 ++ assembly/src/parsers/serde/serialization.rs | 5 + core/src/operations/decorators/assembly_op.rs | 42 ++++- docs/src/intro/usage.md | 71 +++++++-- miden/src/cli/debug/command.rs | 10 +- miden/src/cli/debug/executor.rs | 33 ++-- miden/tests/integration/exec_iters.rs | 62 ++++++-- .../operations/decorators/asmop.rs | 147 +++++++++++++---- processor/src/debug.rs | 150 ++++++++++-------- processor/src/lib.rs | 3 +- 14 files changed, 419 insertions(+), 159 deletions(-) diff --git a/assembly/src/assembler/context.rs b/assembly/src/assembler/context.rs index 786d688ffb..d9683fb10a 100644 --- a/assembly/src/assembler/context.rs +++ b/assembly/src/assembler/context.rs @@ -250,11 +250,18 @@ impl AssemblyContext { // HELPER METHODS // -------------------------------------------------------------------------------------------- - /// Returns the context of the procedure currently being complied, or None if module or + /// Returns the context of the procedure currently being compiled, or None if module or /// procedure stacks are empty. fn current_proc_context(&self) -> Option<&ProcedureContext> { self.module_stack.last().and_then(|m| m.proc_stack.last()) } + + /// Returns the name of the current procedure, or the reserved name for the main block. + pub(crate) fn current_context_name(&self) -> &str { + self.current_proc_context() + .map(|p| p.name().as_ref()) + .expect("library compilation mode is currently not supported!") + } } // MODULE CONTEXT @@ -478,6 +485,13 @@ impl ProcedureContext { self.name.is_main() } + /// Returns the current context name. + /// + /// Check [AssemblyContext::current_context_name] for reference. + pub const fn name(&self) -> &ProcedureName { + &self.name + } + pub fn into_procedure(self, id: ProcedureId, code_root: CodeBlock) -> Procedure { let Self { name, diff --git a/assembly/src/assembler/instruction/mod.rs b/assembly/src/assembler/instruction/mod.rs index 25eadb7b64..6a874f79d2 100644 --- a/assembly/src/assembler/instruction/mod.rs +++ b/assembly/src/assembler/instruction/mod.rs @@ -34,7 +34,7 @@ impl Assembler { // this will allow us to map the instruction to the sequence of operations which were // executed as a part of this instruction. if self.in_debug_mode() { - span.track_instruction(instruction); + span.track_instruction(instruction, ctx); } let result = match instruction { @@ -306,6 +306,15 @@ impl Assembler { Instruction::CallLocal(idx) => self.call_local(*idx, ctx), Instruction::CallImported(id) => self.call_imported(id, ctx), Instruction::SysCall(id) => self.syscall(id, ctx), + + // ----- debug decorators ------------------------------------------------------------- + Instruction::Breakpoint => { + if self.in_debug_mode() { + span.add_op(Noop)?; + span.track_instruction(instruction, ctx); + } + Ok(None) + } }; // compute and update the cycle count of the instruction which just finished executing diff --git a/assembly/src/assembler/span_builder.rs b/assembly/src/assembler/span_builder.rs index bcc8ac0029..6f1f797e6c 100644 --- a/assembly/src/assembler/span_builder.rs +++ b/assembly/src/assembler/span_builder.rs @@ -1,6 +1,6 @@ use super::{ - AssemblyError, BodyWrapper, Borrow, CodeBlock, Decorator, DecoratorList, Instruction, - Operation, ToString, Vec, + AssemblyContext, AssemblyError, BodyWrapper, Borrow, CodeBlock, Decorator, DecoratorList, + Instruction, Operation, ToString, Vec, }; use vm_core::AssemblyOp; @@ -102,8 +102,12 @@ impl SpanBuilder { /// /// This indicates that the provided instruction should be tracked and the cycle count for /// this instruction will be computed when the call to set_instruction_cycle_count() is made. - pub fn track_instruction(&mut self, instruction: &Instruction) { - let op = AssemblyOp::new(instruction.to_string(), 0); + pub fn track_instruction(&mut self, instruction: &Instruction, ctx: &mut AssemblyContext) { + let context_name = ctx.current_context_name().to_string(); + let num_cycles = 0; + let op = instruction.to_string(); + let should_break = instruction.should_break(); + let op = AssemblyOp::new(context_name, num_cycles, op, should_break); self.push_decorator(Decorator::AsmOp(op)); self.last_asmop_pos = self.decorators.len() - 1; } diff --git a/assembly/src/parsers/context.rs b/assembly/src/parsers/context.rs index ca5972709f..3cb2e9e0ac 100644 --- a/assembly/src/parsers/context.rs +++ b/assembly/src/parsers/context.rs @@ -511,6 +511,9 @@ impl ParserContext { // ----- constant statements ---------------------------------------------------------- "const" => Err(ParsingError::const_invalid_scope(op)), + // ----- debug decorators ------------------------------------------------------------- + "breakpoint" => simple_instruction(op, Breakpoint), + // ----- catch all -------------------------------------------------------------------- _ => Err(ParsingError::invalid_op(op)), } diff --git a/assembly/src/parsers/nodes.rs b/assembly/src/parsers/nodes.rs index 9884e1d750..28d2ecd42f 100644 --- a/assembly/src/parsers/nodes.rs +++ b/assembly/src/parsers/nodes.rs @@ -277,6 +277,16 @@ pub enum Instruction { CallLocal(u16), CallImported(ProcedureId), SysCall(ProcedureId), + + // ----- debug decorators --------------------------------------------------------------------- + Breakpoint, +} + +impl Instruction { + /// Returns true if the instruction should yield a breakpoint. + pub const fn should_break(&self) -> bool { + matches!(self, Self::Breakpoint) + } } impl fmt::Display for Instruction { @@ -543,6 +553,9 @@ impl fmt::Display for Instruction { Self::CallLocal(index) => write!(f, "call.{index}"), Self::CallImported(proc_id) => write!(f, "call.{proc_id}"), Self::SysCall(proc_id) => write!(f, "syscall.{proc_id}"), + + // ----- debug decorators ------------------------------------------------------------- + Self::Breakpoint => write!(f, "breakpoint"), } } } diff --git a/assembly/src/parsers/serde/serialization.rs b/assembly/src/parsers/serde/serialization.rs index 3a6eaed5b2..e0e9b413b3 100644 --- a/assembly/src/parsers/serde/serialization.rs +++ b/assembly/src/parsers/serde/serialization.rs @@ -467,6 +467,11 @@ impl Serializable for Instruction { OpCode::SysCall.write_into(target)?; imported.write_into(target)? } + + // ----- debug decorators ------------------------------------------------------------- + Self::Breakpoint => { + // this is a transparent instruction and will not be encoded into the library + } } Ok(()) } diff --git a/core/src/operations/decorators/assembly_op.rs b/core/src/operations/decorators/assembly_op.rs index 05652a1c6f..1ade86fb22 100644 --- a/core/src/operations/decorators/assembly_op.rs +++ b/core/src/operations/decorators/assembly_op.rs @@ -1,4 +1,5 @@ use crate::utils::string::String; +use core::fmt; // ASSEMBLY OP // ================================================================================================ @@ -6,27 +7,44 @@ use crate::utils::string::String; /// Contains information corresponding to an assembly instruction (only applicable in debug mode). #[derive(Clone, Debug, Eq, PartialEq)] pub struct AssemblyOp { - op: String, + context_name: String, num_cycles: u8, + op: String, + should_break: bool, } impl AssemblyOp { /// Returns [AssemblyOp] instantiated with the specified assembly instruction string and number /// of cycles it takes to execute the assembly instruction. - pub fn new(op: String, num_cycles: u8) -> Self { - Self { op, num_cycles } + pub fn new(context_name: String, num_cycles: u8, op: String, should_break: bool) -> Self { + Self { + context_name, + num_cycles, + op, + should_break, + } } - /// Returns the assembly instruction corresponding to this decorator. - pub fn op(&self) -> &String { - &self.op + /// Returns the context name for this operation. + pub fn context_name(&self) -> &str { + &self.context_name } /// Returns the number of VM cycles taken to execute the assembly instruction of this decorator. - pub fn num_cycles(&self) -> u8 { + pub const fn num_cycles(&self) -> u8 { self.num_cycles } + /// Returns the assembly instruction corresponding to this decorator. + pub fn op(&self) -> &str { + &self.op + } + + /// Returns `true` if there is a breakpoint for the current operation. + pub const fn should_break(&self) -> bool { + self.should_break + } + // STATE MUTATORS // -------------------------------------------------------------------------------------------- @@ -35,3 +53,13 @@ impl AssemblyOp { self.num_cycles = num_cycles; } } + +impl fmt::Display for AssemblyOp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "context={}, operation={}, cost={}", + self.context_name, self.op, self.num_cycles, + ) + } +} diff --git a/docs/src/intro/usage.md b/docs/src/intro/usage.md index 88d34dbbd1..acc5cb925c 100644 --- a/docs/src/intro/usage.md +++ b/docs/src/intro/usage.md @@ -65,23 +65,60 @@ This will dump the output of the program into the `fib.out` file. The output fil ### Miden Debugger -The Miden debugger is a shell that allow for efficient debugging of miden assembly programs that are sourced from file. A Miden assembly program file and inputs are specified when the debugger is instantiated. The debugger allows the user to step through the execution of the program with clock cycle granularity and provides the ability for virtual machine state inspection at each clock cycle. The Miden debugger supports the following commands: - -``` -!next steps to the next clock cycle -!play executes program until completion or failure -!play.n executes n clock cycles -!prev steps to the previous clock cycle -!rewind rewinds program until beginning -!rewind.n rewinds n clock cycles -!print displays the complete state of the virtual machine -!stack displays the complete state of the stack -!stack[i] displays the stack element at index i -!mem displays the complete state of memory -!mem[i] displays memory at address i -!clock displays the current clock cycle -!quit quits the debugger -!help displays this message +The Miden debugger is a command-line interface (CLI) application, inspired on [GNU gdb](https://sourceware.org/gdb/), that allows debugging of Miden assembly (MASM) programs. The debugger allows the user to step through the execution of the program, both forward and backward, either per clock cycle tick, or via breakpoints. + +The Miden debugger supports the following commands: + +| Command | Shortcut | Arguments | Description | +| --- | --- | --- | --- | +| next | n | count? | Steps `count` clock cycles. Will step `1` cycle of `count` is ommitted. | +| continue | c | - | Executes the program until completion, failure or a breakpoint. | +| back | b | count? | Backward step `count` clock cycles. Will back-step `1` cycle of `count` is ommitted. | +| rewind | r | - | Executes the program backwards until the beginning, failure or a breakpoint. | +| print | p | - | Displays the complete state of the virtual machine. | +| print mem | p m | address? | Displays the memory value at `address`. If `address` is ommitted, didisplays all the memory values. | +| print stack | p s | index? | Displays the stack value at `index`. If `index` is ommitted, displays all the stack values. | +| clock | c | - | Displays the current clock cycle. | +| quit | q | - | Quits the debugger. | +| help | h | - | Displays the help message. | + +In order to start debugging, the user should provide a `MASM` program: + +```shell +cargo run --features executable -- debug --assembly miden/examples/nprime/nprime.masm +``` + +The expected output is: + +``` +============================================================ +Debug program +============================================================ +Reading program file `miden/examples/nprime/nprime.masm` +Compiling program... done (16 ms) +Debugging program with hash 11dbbddff27e26e48be3198133df8cbed6c5875d0fb +606c9f037c7893fde4118... +Reading input file `miden/examples/nprime/nprime.inputs` +Welcome! Enter `h` for help. +>> +``` + +In order to add a breakpoint, the user should insert a `breakpoint` instruction into the MASM file. This will generate a `Noop` operation that will be decorated with the debug break configuration. This is a provisory solution until the source mapping is implemented. + +The following example will halt on the third instruction of `foo`: + +``` +proc.foo + dup + dup.2 + breakpoint + swap + add.1 +end + +begin + exec.foo +end ``` ### REPL diff --git a/miden/src/cli/debug/command.rs b/miden/src/cli/debug/command.rs index 70465a3687..71447f1f2a 100644 --- a/miden/src/cli/debug/command.rs +++ b/miden/src/cli/debug/command.rs @@ -3,8 +3,8 @@ pub enum DebugCommand { Continue, Next(usize), - RewindAll, - Rewind(usize), + Rewind, + Back(usize), PrintState, PrintStack, PrintStackItem(usize), @@ -36,7 +36,7 @@ impl DebugCommand { "n" | "next" => Self::parse_next(tokens.by_ref())?, "c" | "continue" => Self::Continue, "b" | "back" => Self::parse_back(tokens.by_ref())?, - "r" | "rewind" => Self::RewindAll, + "r" | "rewind" => Self::Rewind, "p" | "print" => Self::parse_print(tokens.by_ref())?, "l" | "clock" => Self::Clock, "h" | "?" | "help" => Self::Help, @@ -89,9 +89,9 @@ impl DebugCommand { n, err ) })?, - None => return Ok(Self::Rewind(1)), + None => return Ok(Self::Back(1)), }; - Ok(Self::Rewind(num_cycles)) + Ok(Self::Back(num_cycles)) } /// parse print command - p [m|s] [addr] diff --git a/miden/src/cli/debug/executor.rs b/miden/src/cli/debug/executor.rs index b081aff807..4aec1c2ad5 100644 --- a/miden/src/cli/debug/executor.rs +++ b/miden/src/cli/debug/executor.rs @@ -45,6 +45,9 @@ impl DebugExecutor { DebugCommand::Continue => { while let Some(new_vm_state) = self.next_vm_state() { self.vm_state = new_vm_state; + if self.should_break() { + break; + } } self.print_vm_state(); } @@ -53,23 +56,29 @@ impl DebugExecutor { match self.next_vm_state() { Some(next_vm_state) => { self.vm_state = next_vm_state; + if self.should_break() { + break; + } } None => break, } } self.print_vm_state(); } - DebugCommand::RewindAll => { - while let Some(new_vm_state) = self.prev_vm_state() { + DebugCommand::Rewind => { + while let Some(new_vm_state) = self.vm_state_iter.back() { self.vm_state = new_vm_state; } self.print_vm_state(); } - DebugCommand::Rewind(cycles) => { + DebugCommand::Back(cycles) => { for _cycle in 0..cycles { - match self.prev_vm_state() { + match self.vm_state_iter.back() { Some(new_vm_state) => { self.vm_state = new_vm_state; + if self.should_break() { + break; + } } None => break, } @@ -105,17 +114,6 @@ impl DebugExecutor { } } - /// iterates to the previous clock cycle. - fn prev_vm_state(&mut self) -> Option { - match self.vm_state_iter.next_back() { - Some(prev_vm_state_result) => prev_vm_state_result.ok(), - None => { - println!("At start of program execution."); - None - } - } - } - // ACCESSORS // -------------------------------------------------------------------------------------------- @@ -213,4 +211,9 @@ impl DebugExecutor { println!("{}", message); } + + /// Returns `true` if the current state should break. + fn should_break(&self) -> bool { + self.vm_state.asmop.as_ref().map(|asm| asm.should_break()).unwrap_or(false) + } } diff --git a/miden/tests/integration/exec_iters.rs b/miden/tests/integration/exec_iters.rs index ba45b6c47f..ea1f2e8f1d 100644 --- a/miden/tests/integration/exec_iters.rs +++ b/miden/tests/integration/exec_iters.rs @@ -1,6 +1,6 @@ use super::build_debug_test; use processor::{AsmOpInfo, VmState}; -use vm_core::{utils::ToElements, Felt, FieldElement, Operation}; +use vm_core::{utils::ToElements, AssemblyOp, Felt, FieldElement, Operation}; // EXEC ITER TESTS // ================================================================= @@ -39,7 +39,10 @@ fn test_exec_iter() { clk: 2, ctx: 0, op: Some(Operation::Pad), - asmop: Some(AsmOpInfo::new("mem_storew.1".to_string(), 3, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 3, "mem_storew.1".to_string(), false), + 1, + )), stack: [0, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1].to_elements(), fmp, memory: Vec::new(), @@ -48,7 +51,10 @@ fn test_exec_iter() { clk: 3, ctx: 0, op: Some(Operation::Incr), - asmop: Some(AsmOpInfo::new("mem_storew.1".to_string(), 3, 2)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 3, "mem_storew.1".to_string(), false), + 2, + )), stack: [1, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2].to_elements(), fmp, memory: Vec::new(), @@ -57,7 +63,10 @@ fn test_exec_iter() { clk: 4, ctx: 0, op: Some(Operation::MStoreW), - asmop: Some(AsmOpInfo::new("mem_storew.1".to_string(), 3, 3)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 3, "mem_storew.1".to_string(), false), + 3, + )), stack: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1].to_elements(), fmp, memory: mem.clone(), @@ -66,7 +75,10 @@ fn test_exec_iter() { clk: 5, ctx: 0, op: Some(Operation::Drop), - asmop: Some(AsmOpInfo::new("dropw".to_string(), 4, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 4, "dropw".to_string(), false), + 1, + )), stack: [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0].to_elements(), fmp, memory: mem.clone(), @@ -75,7 +87,10 @@ fn test_exec_iter() { clk: 6, ctx: 0, op: Some(Operation::Drop), - asmop: Some(AsmOpInfo::new("dropw".to_string(), 4, 2)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 4, "dropw".to_string(), false), + 2, + )), stack: [14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0].to_elements(), fmp, memory: mem.clone(), @@ -84,7 +99,10 @@ fn test_exec_iter() { clk: 7, ctx: 0, op: Some(Operation::Drop), - asmop: Some(AsmOpInfo::new("dropw".to_string(), 4, 3)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 4, "dropw".to_string(), false), + 3, + )), stack: [13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0].to_elements(), fmp, memory: mem.clone(), @@ -93,7 +111,10 @@ fn test_exec_iter() { clk: 8, ctx: 0, op: Some(Operation::Drop), - asmop: Some(AsmOpInfo::new("dropw".to_string(), 4, 4)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 4, "dropw".to_string(), false), + 4, + )), stack: [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0].to_elements(), fmp, memory: mem.clone(), @@ -102,7 +123,10 @@ fn test_exec_iter() { clk: 9, ctx: 0, op: Some(Operation::Push(Felt::new(17))), - asmop: Some(AsmOpInfo::new("push.17".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "push.17".to_string(), false), + 1, + )), stack: [17, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0].to_elements(), fmp, memory: mem.clone(), @@ -138,7 +162,10 @@ fn test_exec_iter() { clk: 13, ctx: 0, op: Some(Operation::Pad), - asmop: Some(AsmOpInfo::new("loc_store.0".to_string(), 4, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("foo".to_string(), 4, "loc_store.0".to_string(), false), + 1, + )), stack: [0, 17, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0].to_elements(), fmp: next_fmp, memory: mem.clone(), @@ -147,7 +174,10 @@ fn test_exec_iter() { clk: 14, ctx: 0, op: Some(Operation::FmpAdd), - asmop: Some(AsmOpInfo::new("loc_store.0".to_string(), 4, 2)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("foo".to_string(), 4, "loc_store.0".to_string(), false), + 2, + )), stack: [2u64.pow(30) + 1, 17, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0] .to_elements(), fmp: next_fmp, @@ -157,7 +187,10 @@ fn test_exec_iter() { clk: 15, ctx: 0, op: Some(Operation::MStore), - asmop: Some(AsmOpInfo::new("loc_store.0".to_string(), 4, 3)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("foo".to_string(), 4, "loc_store.0".to_string(), false), + 3, + )), stack: [17, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0].to_elements(), fmp: next_fmp, memory: vec![ @@ -169,7 +202,10 @@ fn test_exec_iter() { clk: 16, ctx: 0, op: Some(Operation::Drop), - asmop: Some(AsmOpInfo::new("loc_store.0".to_string(), 4, 4)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("foo".to_string(), 4, "loc_store.0".to_string(), false), + 4, + )), stack: [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0].to_elements(), fmp: next_fmp, memory: vec![ diff --git a/miden/tests/integration/operations/decorators/asmop.rs b/miden/tests/integration/operations/decorators/asmop.rs index 93e22fc37b..79659c888b 100644 --- a/miden/tests/integration/operations/decorators/asmop.rs +++ b/miden/tests/integration/operations/decorators/asmop.rs @@ -1,6 +1,6 @@ use crate::build_debug_test; use processor::{AsmOpInfo, VmStateIterator}; -use vm_core::{Felt, Operation}; +use vm_core::{AssemblyOp, Felt, Operation}; #[test] fn asmop_one_span_block_test() { @@ -20,22 +20,34 @@ fn asmop_one_span_block_test() { }, VmStatePartial { clk: 2, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 2, "push.1".to_string(), false), + 1, + )), op: Some(Operation::Pad), }, VmStatePartial { clk: 3, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 2)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 2, "push.1".to_string(), false), + 2, + )), op: Some(Operation::Incr), }, VmStatePartial { clk: 4, - asmop: Some(AsmOpInfo::new("push.2".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "push.2".to_string(), false), + 1, + )), op: Some(Operation::Push(Felt::new(2))), }, VmStatePartial { clk: 5, - asmop: Some(AsmOpInfo::new("add".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "add".to_string(), false), + 1, + )), op: Some(Operation::Add), }, VmStatePartial { @@ -66,22 +78,34 @@ fn asmop_with_one_procedure() { }, VmStatePartial { clk: 2, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("foo".to_string(), 2, "push.1".to_string(), false), + 1, + )), op: Some(Operation::Pad), }, VmStatePartial { clk: 3, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 2)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("foo".to_string(), 2, "push.1".to_string(), false), + 2, + )), op: Some(Operation::Incr), }, VmStatePartial { clk: 4, - asmop: Some(AsmOpInfo::new("push.2".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("foo".to_string(), 1, "push.2".to_string(), false), + 1, + )), op: Some(Operation::Push(Felt::new(2))), }, VmStatePartial { clk: 5, - asmop: Some(AsmOpInfo::new("add".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("foo".to_string(), 1, "add".to_string(), false), + 1, + )), op: Some(Operation::Add), }, VmStatePartial { @@ -116,62 +140,98 @@ fn asmop_repeat_test() { }, VmStatePartial { clk: 2, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 2, "push.1".to_string(), false), + 1, + )), op: Some(Operation::Pad), }, VmStatePartial { clk: 3, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 2)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 2, "push.1".to_string(), false), + 2, + )), op: Some(Operation::Incr), }, VmStatePartial { clk: 4, - asmop: Some(AsmOpInfo::new("push.2".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "push.2".to_string(), false), + 1, + )), op: Some(Operation::Push(Felt::new(2))), }, VmStatePartial { clk: 5, - asmop: Some(AsmOpInfo::new("add".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "add".to_string(), false), + 1, + )), op: Some(Operation::Add), }, VmStatePartial { clk: 6, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 2, "push.1".to_string(), false), + 1, + )), op: Some(Operation::Pad), }, VmStatePartial { clk: 7, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 2)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 2, "push.1".to_string(), false), + 2, + )), op: Some(Operation::Incr), }, VmStatePartial { clk: 8, - asmop: Some(AsmOpInfo::new("push.2".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "push.2".to_string(), false), + 1, + )), op: Some(Operation::Push(Felt::new(2))), }, VmStatePartial { clk: 9, - asmop: Some(AsmOpInfo::new("add".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "add".to_string(), false), + 1, + )), op: Some(Operation::Add), }, VmStatePartial { clk: 10, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 2, "push.1".to_string(), false), + 1, + )), op: Some(Operation::Pad), }, VmStatePartial { clk: 11, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 2)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 2, "push.1".to_string(), false), + 2, + )), op: Some(Operation::Incr), }, VmStatePartial { clk: 12, - asmop: Some(AsmOpInfo::new("push.2".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "push.2".to_string(), false), + 1, + )), op: Some(Operation::Push(Felt::new(2))), }, VmStatePartial { clk: 13, - asmop: Some(AsmOpInfo::new("add".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "add".to_string(), false), + 1, + )), op: Some(Operation::Add), }, VmStatePartial { @@ -231,7 +291,10 @@ fn asmop_conditional_execution_test() { }, VmStatePartial { clk: 3, - asmop: Some(AsmOpInfo::new("eq".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "eq".to_string(), false), + 1, + )), op: Some(Operation::Eq), }, VmStatePartial { @@ -251,22 +314,34 @@ fn asmop_conditional_execution_test() { }, VmStatePartial { clk: 7, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 2, "push.1".to_string(), false), + 1, + )), op: Some(Operation::Pad), }, VmStatePartial { clk: 8, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 2)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 2, "push.1".to_string(), false), + 2, + )), op: Some(Operation::Incr), }, VmStatePartial { clk: 9, - asmop: Some(AsmOpInfo::new("push.2".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "push.2".to_string(), false), + 1, + )), op: Some(Operation::Push(Felt::new(2))), }, VmStatePartial { clk: 10, - asmop: Some(AsmOpInfo::new("add".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "add".to_string(), false), + 1, + )), op: Some(Operation::Add), }, VmStatePartial { @@ -309,7 +384,10 @@ fn asmop_conditional_execution_test() { }, VmStatePartial { clk: 3, - asmop: Some(AsmOpInfo::new("eq".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "eq".to_string(), false), + 1, + )), op: Some(Operation::Eq), }, VmStatePartial { @@ -329,17 +407,26 @@ fn asmop_conditional_execution_test() { }, VmStatePartial { clk: 7, - asmop: Some(AsmOpInfo::new("push.3".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "push.3".to_string(), false), + 1, + )), op: Some(Operation::Push(Felt::new(3))), }, VmStatePartial { clk: 8, - asmop: Some(AsmOpInfo::new("push.4".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "push.4".to_string(), false), + 1, + )), op: Some(Operation::Push(Felt::new(4))), }, VmStatePartial { clk: 9, - asmop: Some(AsmOpInfo::new("add".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new( + AssemblyOp::new("#main".to_string(), 1, "add".to_string(), false), + 1, + )), op: Some(Operation::Add), }, VmStatePartial { diff --git a/processor/src/debug.rs b/processor/src/debug.rs index a6475eb821..11c7d6541e 100644 --- a/processor/src/debug.rs +++ b/processor/src/debug.rs @@ -3,7 +3,10 @@ use crate::{ System, Vec, }; use core::fmt; -use vm_core::{utils::string::String, Operation, StackOutputs, Word}; +use vm_core::{ + utils::string::{String, ToString}, + AssemblyOp, Operation, StackOutputs, Word, +}; /// VmState holds a current process state information at a specific clock cycle. #[derive(Clone, Debug, Eq, PartialEq)] @@ -24,8 +27,17 @@ impl fmt::Display for VmState { self.memory.iter().map(|x| (x.0, word_to_ints(&x.1))).collect(); write!( f, - "clk={}, op={:?}, asmop={:?}, fmp={}, stack={stack:?}, memory={memory:?}", - self.clk, self.op, self.asmop, self.fmp + "clk={}{}{}, fmp={}, stack={stack:?}, memory={memory:?}", + self.clk, + match self.op { + Some(op) => format!(", op={op}"), + None => "".to_string(), + }, + match &self.asmop { + Some(op) => format!(", {op}"), + None => "".to_string(), + }, + self.fmp ) } } @@ -75,19 +87,21 @@ impl VmStateIterator { // keeps track of the next assembly op in the list. It's the same as the current asmop // when the current asmop is last in the list - let next_asmop = if self.asmop_idx < assembly_ops.len() { + let next_asmop = if self.forward && self.asmop_idx < assembly_ops.len() { &assembly_ops[self.asmop_idx] } else { - &assembly_ops[self.asmop_idx - 1] + &assembly_ops[self.asmop_idx.saturating_sub(1)] }; // keeps track of the current assembly op in the list. It's the same as the next asmop // when the clock cycle is less than the clock cycle of the first asmop. let (curr_asmop, cycle_idx) = if self.asmop_idx > 0 { + let a = self.clk; + let b = assembly_ops[self.asmop_idx - 1].0 as u32; ( &assembly_ops[self.asmop_idx - 1], // difference between current clock cycle and start clock cycle of the current asmop - (self.clk - assembly_ops[self.asmop_idx - 1].0 as u32) as u8, + (a.max(b) - a.min(b)) as u8, ) } else { (next_asmop, 0) //dummy value, never used. @@ -96,23 +110,18 @@ impl VmStateIterator { // if this is the first op in the sequence corresponding to the next asmop, returns a new // instance of [AsmOp] instantiated with next asmop, num_cycles and cycle_idx of 1. if next_asmop.0 as u32 == self.clk - 1 { - let asmop = Some(AsmOpInfo::new( - next_asmop.1.op().clone(), - next_asmop.1.num_cycles(), - 1, // cycle_idx starts at 1 instead of 0 to remove ambiguity - )); - (asmop, true) + // cycle_idx starts at 1 instead of 0 to remove ambiguity + let cycle_idx = 1; + let asmop = AsmOpInfo::new(next_asmop.1.clone(), cycle_idx); + (Some(asmop), true) } // if this is not the first asmop in the list and if this op is part of current asmop, // returns a new instance of [AsmOp] instantiated with current asmop, num_cycles and // cycle_idx of current op. else if self.asmop_idx > 0 && cycle_idx <= curr_asmop.1.num_cycles() { - let asmop = Some(AsmOpInfo::new( - curr_asmop.1.op().clone(), - curr_asmop.1.num_cycles(), - cycle_idx, /* diff between curr clock cycle and start clock cycle of the current asmop */ - )); - (asmop, false) + // diff between curr clock cycle and start clock cycle of the current asmop + let asmop = AsmOpInfo::new(curr_asmop.1.clone(), cycle_idx); + (Some(asmop), false) } // if the next asmop is the first in the list and is at a greater than current clock cycle // or if the current op is not a part of any asmop, return None. @@ -120,26 +129,16 @@ impl VmStateIterator { (None, false) } } -} - -impl Iterator for VmStateIterator { - type Item = Result; - fn next(&mut self) -> Option { - if self.clk > self.system.clk() { - match &self.error { - Some(_) => { - let error = core::mem::take(&mut self.error); - return Some(Err(error.unwrap())); - } - None => return None, - } + pub fn back(&mut self) -> Option { + if self.clk == 0 { + return None; } - // if we are changing iteration directions we must increment the clk counter - if !self.forward && self.clk < self.system.clk() { - self.clk += 1; - self.forward = true; + // if we are changing directions we must decrement the clk counter. + if self.forward { + self.clk = self.clk.saturating_sub(1); + self.forward = false; } let ctx = self.system.get_ctx_at(self.clk); @@ -152,10 +151,10 @@ impl Iterator for VmStateIterator { let (asmop, is_start) = self.get_asmop(); if is_start { - self.asmop_idx += 1; + self.asmop_idx -= 1; } - let result = Some(Ok(VmState { + let result = Some(VmState { clk: self.clk, ctx, op, @@ -163,26 +162,32 @@ impl Iterator for VmStateIterator { fmp: self.system.get_fmp_at(self.clk), stack: self.stack.get_state_at(self.clk), memory: self.chiplets.get_mem_state_at(ctx, self.clk), - })); + }); - self.clk += 1; + self.clk -= 1; result } } -impl DoubleEndedIterator for VmStateIterator { - fn next_back(&mut self) -> Option { - if self.clk == 0 { - return None; - } +impl Iterator for VmStateIterator { + type Item = Result; - self.clk -= 1; + fn next(&mut self) -> Option { + if self.clk > self.system.clk() { + match &self.error { + Some(_) => { + let error = core::mem::take(&mut self.error); + return Some(Err(error.unwrap())); + } + None => return None, + } + } - // if we are changing directions we must decrement the clk counter. - if self.forward && self.clk > 0 { - self.clk -= 1; - self.forward = false; + // if we are changing iteration directions we must increment the clk counter + if !self.forward && self.clk < self.system.clk() { + self.clk += 1; + self.forward = true; } let ctx = self.system.get_ctx_at(self.clk); @@ -195,10 +200,10 @@ impl DoubleEndedIterator for VmStateIterator { let (asmop, is_start) = self.get_asmop(); if is_start { - self.asmop_idx -= 1; + self.asmop_idx += 1; } - Some(Ok(VmState { + let result = Some(Ok(VmState { clk: self.clk, ctx, op, @@ -206,7 +211,11 @@ impl DoubleEndedIterator for VmStateIterator { fmp: self.system.get_fmp_at(self.clk), stack: self.stack.get_state_at(self.clk), memory: self.chiplets.get_mem_state_at(ctx, self.clk), - })) + })); + + self.clk += 1; + + result } } @@ -220,8 +229,7 @@ fn word_to_ints(word: &Word) -> [u64; 4] { /// AsmOp decorator. This index starts from 1 instead of 0. #[derive(Clone, Debug, Eq, PartialEq)] pub struct AsmOpInfo { - op: String, - num_cycles: u8, + asmop: AssemblyOp, cycle_idx: u8, } @@ -232,33 +240,34 @@ impl AsmOpInfo { /// Returns [AsmOpInfo] instantiated with the specified assembly instruction string, number of /// cycles it takes to execute the assembly instruction and op index in sequence of operations /// corresponding to the current assembly instruction. The first index is 1 instead of 0. - pub fn new(op: String, num_cycles: u8, cycle_idx: u8) -> Self { - Self { - op, - num_cycles, - cycle_idx, - } + pub fn new(asmop: AssemblyOp, cycle_idx: u8) -> Self { + Self { asmop, cycle_idx } + } + + /// Returns the context name for this operation. + pub fn context_name(&self) -> &str { + self.asmop.context_name() } /// Returns the assembly instruction corresponding to this state. - pub fn op(&self) -> &String { - &self.op + pub fn op(&self) -> &str { + self.asmop.op() } /// Returns the gerneralized form of assembly instruction corresponding to this state. pub fn op_generalized(&self) -> String { - let op_vec: Vec<&str> = self.op.split('.').collect(); + let op_vec: Vec<&str> = self.op().split('.').collect(); let keep_params = matches!(op_vec[0], "movdn" | "movup"); if !keep_params && op_vec.last().unwrap().parse::().is_ok() { op_vec.split_last().unwrap().1.join(".") } else { - self.op.clone() + self.op().to_string() } } /// Returns the number of VM cycles taken to execute the assembly instruction. pub fn num_cycles(&self) -> u8 { - self.num_cycles + self.asmop.num_cycles() } /// Returns the operation index of the operation at the specified clock cycle in the sequence @@ -266,4 +275,15 @@ impl AsmOpInfo { pub fn cycle_idx(&self) -> u8 { self.cycle_idx } + + /// Returns `true` if the debug should break for this line. + pub const fn should_break(&self) -> bool { + self.asmop.should_break() + } +} + +impl fmt::Display for AsmOpInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}, cycles={}", self.asmop, self.cycle_idx) + } } diff --git a/processor/src/lib.rs b/processor/src/lib.rs index 21b95bb879..140feff136 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -12,7 +12,8 @@ pub use vm_core::{ }, errors::InputError, utils::DeserializationError, - Kernel, Operation, Program, ProgramInfo, QuadExtension, StackInputs, StackOutputs, Word, + AssemblyOp, Kernel, Operation, Program, ProgramInfo, QuadExtension, StackInputs, StackOutputs, + Word, }; use vm_core::{ code_blocks::{ From ddfd165d4596feb70d8b4d312fa68f214f329886 Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Tue, 14 Mar 2023 15:04:49 +0100 Subject: [PATCH 28/47] chore: update cargo toml and makefile parameters This commit removes the mandatory optimization from cargo toml and creates makefile tasks to replace them. They will allow the user to choose when he wants a full optimization, or a partial. The full optimization is useful for release artifacts, and partial is useful for development tasks such as tests. closes #768 --- .github/workflows/ci.yml | 2 +- Cargo.toml | 11 +++++++---- Makefile | 12 ++++++++++-- docs/src/intro/usage.md | 19 ++++++++++++------- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dbcdc42037..571e57a0a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: matrix: toolchain: [stable, nightly] os: [ubuntu] - args: [--release, --doc] + args: [--profile test-release, --profile test-release --doc] steps: - uses: actions/checkout@main - name: Install rust diff --git a/Cargo.toml b/Cargo.toml index 4d3d78d8bf..7fd88d55d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,10 +10,13 @@ members = [ "verifier" ] -[profile.release] +[profile.optimized] +inherits = "release" codegen-units = 1 lto = true -[profile.bench] -codegen-units = 1 -lto = true +[profile.test-release] +inherits = "release" +debug = true +debug-assertions = true +overflow-checks = true diff --git a/Makefile b/Makefile index 6c9f7111e6..c65e7ac64f 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,13 @@ +FEATURES_INTERNALS=--features internals +FEATURES_CONCURRENT_EXEC=--features concurrent,executable +PROFILE_OPTIMIZED=--profile optimized +PROFILE_TEST=--profile test-release + +bench: + cargo bench $(PROFILE_OPTIMIZED) + exec: - cargo build --release --features concurrent,executable + cargo build $(PROFILE_OPTIMIZED) $(FEATURES_CONCURRENT_EXEC) test: - RUSTFLAGS="-C debug-assertions -C overflow-checks -C debuginfo=2" cargo test --release + cargo test $(PROFILE_TEST) $(FEATURES_INTERNALS) diff --git a/docs/src/intro/usage.md b/docs/src/intro/usage.md index 88d34dbbd1..7af378c473 100644 --- a/docs/src/intro/usage.md +++ b/docs/src/intro/usage.md @@ -11,17 +11,22 @@ The above functionality is also exposed via the single [miden](https://crates.io ## CLI interface ### Compiling Miden VM -To compile Miden VM into a binary, you can run the following command: +To compile Miden VM into a binary, we have a [Makefile](https://www.gnu.org/software/make/manual/make.html) with the following tasks: ``` -cargo build --release --features executable +make exec ``` -This will place `miden` executable in the `./target/release` directory. - -By default, the executable will be compiled in the single-threaded mode. If you would like to enable multi-threaded proof generation, you can compile Miden VM using the following command: +This will place an optimized, multi-threaded `miden` executable in the `./target/release` directory. It is equivalent to executing: ``` -cargo build --release --features "executable concurrent" +cargo build --profile optimized --features concurrent,executable +``` +If you would like to enable single-threaded mode, you can compile Miden Vm using the following command: +``` +cargo build --profile optimized --features executable +``` +For a faster build, you can compile with less optimizations, replacing `--profile optimized` by `--release`. Example: +``` +cargo build --release --features concurrent,executable ``` - ### Controlling parallelism Internally, Miden VM uses [rayon](https://github.com/rayon-rs/rayon) for parallel computations. To control the number of threads used to generate a STARK proof, you can use `RAYON_NUM_THREADS` environment variable. From 641606e80c91b9f5d9686854278a3da2d38b8b88 Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Mon, 20 Mar 2023 21:18:01 +0100 Subject: [PATCH 29/47] refactor: rename advice tape and values map to stack and map related issue: #774 --- assembly/src/assembler/instruction/adv_ops.rs | 4 +- .../src/assembler/instruction/crypto_ops.rs | 6 +- assembly/src/lib.rs | 2 +- core/Cargo.toml | 3 +- core/src/operations/decorators/advice.rs | 25 ++++--- core/src/operations/decorators/mod.rs | 6 +- core/src/operations/mod.rs | 8 +- docs/src/design/stack/io_ops.md | 4 +- docs/src/intro/overview.md | 8 +- .../assembly/cryptographic_operations.md | 2 +- docs/src/user_docs/assembly/io_operations.md | 10 +-- docs/src/user_docs/assembly/overview.md | 22 ------ miden/README.md | 2 +- miden/src/cli/data.rs | 10 +-- miden/tests/integration/main.rs | 26 +++---- .../operations/decorators/advice.rs | 20 ++--- .../integration/operations/io_ops/adv_ops.rs | 26 +++---- .../integration/operations/io_ops/mod.rs | 8 +- .../integration/stdlib/crypto/fri/mod.rs | 40 +++++----- .../stdlib/crypto/fri/verifier_fri_e2f4.rs | 10 +-- processor/src/advice/inputs.rs | 40 +++++----- processor/src/advice/mem_provider.rs | 64 +++++++++------- processor/src/advice/merkle_set.rs | 4 +- processor/src/advice/mod.rs | 75 ++++++++++--------- processor/src/advice/source.rs | 12 +-- processor/src/decorators/mod.rs | 70 +++++++++-------- processor/src/errors.rs | 6 +- processor/src/operations/crypto_ops.rs | 2 +- processor/src/operations/io_ops.rs | 59 ++++++--------- processor/src/operations/mod.rs | 6 +- stdlib/asm/math/u64.masm | 18 ++--- 31 files changed, 290 insertions(+), 308 deletions(-) delete mode 100644 docs/src/user_docs/assembly/overview.md diff --git a/assembly/src/assembler/instruction/adv_ops.rs b/assembly/src/assembler/instruction/adv_ops.rs index 1ff871fd60..05eecc6084 100644 --- a/assembly/src/assembler/instruction/adv_ops.rs +++ b/assembly/src/assembler/instruction/adv_ops.rs @@ -6,8 +6,8 @@ use vm_core::{code_blocks::CodeBlock, Operation::*}; // ================================================================================================ /// Appends the number of READ operations specified by the instruction's immediate value to the -/// span. This removes the specified number of elements from the advice tape and pushes them onto -/// the stack. +/// span. This pops the specified number of elements from the advice stack and pushes them onto the +/// operand stack. /// /// # Errors /// Returns an error if the specified number of values to pushed is smaller than 1 or greater diff --git a/assembly/src/assembler/instruction/crypto_ops.rs b/assembly/src/assembler/instruction/crypto_ops.rs index 9b49a9e8ad..60da913a13 100644 --- a/assembly/src/assembler/instruction/crypto_ops.rs +++ b/assembly/src/assembler/instruction/crypto_ops.rs @@ -113,7 +113,7 @@ pub(super) fn hmerge(span: &mut SpanBuilder) -> Result, Assemb /// This operation takes 9 VM cycles. pub(super) fn mtree_get(span: &mut SpanBuilder) -> Result, AssemblyError> { // stack: [d, i, R, ...] - // inject the node value we're looking for at the head of the advice tape + // pops the value of the node we are looking for from the advice stack read_mtree_node(span); #[rustfmt::skip] let ops = [ @@ -194,10 +194,10 @@ fn read_mtree_node(span: &mut SpanBuilder) { // the following format: [d, i, R], whereas in the case of `mtree.set` and `mtree.cwm` we // would also have the new node value post the tree root: [d, i, R, V_new]. // - // inject the node value we're looking for at the head of the advice tape. + // pops the value of the node we are looking for from the advice stack span.push_decorator(Decorator::Advice(AdviceInjector::MerkleNode)); - // read old node value from advice tape => MPVERIFY: [V_old, d, i, R, ...] + // pops the old node value from advice the stack => MPVERIFY: [V_old, d, i, R, ...] // MRUPDATE: [V_old, d, i, R, V_new, ...] span.push_op_many(Read, 4); } diff --git a/assembly/src/lib.rs b/assembly/src/lib.rs index a3d1f8e6b4..0c7d993c32 100644 --- a/assembly/src/lib.rs +++ b/assembly/src/lib.rs @@ -54,7 +54,7 @@ const MODULE_PATH_DELIM: &str = "::"; /// The maximum number of constant inputs allowed for the `push` instruction. const MAX_PUSH_INPUTS: usize = 16; -/// The maximum number of elements that can be read from the advice tape in a single `adv_push` +/// The maximum number of elements that can be popped from the advice stack in a single `adv_push` /// instruction. const ADVICE_READ_LIMIT: u8 = 16; diff --git a/core/Cargo.toml b/core/Cargo.toml index b63f816f84..41e3301fbf 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -21,7 +21,8 @@ std = ["math/std", "winter-utils/std"] [dependencies] math = { package = "winter-math", version = "0.5", default-features = false } -crypto = { package = "miden-crypto", version = "0.1", default-features = false } +#crypto = { package = "miden-crypto", version = "0.1", default-features = false } +crypto = { package = "miden-crypto", git = "https://github.com/0xPolygonMiden/crypto", branch = "next", default-features = false } winter-utils = { package = "winter-utils", version = "0.5", default-features = false } [dev-dependencies] diff --git a/core/src/operations/decorators/advice.rs b/core/src/operations/decorators/advice.rs index 18285dcc76..3e583b272f 100644 --- a/core/src/operations/decorators/advice.rs +++ b/core/src/operations/decorators/advice.rs @@ -3,37 +3,38 @@ use core::fmt; /// TODO: add docs #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum AdviceInjector { - /// Injects a node of the Merkle tree specified by the values on the stack at the head of the - /// advice tape. The stack is expected to be arranged as follows (from the top): + /// Pushes a node of the Merkle tree specified by the values on the top of the operand stack + /// onto the advice stack. The operand stack is expected to be arranged as follows (from the + /// top): /// - depth of the node, 1 element /// - index of the node, 1 element /// - root of the tree, 4 elements MerkleNode, - /// Injects the result of u64 division (both the quotient and the remainder) at the head of - /// the advice tape. The stack is expected to be arranged as follows (from the top): + /// Pushes the result of [u64] division (both the quotient and the remainder) onto the advice + /// stack. The operand stack is expected to be arranged as follows (from the top): /// - divisor split into two 32-bit elements /// - dividend split into two 32-bit elements /// - /// The result is injected into the advice tape as follows: first the remainder is injected, - /// then the quotient is injected. + /// The result is pushed onto the advice stack as follows: first the remainder is pushed, + /// then the quotient. DivResultU64, - /// Injects a list of field elements at the front of the advice tape. The list is looked up in - /// the key-value map maintained by the advice provider using the top 4 elements on the stack - /// as the key. + /// Pushes a list of field elements onto the advice stack. The list is looked up in the + /// key-value map maintained by the advice provider using the top 4 elements of the operand + /// stack as key. MapValue, - /// Injects a list of words from the memory starting from the specified start address. + /// Pushes a list of words from the memory starting from the specified address. Memory(u32, u32), /// Given an element of quadratic extension field, it computes multiplicative inverse and - /// injects the result into advice tape. + /// push the result into the advice stack. Ext2Inv, /// Given ( power of 2 many ) evaluations of a polynomial over some specified domain, this /// routine interpolates ( using inverse NTT ) the evaluations into a polynomial in - /// coefficient form and injects the result into the advice tape. + /// coefficient form and pushes the result into the advice stack. Ext2INTT, } diff --git a/core/src/operations/decorators/mod.rs b/core/src/operations/decorators/mod.rs index 328e505302..f34a015173 100644 --- a/core/src/operations/decorators/mod.rs +++ b/core/src/operations/decorators/mod.rs @@ -10,9 +10,9 @@ use core::fmt; #[derive(Clone, Debug, Eq, PartialEq)] pub enum Decorator { - /// Injects zero or more values at the head of the advice tape as specified by the injector. - /// This operation affects only the advice tape, but has no effect on other VM components - /// (e.g., stack, memory), and does not advance VM clock. + /// Pushes zero or more values onto the advice stack, as specified by the injector. This + /// operation affects only the advice stack and has no effect on other VM components (e.g. + /// operand stack, memory), and does not advance the VM clock. Advice(AdviceInjector), /// Adds information about the assembly instruction at a particular index /// (only applicable in debug mode) diff --git a/core/src/operations/mod.rs b/core/src/operations/mod.rs index 7f3d0e6273..2436f71496 100644 --- a/core/src/operations/mod.rs +++ b/core/src/operations/mod.rs @@ -312,10 +312,10 @@ pub enum Operation { /// Pushes the immediate value onto the stack. Push(Felt), - /// Removes the next element from the advice tape and pushes it onto the stack. + /// Removes the next element from the advice stack and pushes it onto the stack. Read, - /// Removes a word (4 elements) from the advice tape and overwrites the top four stack + /// Removes a word (4 elements) from the advice stack and overwrites the top four stack /// elements with it. ReadW, @@ -348,11 +348,11 @@ pub enum Operation { /// - All other stack elements remain the same. MStream, - /// Loads two words from the advice tape, writes them to memory, and replaces the top 8 elements + /// Pops two words from the advice stack, writes them to memory, and replaces the top 8 elements /// of the stack with them, element-wise, in stack order. /// /// The operation works as follows: - /// - Two words are read from the head of the advice tape. + /// - Two words are popped from the advice stack. /// - The destination memory address for the first word is retrieved from the 13th stack element /// (position 12). /// - The two words are written to memory consecutively, starting at this address. diff --git a/docs/src/design/stack/io_ops.md b/docs/src/design/stack/io_ops.md index a0cc4afa77..a27314509c 100644 --- a/docs/src/design/stack/io_ops.md +++ b/docs/src/design/stack/io_ops.md @@ -22,7 +22,7 @@ The effect of this operation on the rest of the stack is: * **Right shift** starting from position $0$. ### READ -Assume $a$ is an element at the head of the advice tape. The `READ` operation removes $a$ from the advice tape and pushes it onto the stack. The diagram below illustrates this graphically. +Assume $a$ is an element at the head of the advice stack. The `READ` operation removes $a$ from the advice stack and pushes it onto the stack. The diagram below illustrates this graphically. ![read](../../assets/design/stack/io_ops/READ.png) @@ -32,7 +32,7 @@ The effect of this operation on the rest of the stack is: * **Right shift** starting from position $0$. ### READW -Assume $a$, $b$, $c$, and $d$, are the elements at the head of the advice tape (with $a$ being on top). The `READW` operation removes these elements from the advice tape and puts them onto the stack by overwriting the top $4$ stack elements. The diagram below illustrates this graphically. +Assume $a$, $b$, $c$, and $d$, are the elements at the head of the advice stack (with $a$ being on top). The `READW` operation removes these elements from the advice stack and puts them onto the stack by overwriting the top $4$ stack elements. The diagram below illustrates this graphically. ![readw](../../assets/design/stack/io_ops/READW.png) diff --git a/docs/src/intro/overview.md b/docs/src/intro/overview.md index 00069fb165..6139ef5e6f 100644 --- a/docs/src/intro/overview.md +++ b/docs/src/intro/overview.md @@ -8,7 +8,7 @@ Miden VM consists of three high-level components as illustrated below. These components are: * **Stack** which is a push-down stack where each item is a field element. Most assembly instructions operate with values located on the stack. The stack can grow up to $2^{32}$ items deep, however, only the top 16 items are directly accessible. * **Memory** which is a linear random-access read-write memory. The memory is word-addressable, meaning, four elements are located at each address, and we can read and write elements to/from memory in batches of four. Memory addresses can be in the range $[0, 2^{32})$. -* **Advice provider** which is a way for the prover to provide nondeterministic inputs to the VM. The advice provider contains a single *advice tape* and unlimited number of *merkle sets*. The latter contain structured data which can be interpreted as a set of Merkle paths. +* **Advice provider** which is a way for the prover to provide nondeterministic inputs to the VM. The advice provider is composed of a single *advice stack*, an *advice map*, and a *merkle store*. The *advice stack* yields elements to the VM stack; the *advice map* stores key-mapped element lists which can be pushed onto the advice stack; finally, the Merkle store contains structured Merkle tree data and serves Merkle paths to the VM. In the future, additional components (e.g., storage, logs) may be added to the VM. @@ -25,7 +25,7 @@ External inputs can be provided to Miden VM in two ways: After a program finishes executing, the elements remaining on the stack become the outputs of the program. Since these outputs will be public inputs for the verifier, having a large stack at the end of execution will increase cost to the verifier. Therefore, it's best to drop unneeded output values. We've provided the [`truncate_stack`](../user_docs/stdlib/sys.md) utility function in the standard library for this purpose. -The number of public inputs and outputs of a program can be reduced by making use of the advice tape and Merkle trees. Just 4 elements are sufficient to represent a root of a Merkle tree, which can be expanded into an arbitrary number of values. +The number of public inputs and outputs of a program can be reduced by making use of the advice stack and Merkle trees. Just 4 elements are sufficient to represent a root of a Merkle tree, which can be expanded into an arbitrary number of values. For example, if we wanted to provide a thousand public input values to the VM, we could put these values into a Merkle tree, initialize the stack with the root of this tree, initialize the advice provider with the tree itself, and then retrieve values from the tree during program execution using `mtree_get` instruction (described [here](../user_docs/assembly/cryptographic_operations.md#hashing-and-merkle-trees)). @@ -41,8 +41,8 @@ For reasons explained [here](../design/stack/main.md), the VM imposes the restri The *advice provider* component is responsible for supplying nondeterministic inputs to the VM. These inputs only need to be known to the prover (i.e., they do not need to be shared with the verifier). The advice provider consists of three components: -* **Advice tape** which is a one-dimensional array of values. The VM can access only the head of the tape. That is the VM can either remove values from the head of the tape or inject new values at the head of the tape. Formally, this means that the advice tape is actually a stack. -* **Advice map** which is a key-value map where keys are words and values are vectors of field elements. The VM can copy values from the advice map onto the advice tape as well as insert new values into the advice map (e.g., from a region of memory). +* **Advice stack** which is a one-dimensional array of field elements. Being a stack, the VM can either push new elements onto the advice stack, or pop the elements from its top. +* **Advice map** which is a key-value map where keys are words and values are vectors of field elements. The VM can copy values from the advice map onto the advice stack as well as insert new values into the advice map (e.g., from a region of memory). * **Merkle sets** which contain structured data reducible to Merkle paths. Some examples of merkle sets are: Merkle tree, Sparse Merkle tree, a collection of Merkle paths. Every merkle set can be uniquely identified by its root. The VM can request Merkle paths from an merkle set, as well as update an merkle set by modifying one of its nodes (this will also change the root of the modified merkle set). The prover initializes the advice provider prior to executing a program, and from that point on the advice provider is manipulated solely by executing operations on the VM. diff --git a/docs/src/user_docs/assembly/cryptographic_operations.md b/docs/src/user_docs/assembly/cryptographic_operations.md index 70afcb9b68..048fccc30e 100644 --- a/docs/src/user_docs/assembly/cryptographic_operations.md +++ b/docs/src/user_docs/assembly/cryptographic_operations.md @@ -10,5 +10,5 @@ Miden assembly provides a set of instructions for performing common cryptographi | hperm
- *(1 cycle)* | [C, B, A, ...] | [F, E, D, ...] | $\{D, E, F\} \leftarrow permute(A, B, C)$
where, $permute()$ computes a Rescue Prime Optimized permutation. | | hmerge
- *(16 cycles)* | [B, A, ...] | [C, ...] | $C \leftarrow hash(A,B)$
where, $hash()$ computes a 2-to-1 Rescue Prime Optimized hash. | | mtree_get
- *(9 cycles)* | [d, i, R, ...] | [V, R, ...] | Verifies that a Merkle tree with root $R$ opens to node $V$ at depth $d$ and index $i$. Merkle tree with root $R$ must be present in the advice provider, otherwise execution fails. | -| mtree_set
- *(29 cycles)* | [d, i, R, V', ...] | [V, R', ...] | Updates a node in the Merkle tree with root $R$ at depth $d$ and index $i$ to value $V'$. $R'$ is the Merkle root of the resulting tree and $V$ is old value of the node. Merkle tree with root $R$ must be present in the advice provider, otherwise execution fails. At the end of the operation Merkle tree in the advice provider with root $R$ is replaced with the Merkle tree with root $R'$. | +| mtree_set
- *(29 cycles)* | [d, i, R, V', ...] | [V, R', ...] | Updates a node in the Merkle tree with root $R$ at depth $d$ and index $i$ to value $V'$. $R'$ is the Merkle root of the resulting tree and $V$ is old value of the node. Merkle tree with root $R$ must be present in the advice provider, otherwise execution fails. At the end of the operation the advice provider will contain both Merkle trees. | | mtree_cwm
- *(29 cycles)* | [d, i, R, V', ...] | [V, R', ...] | Copies a Merkle tree with root $R$ and updates a node at depth $d$ and index $i$ in the copied tree to value $V'$. $R'$ is the Merkle root of the new tree and $V$ is old value of the node. Merkle tree with root $R$ must be present in the advice provider, otherwise execution fails. At the end of the operation the advice provider will contain both Merkle trees. | diff --git a/docs/src/user_docs/assembly/io_operations.md b/docs/src/user_docs/assembly/io_operations.md index ef00f85021..54b590254b 100644 --- a/docs/src/user_docs/assembly/io_operations.md +++ b/docs/src/user_docs/assembly/io_operations.md @@ -3,7 +3,7 @@ Miden assembly provides a set of instructions for moving data between the stack * **Program code**: values to be moved onto the stack can be hard-coded in a program's source code. * **Environment**: values can be moved onto the stack from environment variables. Currently, the available environment variables are *stack_depth*, which holds the current depth of the stack, and *local_address*, which stores absolute addresses of local variables. In the future, other environment variables may be added. -* **Advice tape**: values can be moved onto the stack from a non-deterministic advice tape contained within the advice provider. Values are always read from the head of the advice tape, and once a value is read, it is removed from the tape. There is no limit on the number of values in the advice tape. +* **Advice stack**: values can be moved onto the stack from the advice provider by popping the from the advice stack. There is no limit on the number of values in the advice stack. * **Memory**: values can be moved between the stack and random-access memory. The memory is word-addressable, meaning, four elements are located at each address, and we can read and write elements to/from memory in batches of four. Memory can be accessed via absolute memory references (i.e., via memory addresses) as well as via local procedure references (i.e., local index). The latter approach ensures that a procedure does not access locals of another procedure. In the future several other sources such as *storage* and *logs* may be added. @@ -35,11 +35,11 @@ In both case the values must still encode valid field elements. | Instruction | Stack_input | Stack_output | Notes | | --------------- | ----------- | ------------ | ------------------------------------------ | -| adv_push.*n*
- *(n cycles)* | [ ... ] | [a, ... ] | $a \leftarrow tape.next()$
Removes the next $n$ values from advice tape and pushes them onto the stack. Valid for $n \in \{1, ..., 16\}$.
Fails if the advice tape has fewer than $n$ values. | -| adv_loadw
- *(1 cycle)* | [0, 0, 0, 0, ... ] | [A, ... ] | $A \leftarrow tape.next(4)$
Removes the next word (4 elements) from the advice tape and overwrites the top four stack elements with it.
Fails if the advice tape has fewer than $4$ values. | -| adv_pipe
- *(2 cycles)* | [S2, S1, S0, a, ... ] | [T2, T1, T0, b, ... ] | $[T_0, T_1, T_2] \leftarrow permute(S_0, tape.next(4), tape.next(4))$
$b \leftarrow a + 2$
Removes the next two words (8 elements) from the advice tape, inserts them into memory sequentially starting from address $a$, then overwrites the top 8 elements of the stack with them, and applies a Rescue Prime Optimized permutation to the top 12 elements of the stack. At the end of the operation, the address is incremented by $2$.
Fails if the advice tape has fewer than $8$ values. | +| adv_push.*n*
- *(n cycles)* | [ ... ] | [a, ... ] | $a \leftarrow stack.pop()$
Pops $n$ values from the advice stack and pushes them onto the operand stack. Valid for $n \in \{1, ..., 16\}$.
Fails if the advice stack has fewer than $n$ values. | +| adv_loadw
- *(1 cycle)* | [0, 0, 0, 0, ... ] | [A, ... ] | $A \leftarrow stack.pop(4)$
Pop the next word (4 elements) from the advice stack and pushes them onto the operand stack.
Fails if the advice stack has fewer than $4$ values. | +| adv_pipe
- *(2 cycles)* | [S2, S1, S0, a, ... ] | [T2, T1, T0, b, ... ] | $[T_0, T_1, T_2] \leftarrow permute(S_0, stack.pop(4), stack.pop(4))$
$b \leftarrow a + 2$
Pops the next two words (8 elements) from the advice stack, inserts them into memory at address $a$ sequentially, overwrites these top 8 elements onto the operand stack, and performs a Rescue Prime Optimized permutation to the top 12 elements of the operand stack. At the end of the operation, the address is incremented by $2$.
Fails if the advice stack has fewer than $8$ values. | -> **Note**: The opcodes above always push data onto the stack so that the first element is placed deepest in the stack. For example, if the data on the tape is `a,b,c,d` and you use the opcode `adv_push.4`, the data will be `d,c,b,a` on your stack. This is also the behavior of the other opcodes. +> **Note**: The opcodes above always push data onto the operand stack so that the first element is placed deepest in the stack. For example, if the data on the stack is `a,b,c,d` and you use the opcode `adv_push.4`, the data will be `d,c,b,a` on your stack. This is also the behavior of the other opcodes. ### Random access memory diff --git a/docs/src/user_docs/assembly/overview.md b/docs/src/user_docs/assembly/overview.md deleted file mode 100644 index 39a9554317..0000000000 --- a/docs/src/user_docs/assembly/overview.md +++ /dev/null @@ -1,22 +0,0 @@ -## Miden VM overview -Miden VM is a stack machine. The base data type of the MV is a field element in a 64-bit [prime field](https://en.wikipedia.org/wiki/Finite_field) defined by modulus $p = 2^{64} - 2^{32} + 1$. This means that all values that the VM operates with are field elements in this field (i.e., values between $0$ and $2^{64} - 2^{32}$, both inclusive). - -Throughout this document, we use lower-case letters to refer to individual field elements (e.g., $a$). Sometimes it is convenient to describe operations over groups of elements. For these purposes we define a *word* to be a group of four elements. We use upper-case letters to refer to words (e.g., $A$). To refer to individual elements within a word, we use numerical subscripts. For example, $a_0$ is the first element of word $A$, $b_3$ is the last element of word $B$, etc. - -Miden VM consists of three high-level components as illustrated below. - -![miden_vm_overview](../../assets/user_docs/assembly/overview/miden_vm_overview.png) - -These components are: -* **Stack** which is a push-down stack where each item is a field element. Most assembly instructions operate with values located on the stack. The stack can grow up to $2^{16}$ items deep, however, only the top 16 items are directly accessible. -* **Memory** which is a linear random-access read-write memory. The memory is word-addressable, meaning, four elements are located at each address, and we can read and write elements to/from memory in batches of four. Memory addresses can be in the range $[0, 2^{32})$. -* **Advice provider** which is a way for the prover to provide non-deterministic inputs to the VM. The advice provider contains a single *advice tape* and unlimited number of *merkle sets*. The latter contain structured data which can be interpreted as a set of Merkle paths. - -In the future, additional components (e.g., storage, logs) may be added to the VM. - -### Terms and notations -In this note we use the following terms and notations: - -- $p$ is the modulus of the VM's base field which is equal to $2^{64} - 2^{32} + 1$. -- A *binary* value means a field element which is either $0$ or $1$. -- Inequality comparisons are assumed to be performed on integer representations of field elements in the range $[0, p)$. diff --git a/miden/README.md b/miden/README.md index 63244ab4a2..9efe8ec4c8 100644 --- a/miden/README.md +++ b/miden/README.md @@ -17,7 +17,7 @@ Currently, there are 3 ways to get values onto the stack: 1. You can use `push` instruction to push values onto the stack. These values become a part of the program itself, and, therefore, cannot be changed between program executions. You can think of them as constants. 2. The stack can be initialized to some set of values at the beginning of the program. These inputs are public and must be shared with the verifier for them to verify a proof of the correct execution of a Miden program. The number of elements at the top of the stack which can receive an initial value is limited to 16. -3. The program may request nondeterministic advice inputs from the prover. These inputs are secret inputs. This means that the prover does not need to share them with the verifier. There are two types of advice inputs: (1) a single advice tape which can contain any number of elements and (2) a list of merkle sets, which are used to provide nondeterministic inputs for instructions which work with Merkle trees. There are no restrictions on the number of advice inputs a program can request. +3. The program may request nondeterministic advice inputs from the prover. These inputs are secret inputs. This means that the prover does not need to share them with the verifier. There are three types of advice inputs: (1) a single advice stack which can contain any number of elements; (2) a key-mapped element lists which can be pushed onto the advice stack; (3) a Merkle store, which is used to provide nondeterministic inputs for instructions which work with Merkle trees. There are no restrictions on the number of advice inputs a program can request. The stack is provided to Miden VM via `StackInputs` struct. These are public inputs of the execution, and should also be provided to the verifier. The secret inputs of the program are provided via `AdviceProvider` instances. There is one in-memory advice provider that can be commonly used for operations that won't require persistence: `MemAdviceProvider`. diff --git a/miden/src/cli/data.rs b/miden/src/cli/data.rs index 7044ecd946..518a6bb542 100644 --- a/miden/src/cli/data.rs +++ b/miden/src/cli/data.rs @@ -20,7 +20,7 @@ use stdlib::StdLibrary; #[derive(Deserialize, Debug)] pub struct InputFile { pub stack_init: Vec, - pub advice_tape: Option>, + pub advice_stack: Option>, } /// Helper methods to interact with the input file @@ -31,7 +31,7 @@ impl InputFile { if !inputs_path.is_some() && !program_path.with_extension("inputs").exists() { return Ok(Self { stack_init: Vec::new(), - advice_tape: Some(Vec::new()), + advice_stack: Some(Vec::new()), }); } @@ -56,8 +56,8 @@ impl InputFile { } pub fn parse_advice_provider(&self) -> Result { - let tape = self - .advice_tape + let stack = self + .advice_stack .as_ref() .map(Vec::as_slice) .unwrap_or(&[]) @@ -65,7 +65,7 @@ impl InputFile { .map(|v| v.parse::().map_err(|e| e.to_string())) .collect::, _>>()?; let advice_inputs = - AdviceInputs::default().with_tape_values(tape).map_err(|e| e.to_string())?; + AdviceInputs::default().with_stack_values(stack).map_err(|e| e.to_string())?; Ok(MemAdviceProvider::from(advice_inputs)) } diff --git a/miden/tests/integration/main.rs b/miden/tests/integration/main.rs index de5064b375..0593536622 100644 --- a/miden/tests/integration/main.rs +++ b/miden/tests/integration/main.rs @@ -28,12 +28,12 @@ fn multi_output_program() { /// specified stack and advice inputs. /// /// Parameters are expected in the following order: -/// `source`, `stack_inputs` (optional), `advice_tape` (optional), `merkle_sets` (optional) +/// `source`, `stack_inputs` (optional), `advice_stack` (optional), `merkle_store` (optional) /// /// * `source`: a string of one or more operations, e.g. "push.1 push.2". /// * `stack_inputs` (optional): the initial inputs which must be at the top of the stack before /// executing the `source`. Stack inputs can be provided independently without any advice inputs. -/// * `advice_tape` (optional): the initial advice tape values. When provided, `stack_inputs` and +/// * `advice_stack` (optional): the initial advice stack values. When provided, `stack_inputs` and /// `merkle_sets` are also expected. /// * `merkle_sets` (optional): the initial merkle set values. When provided, `stack_inputs` and /// `advice_tape` are also expected. @@ -53,12 +53,12 @@ macro_rules! build_op_test { /// stack and advice inputs. /// /// Parameters are expected in the following order: -/// `source`, `stack_inputs` (optional), `advice_tape` (optional), `merkle_sets` (optional) +/// `source`, `stack_inputs` (optional), `advice_stack` (optional), `merkle_store` (optional) /// /// * `source`: a well-formed source string. /// * `stack_inputs` (optional): the initial inputs which must be at the top of the stack before /// executing the `source`. Stack inputs can be provided independently without any advice inputs. -/// * `advice_tape` (optional): the initial advice tape values. When provided, `stack_inputs` and +/// * `advice_stack` (optional): the initial advice stack values. When provided, `stack_inputs` and /// `merkle_sets` are also expected. /// * `merkle_sets` (optional): the initial merkle set values. When provided, `stack_inputs` and /// `advice_tape` are also expected. @@ -73,12 +73,12 @@ macro_rules! build_test { /// and advice inputs. /// /// Parameters are expected in the following order: -/// `source`, `stack_inputs` (optional), `advice_tape` (optional), `merkle_sets` (optional) +/// `source`, `stack_inputs` (optional), `advice_stack` (optional), `merkle_store` (optional) /// /// * `source`: a well-formed source string. /// * `stack_inputs` (optional): the initial inputs which must be at the top of the stack before /// executing the `source`. Stack inputs can be provided independently without any advice inputs. -/// * `advice_tape` (optional): the initial advice tape values. When provided, `stack_inputs` and +/// * `advice_stack` (optional): the initial advice stack values. When provided, `stack_inputs` and /// `merkle_sets` are also expected. /// * `merkle_sets` (optional): the initial merkle set values. When provided, `stack_inputs` and /// `advice_tape` are also expected. @@ -117,13 +117,13 @@ macro_rules! build_test_by_mode { } }}; ( - $in_debug_mode:expr, $source:expr, $stack_inputs:expr, $advice_tape:expr, $merkle_sets:expr + $in_debug_mode:expr, $source:expr, $stack_inputs:expr, $advice_stack:expr, $merkle_sets:expr ) => {{ let stack_inputs: Vec = $stack_inputs.to_vec(); let stack_inputs = $crate::helpers::StackInputs::try_from_values(stack_inputs).unwrap(); - let tape_values: Vec = $advice_tape.to_vec(); + let stack_values: Vec = $advice_stack.to_vec(); let advice_inputs = $crate::helpers::AdviceInputs::default() - .with_tape_values(tape_values) + .with_stack_values(stack_values) .unwrap() .with_merkle_sets($merkle_sets) .unwrap(); @@ -136,16 +136,16 @@ macro_rules! build_test_by_mode { in_debug_mode: $in_debug_mode, } }}; - ($in_debug_mode:expr, $source:expr, $stack_inputs:expr, $advice_tape:expr, $advice_sets:expr, $advice_map:expr) => {{ + ($in_debug_mode:expr, $source:expr, $stack_inputs:expr, $advice_stack:expr, $advice_sets:expr, $advice_map:expr) => {{ let stack_inputs: Vec = $stack_inputs.to_vec(); let stack_inputs = $crate::helpers::StackInputs::try_from_values(stack_inputs).unwrap(); - let tape_values: Vec = $advice_tape.to_vec(); + let stack_values: Vec = $advice_stack.to_vec(); let advice_inputs = $crate::helpers::AdviceInputs::default() - .with_tape_values(tape_values) + .with_stack_values(stack_values) .unwrap() .with_merkle_sets($advice_sets) .unwrap() - .with_values_map($advice_map); + .with_map($advice_map); $crate::helpers::Test { source: String::from($source), diff --git a/miden/tests/integration/operations/decorators/advice.rs b/miden/tests/integration/operations/decorators/advice.rs index cdfcfeadda..c08af75dee 100644 --- a/miden/tests/integration/operations/decorators/advice.rs +++ b/miden/tests/integration/operations/decorators/advice.rs @@ -6,6 +6,7 @@ use rand_utils::rand_value; #[test] fn advice_inject_u64div() { + // push a/b onto the advice stack and then move these values onto the operand stack. let source = "begin adv.u64div adv_push.4 end"; // get two random 64-bit integers and split them into 32-bit limbs @@ -27,7 +28,6 @@ fn advice_inject_u64div() { let r_hi = r >> 32; let r_lo = r as u32 as u64; - // inject a/b into the advice tape and then read these values from the tape let test = build_test!(source, &[a_lo, a_hi, b_lo, b_hi]); let expected = [r_hi, r_lo, q_hi, q_lo, b_hi, b_lo, a_hi, a_lo]; test.expect_stack(&expected); @@ -36,9 +36,9 @@ fn advice_inject_u64div() { #[test] fn advice_inject_u64div_repeat() { // This procedure repeats the following steps 7 times: - // - pushes quotient and remainder to advice tape + // - pushes quotient and remainder to advice stack // - drops divisor (top 2 elements of the stack reperesenting 32 bit limbs of divisor) - // - reads quotient from advice tape to the stack + // - reads quotient from advice stack to the stack // - push 2_u64 to the stack divided into 2 32 bit limbs // Finally the first 2 elements of the stack are removed let source = "begin @@ -78,6 +78,7 @@ fn advice_inject_u64div_repeat() { #[test] fn advice_inject_u64div_local_procedure() { + // push a/b onto the advice stack and then move these values onto the operand stack. let source = "proc.foo adv.u64div adv_push.4 end begin exec.foo end"; // get two random 64-bit integers and split them into 32-bit limbs @@ -99,7 +100,6 @@ fn advice_inject_u64div_local_procedure() { let r_hi = r >> 32; let r_lo = r as u32 as u64; - // inject a/b into the advice tape and then read these values from the tape let test = build_test!(source, &[a_lo, a_hi, b_lo, b_hi]); let expected = [r_hi, r_lo, q_hi, q_lo, b_hi, b_lo, a_hi, a_lo]; test.expect_stack(&expected); @@ -138,30 +138,30 @@ fn advice_inject_mem() { # State Transition: # advice_map: k: [8, 7, 6, 5], v: [4, 3, 2, 1, 8, 7, 6, 5] - # copy from advice map to advice tape + # copy from advice map to advice stack adv.keyval dropw # State Transition: # stack: [0, 0, 0, 0] - # advice_tape: [4, 3, 2, 1, 8, 7, 6, 5] + # advice_stack: [4, 3, 2, 1, 8, 7, 6, 5] - # copy first word from advice tape to stack + # copy first word from advice stack to stack # adv_loadw copies the word to the stack with elements in the reverse order. adv_loadw # State Transition: # stack: [1, 2, 3, 4, 0, 0, 0, 0] - # advice_tape: [8, 7, 6, 5] + # advice_stack: [8, 7, 6, 5] # swap first 2 words on stack swapw # State Transition: # stack: [0, 0, 0, 0, 1, 2, 3, 4] - # copy next word from advice tape to stack + # copy next word from advice stack to stack # adv_loadw copies the word to the stack with elements in the reverse order. adv_loadw # State Transition: # stack: [5, 6, 7, 8, 1, 2, 3, 4] - # advice_tape: [] + # advice_stack: [] # swap first 2 words on stack swapw diff --git a/miden/tests/integration/operations/io_ops/adv_ops.rs b/miden/tests/integration/operations/io_ops/adv_ops.rs index 102c6a212f..0f3c5b513d 100644 --- a/miden/tests/integration/operations/io_ops/adv_ops.rs +++ b/miden/tests/integration/operations/io_ops/adv_ops.rs @@ -7,14 +7,14 @@ use vm_core::{chiplets::hasher::apply_permutation, utils::ToElements, Felt, Star #[test] fn adv_push() { let asm_op = "adv_push"; - let advice_tape = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + let advice_stack = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; let test_n = |n: usize| { let source = format!("{asm_op}.{n}"); let mut final_stack = vec![0; n]; - final_stack.copy_from_slice(&advice_tape[..n]); + final_stack.copy_from_slice(&advice_stack[..n]); final_stack.reverse(); - let test = build_op_test!(source, &[], &advice_tape, vec![]); + let test = build_op_test!(source, &[], &advice_stack, vec![]); test.expect_stack(&final_stack); }; @@ -27,9 +27,9 @@ fn adv_push() { #[test] fn adv_push_invalid() { - // attempting to read from empty advice tape should throw an error + // attempting to read from empty advice stack should throw an error let test = build_op_test!("adv_push.1"); - test.expect_error(TestError::ExecutionError("AdviceTapeReadFailed")); + test.expect_error(TestError::ExecutionError("AdviceStackReadFailed")); } // OVERWRITING VALUES ON THE STACK (LOAD) @@ -38,19 +38,19 @@ fn adv_push_invalid() { #[test] fn adv_loadw() { let asm_op = "adv_loadw"; - let advice_tape = [1, 2, 3, 4]; - let mut final_stack = advice_tape; + let advice_stack = [1, 2, 3, 4]; + let mut final_stack = advice_stack; final_stack.reverse(); - let test = build_op_test!(asm_op, &[8, 7, 6, 5], &advice_tape, vec![]); + let test = build_op_test!(asm_op, &[8, 7, 6, 5], &advice_stack, vec![]); test.expect_stack(&final_stack); } #[test] fn adv_loadw_invalid() { - // attempting to read from empty advice tape should throw an error + // attempting to read from empty advice stack should throw an error let test = build_op_test!("adv_loadw", &[0, 0, 0, 0]); - test.expect_error(TestError::ExecutionError("AdviceTapeReadFailed")); + test.expect_error(TestError::ExecutionError("AdviceStackReadFailed")); } // MOVING ELEMENTS TO MEMORY VIA THE STACK (PIPE) @@ -64,11 +64,11 @@ fn adv_pipe() { adv_pipe end"; - let advice_tape = [1, 2, 3, 4, 5, 6, 7, 8]; + let advice_stack = [1, 2, 3, 4, 5, 6, 7, 8]; // the state of the hasher is the first 12 elements of the stack (in reverse order). the state // is built by replacing the values on the top of the stack with the top 8 values from the head - // of the advice tape (i.e. values 1 through 8). Thus, the first 8 elements on the stack will be + // of the advice stack (i.e. values 1 through 8). Thus, the first 8 elements on the stack will be // 1-8 in stack order (stack[0] = 8), and the remaining 4 are untouched (i.e., 9, 10, 11, 12). let mut state: [Felt; 12] = [12_u64, 11, 10, 9, 1, 2, 3, 4, 5, 6, 7, 8].to_elements().try_into().unwrap(); @@ -82,6 +82,6 @@ fn adv_pipe() { final_stack.reverse(); final_stack.push(2); - let test = build_test!(source, &[], &advice_tape, vec![]); + let test = build_test!(source, &[], &advice_stack, vec![]); test.expect_stack(&final_stack); } diff --git a/miden/tests/integration/operations/io_ops/mod.rs b/miden/tests/integration/operations/io_ops/mod.rs index 2cafc2576c..9d03101e04 100644 --- a/miden/tests/integration/operations/io_ops/mod.rs +++ b/miden/tests/integration/operations/io_ops/mod.rs @@ -41,24 +41,24 @@ fn mem_stream_pipe() { drop end"; - let advice_tape = [1, 2, 3, 4, 5, 6, 7, 8]; + let advice_stack = [1, 2, 3, 4, 5, 6, 7, 8]; // --- different stack values should yield the same results from adv_pipe and mem_stream ------ // initialize with anything other than zeros, since the stack is set to 0s between the adv_pipe // and mem_stream operations in the source script. let stack_inputs = [1, 1, 1, 1, 1, 1, 1, 1]; - let test = build_test!(source, &stack_inputs, &advice_tape, vec![]); + let test = build_test!(source, &stack_inputs, &advice_stack, vec![]); let final_stack = test.get_last_stack_state(); assert_eq!(final_stack[0..4], final_stack[4..8]); // --- the same stack values should yield the same results from adv_pipe and mem_stream ------- // initialize with all zeros, just like between the adv_pipe and mem_stream operations above. - let test = build_test!(source, &[], &advice_tape, vec![]); + let test = build_test!(source, &[], &advice_stack, vec![]); let final_stack = test.get_last_stack_state(); assert_eq!(final_stack[0..4], final_stack[4..8]); // --- assert that the hashed output values are correct --------------------------------------- - // compute the expected result of hashing the elements in the advice tape inputs. + // compute the expected result of hashing the elements in the advice stack inputs. let mut state: [Felt; 12] = [0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8].to_elements().try_into().unwrap(); apply_permutation(&mut state); diff --git a/miden/tests/integration/stdlib/crypto/fri/mod.rs b/miden/tests/integration/stdlib/crypto/fri/mod.rs index d04cac7a33..62a1f1aae2 100644 --- a/miden/tests/integration/stdlib/crypto/fri/mod.rs +++ b/miden/tests/integration/stdlib/crypto/fri/mod.rs @@ -30,14 +30,14 @@ fn fri_fold4_ext2_remainder32() { let depth = trace_len_e + blowup_exp; let domain_size = 1 << depth; - let (advice_provider, tape, position_eval, alphas, commitments, remainder, num_queries) = + let (advice_provider, stack, position_eval, alphas, commitments, remainder, num_queries) = fri_prove_verify_fold4_ext2(trace_len_e).expect("should not panic"); - let tape = prepare_advice( + let advice_stack = prepare_advice( depth, domain_size, num_queries, - tape, + stack, position_eval, alphas, commitments, @@ -49,7 +49,7 @@ fn fri_fold4_ext2_remainder32() { let test = build_test!( source, &[domain_generator], - &tape, + &advice_stack, advice_provider.0.clone(), advice_map.clone() ); @@ -73,14 +73,14 @@ fn fri_fold4_ext2_remainder64() { let depth = trace_len_e + blowup_exp; let domain_size = 1 << depth; - let (advice_provider, tape, position_eval, alphas, commitments, remainder, num_queries) = + let (advice_provider, stack, position_eval, alphas, commitments, remainder, num_queries) = fri_prove_verify_fold4_ext2(trace_len_e).expect("should not panic"); - let tape = prepare_advice( + let stack = prepare_advice( depth, domain_size, num_queries, - tape, + stack, position_eval, alphas, commitments, @@ -92,7 +92,7 @@ fn fri_fold4_ext2_remainder64() { let test = build_test!( source, &[domain_generator], - &tape, + &stack, advice_provider.0.clone(), advice_map.clone() ); @@ -104,17 +104,17 @@ fn prepare_advice( depth: usize, domain_size: u32, num_queries: usize, - tape_pre: Vec, + stack_pre: Vec, position_eval: Vec, alphas: Vec, com: Vec, remainder: Vec, ) -> Vec { - let mut tape = vec![]; + let mut stack = vec![]; let remainder_length = remainder.len() / 2; let num_layers = (com.len() / 4) - 1; - tape.push(num_layers as u64); + stack.push(num_layers as u64); let mut current_domain_size = domain_size as u64; let mut current_depth = depth as u64; @@ -122,13 +122,13 @@ fn prepare_advice( for i in 0..num_layers { current_domain_size /= 4; - tape.extend_from_slice(&com[(4 * i)..(4 * i + 4)]); - tape.extend_from_slice(&alphas[(4 * i)..(4 * i + 2)]); - tape.extend_from_slice(&vec![current_depth - 1, current_domain_size]); + stack.extend_from_slice(&com[(4 * i)..(4 * i + 4)]); + stack.extend_from_slice(&alphas[(4 * i)..(4 * i + 2)]); + stack.extend_from_slice(&vec![current_depth - 1, current_domain_size]); current_depth -= 2; } - tape.push(remainder_length as u64 / 2); + stack.push(remainder_length as u64 / 2); for i in 0..remainder_length / 2 { let mut remainder_4 = vec![0; 4]; @@ -137,14 +137,14 @@ fn prepare_advice( remainder_4[2] = remainder[4 * i + 2]; remainder_4[3] = remainder[4 * i + 3]; - tape.extend_from_slice(&remainder_4); + stack.extend_from_slice(&remainder_4); } - tape.push(num_queries as u64); + stack.push(num_queries as u64); - tape.extend_from_slice(&position_eval[..]); + stack.extend_from_slice(&position_eval[..]); - tape.extend_from_slice(&tape_pre[..]); + stack.extend_from_slice(&stack_pre[..]); - tape + stack } diff --git a/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs b/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs index cb66ab290c..f3c7d46df2 100644 --- a/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs +++ b/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs @@ -31,7 +31,7 @@ type QuadExt = QuadExtension; // merkle_sets contains the Merkle authentication paths used to authenticate the queries. // advice_maps is used to unhash Merkle nodes to a sequence of field elements representing // the query-values. TODO: Make use of the advice_maps. -// 2) `advice_tape: Vec` is how the query values are provided in order to unhash them. +// 2) `advice_stack: Vec` is how the query values are provided in order to unhash them. // This should be replaced with the use of advice_maps. // 3) `positions: Vec` a vector of consecutive quadruples of the form (0, p, e1, e0) // where p is index of the query at the first layer and (e1, e0) is its corresponding @@ -253,12 +253,12 @@ impl FriVerifierFold4Ext2 { channel.unbatch::<4, 3>(&positions, self.domain_size(), self.layer_commitments.clone()); let mut d_generator; - let mut full_tape = vec![]; + let mut full_stack = vec![]; let mut all_alphas = vec![]; let mut all_position_evaluation = vec![]; for (index, &position) in positions.iter().enumerate() { d_generator = self.domain_generator; - let (cur_pos, evaluation, partial_tape, position_evaluation, alphas) = + let (cur_pos, evaluation, partial_stack, position_evaluation, alphas) = iterate_query_fold_4_quad_ext( &self.layer_alphas, &advice_provider.0, @@ -269,7 +269,7 @@ impl FriVerifierFold4Ext2 { &evaluations[index], &mut d_generator, )?; - full_tape.extend_from_slice(&partial_tape[..]); + full_stack.extend_from_slice(&partial_stack[..]); all_position_evaluation.extend_from_slice(&position_evaluation[..]); all_alphas = alphas; @@ -288,7 +288,7 @@ impl FriVerifierFold4Ext2 { } } - Ok((advice_provider, full_tape, all_position_evaluation, all_alphas)) + Ok((advice_provider, full_stack, all_position_evaluation, all_alphas)) } } diff --git a/processor/src/advice/inputs.rs b/processor/src/advice/inputs.rs index 39705081c2..f438d358f5 100644 --- a/processor/src/advice/inputs.rs +++ b/processor/src/advice/inputs.rs @@ -10,14 +10,14 @@ use super::{utils::IntoBytes, BTreeMap, Felt, InputError, MerkleSet, Vec}; /// /// There are three types of advice inputs: /// -/// 1. Single advice tape which can contain any number of elements. -/// 2. Multiple advice tapes that can be appended to the main tape, and are mapped by 32 bytes keys. +/// 1. Single advice stack which can contain any number of elements. +/// 2. Key-mapped stacks set that can be pushed onto the operand stack. /// 3. Merkle sets list, which are used to provide nondeterministic inputs for instructions that /// operates with Merkle trees. #[derive(Clone, Debug, Default)] pub struct AdviceInputs { - tape: Vec, - values_map: BTreeMap<[u8; 32], Vec>, + stack: Vec, + map: BTreeMap<[u8; 32], Vec>, merkle_sets: BTreeMap<[u8; 32], MerkleSet>, } @@ -25,13 +25,13 @@ impl AdviceInputs { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Attempts to extend the tape values with the given sequence of integers, returning an error + /// Attempts to extend the stack values with the given sequence of integers, returning an error /// if any of the numbers fails while converting to an element `[Felt]`. - pub fn with_tape_values(mut self, iter: I) -> Result + pub fn with_stack_values(mut self, iter: I) -> Result where I: IntoIterator, { - let tape = iter + let stack = iter .into_iter() .map(|v| { Felt::try_from(v).map_err(|_| { @@ -39,25 +39,25 @@ impl AdviceInputs { }) }) .collect::, _>>()?; - self.tape.extend(tape); + self.stack.extend(stack); Ok(self) } - /// Extends the tape with the given elements. - pub fn with_tape(mut self, iter: I) -> Self + /// Extends the stack with the given elements. + pub fn with_stack(mut self, iter: I) -> Self where I: IntoIterator, { - self.tape.extend(iter); + self.stack.extend(iter); self } /// Extends the map of values with the given argument, replacing previously inserted items. - pub fn with_values_map(mut self, iter: I) -> Self + pub fn with_map(mut self, iter: I) -> Self where I: IntoIterator)>, { - self.values_map.extend(iter); + self.map.extend(iter); self } @@ -80,14 +80,14 @@ impl AdviceInputs { // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns a reference to the advice tape. - pub fn tape(&self) -> &[Felt] { - &self.tape + /// Returns a reference to the advice stack. + pub fn stack(&self) -> &[Felt] { + &self.stack } /// Fetch a values set mapped by the given key. pub fn mapped_values(&self, key: &[u8; 32]) -> Option<&[Felt]> { - self.values_map.get(key).map(Vec::as_slice) + self.map.get(key).map(Vec::as_slice) } /// Fetch a Merkle set mapped by the given key. @@ -104,10 +104,10 @@ impl AdviceInputs { self, ) -> (Vec, BTreeMap<[u8; 32], Vec>, BTreeMap<[u8; 32], MerkleSet>) { let Self { - tape, - values_map, + stack, + map, merkle_sets, } = self; - (tape, values_map, merkle_sets) + (stack, map, merkle_sets) } } diff --git a/processor/src/advice/mem_provider.rs b/processor/src/advice/mem_provider.rs index aa65729edb..e4233e01f0 100644 --- a/processor/src/advice/mem_provider.rs +++ b/processor/src/advice/mem_provider.rs @@ -12,72 +12,73 @@ use super::{ #[derive(Debug, Clone, Default)] pub struct MemAdviceProvider { step: u32, - tape: Vec, - values: BTreeMap<[u8; 32], Vec>, + stack: Vec, + map: BTreeMap<[u8; 32], Vec>, sets: BTreeMap<[u8; 32], MerkleSet>, } impl From for MemAdviceProvider { fn from(inputs: AdviceInputs) -> Self { - let (mut tape, values, sets) = inputs.into_parts(); - tape.reverse(); + let (mut stack, map, sets) = inputs.into_parts(); + stack.reverse(); Self { step: 0, - tape, - values, + stack, + map, sets, } } } impl AdviceProvider for MemAdviceProvider { - // ADVICE TAPE + // ADVICE STACK // -------------------------------------------------------------------------------------------- - fn read_tape(&mut self) -> Result { - self.tape.pop().ok_or(ExecutionError::AdviceTapeReadFailed(self.step)) + fn pop_stack(&mut self) -> Result { + self.stack.pop().ok_or(ExecutionError::AdviceStackReadFailed(self.step)) } - fn read_tape_w(&mut self) -> Result { - if self.tape.len() < 4 { - return Err(ExecutionError::AdviceTapeReadFailed(self.step)); + fn pop_stack_word(&mut self) -> Result { + if self.stack.len() < 4 { + return Err(ExecutionError::AdviceStackReadFailed(self.step)); } - let idx = self.tape.len() - 4; - let result = [self.tape[idx + 3], self.tape[idx + 2], self.tape[idx + 1], self.tape[idx]]; + let idx = self.stack.len() - 4; + let result = + [self.stack[idx + 3], self.stack[idx + 2], self.stack[idx + 1], self.stack[idx]]; - self.tape.truncate(idx); + self.stack.truncate(idx); Ok(result) } - fn read_tape_dw(&mut self) -> Result<[Word; 2], ExecutionError> { - let word0 = self.read_tape_w()?; - let word1 = self.read_tape_w()?; + fn pop_stack_dword(&mut self) -> Result<[Word; 2], ExecutionError> { + let word0 = self.pop_stack_word()?; + let word1 = self.pop_stack_word()?; Ok([word0, word1]) } - fn write_tape(&mut self, source: AdviceSource) -> Result<(), ExecutionError> { + fn push_stack(&mut self, source: AdviceSource) -> Result<(), ExecutionError> { match source { AdviceSource::Value(value) => { - self.tape.push(value); + self.stack.push(value); Ok(()) } AdviceSource::Map { key } => { - let values = self - .values + let map = self + .map .get(&key.into_bytes()) .ok_or(ExecutionError::AdviceKeyNotFound(key))?; - self.tape.extend(values.iter().rev()); + self.stack.extend(map.iter().rev()); Ok(()) } } } fn insert_into_map(&mut self, key: Word, values: Vec) -> Result<(), ExecutionError> { - match self.values.insert(key.into_bytes(), values) { + match self.map.insert(key.into_bytes(), values) { None => Ok(()), Some(_) => Err(ExecutionError::DuplicateAdviceKey(key)), } @@ -86,7 +87,12 @@ impl AdviceProvider for MemAdviceProvider { // ADVISE SETS // -------------------------------------------------------------------------------------------- - fn get_tree_node(&self, root: Word, depth: Felt, index: Felt) -> Result { + fn get_tree_node( + &self, + root: Word, + depth: &Felt, + index: &Felt, + ) -> Result { // look up the merkle set and return an error if none is found let merkle_set = self .sets @@ -94,7 +100,7 @@ impl AdviceProvider for MemAdviceProvider { .ok_or_else(|| ExecutionError::MerkleSetNotFound(root.into_bytes()))?; // get the tree node from the merkle set based on depth and index - let index = NodeIndex::from_elements(&depth, &index) + let index = NodeIndex::from_elements(depth, index) .map_err(ExecutionError::MerkleSetLookupFailed)?; let node = merkle_set.get_node(index).map_err(ExecutionError::MerkleSetLookupFailed)?; @@ -104,8 +110,8 @@ impl AdviceProvider for MemAdviceProvider { fn get_merkle_path( &self, root: Word, - depth: Felt, - index: Felt, + depth: &Felt, + index: &Felt, ) -> Result { // look up the merkle set and return an error if none is found let merkle_set = self @@ -114,7 +120,7 @@ impl AdviceProvider for MemAdviceProvider { .ok_or_else(|| ExecutionError::MerkleSetNotFound(root.into_bytes()))?; // get the Merkle path from the merkle set based on depth and index - let index = NodeIndex::from_elements(&depth, &index) + let index = NodeIndex::from_elements(depth, index) .map_err(ExecutionError::MerkleSetLookupFailed)?; let path = merkle_set.get_path(index).map_err(ExecutionError::MerkleSetLookupFailed)?; diff --git a/processor/src/advice/merkle_set.rs b/processor/src/advice/merkle_set.rs index 70099d0b5c..d514d0f481 100644 --- a/processor/src/advice/merkle_set.rs +++ b/processor/src/advice/merkle_set.rs @@ -30,7 +30,9 @@ impl MerkleSet { values: Vec, depth: u8, ) -> Result { - SimpleSmt::new(keys.into_iter().zip(values.into_iter()), depth).map(Self::SparseMerkleTree) + SimpleSmt::new(depth)? + .with_leaves(keys.into_iter().zip(values.into_iter())) + .map(Self::SparseMerkleTree) } // PUBLIC ACCESSORS diff --git a/processor/src/advice/mod.rs b/processor/src/advice/mod.rs index ab5fd3c066..9b9ae9729a 100644 --- a/processor/src/advice/mod.rs +++ b/processor/src/advice/mod.rs @@ -40,10 +40,10 @@ pub use source::AdviceSource; /// /// An advice provider supplies non-deterministic inputs to the processor. /// -/// 1. Provide a tape functionality that yields elements as a stack (last in, first out). These can +/// 1. Provide a stack functionality that yields elements as a stack (last in, first out). These can /// be yielded as elements, words or double words. -/// 2. Provide a map functionality that will store temporary tapes that can be appended to the main -/// tape. This operation should not allow key overwrite; that is: if a given key exists, the +/// 2. Provide a map functionality that will store temporary stacks that can be appended to the main +/// stack. This operation should not allow key overwrite; that is: if a given key exists, the /// implementation should error if the user attempts to insert this key again, instead of the /// common behavior of the maps to simply override the previous contents. This is a design /// decision to increase the runtime robustness of the execution. @@ -68,37 +68,38 @@ pub trait AdviceProvider { self } - // ADVICE TAPE + // ADVICE STACK // -------------------------------------------------------------------------------------------- - /// Pops an element from the advice tape and returns it. + /// Pops an element from the advice stack and returns it. /// /// # Errors - /// Returns an error if the advice tape is empty. - fn read_tape(&mut self) -> Result; + /// Returns an error if the advice stack is empty. + fn pop_stack(&mut self) -> Result; - /// Pops a word (4 elements) from the advice tape and returns it. + /// Pops a word (4 elements) from the advice stack and returns it. /// - /// Note: a word is always stored as little-endian. A `[...,a,b,c,d]` tape will yield + /// Note: a word is always stored as little-endian. A `[...,a,b,c,d]` stack will yield /// `[d,c,b,a]`. /// /// # Errors - /// Returns an error if the advice tape does not contain a full word. - fn read_tape_w(&mut self) -> Result; + /// Returns an error if the advice stack does not contain a full word. + fn pop_stack_word(&mut self) -> Result; - /// Pops a double word (8 elements) from the advice tape and returns them. + /// Pops a double word (8 elements) from the advice stack and returns them. /// - /// Note: a double word is always stored as little-endian. A `[...,a,b,c,d,e,f,g,h]` tape will + /// Note: a double word is always stored as little-endian. A `[...,a,b,c,d,e,f,g,h]` stack will /// yield `[h,g,f,e],[,d,c,b,a]`. /// /// # Errors - /// Returns an error if the advice tape does not contain two words. - fn read_tape_dw(&mut self) -> Result<[Word; 2], ExecutionError>; + /// Returns an error if the advice stack does not contain two words. + fn pop_stack_dword(&mut self) -> Result<[Word; 2], ExecutionError>; - /// Writes values specified by the source to the head of the advice tape. - fn write_tape(&mut self, source: AdviceSource) -> Result<(), ExecutionError>; + /// Writes values specified by the source to the head of the advice stack. + fn push_stack(&mut self, source: AdviceSource) -> Result<(), ExecutionError>; - /// Maps a key to a value list to be yielded by `write_tape_from_map`. + /// Maps a key to a value list to be yielded by `push_stack` with the [AdviceSource::Map] + /// variant. /// /// # Errors /// Returns an error if the key is already present in the advice map. @@ -115,7 +116,8 @@ pub trait AdviceProvider { /// - The specified depth is either zero or greater than the depth of the Merkle tree /// identified by the specified root. /// - Value of the node at the specified depth and index is not known to this advice provider. - fn get_tree_node(&self, root: Word, depth: Felt, index: Felt) -> Result; + fn get_tree_node(&self, root: Word, depth: &Felt, index: &Felt) + -> Result; /// Returns a path to a node at the specified index in a Merkle tree with the specified root. /// @@ -128,8 +130,8 @@ pub trait AdviceProvider { fn get_merkle_path( &self, root: Word, - depth: Felt, - index: Felt, + depth: &Felt, + index: &Felt, ) -> Result; /// Updates a leaf at the specified index on an existing Merkle tree with the specified root; @@ -168,35 +170,40 @@ impl<'a, T> AdviceProvider for &'a mut T where T: AdviceProvider, { - fn read_tape(&mut self) -> Result { - T::read_tape(self) + fn pop_stack(&mut self) -> Result { + T::pop_stack(self) } - fn read_tape_w(&mut self) -> Result { - T::read_tape_w(self) + fn pop_stack_word(&mut self) -> Result { + T::pop_stack_word(self) } - fn read_tape_dw(&mut self) -> Result<[Word; 2], ExecutionError> { - T::read_tape_dw(self) + fn pop_stack_dword(&mut self) -> Result<[Word; 2], ExecutionError> { + T::pop_stack_dword(self) } - fn write_tape(&mut self, source: AdviceSource) -> Result<(), ExecutionError> { - T::write_tape(self, source) + fn push_stack(&mut self, source: AdviceSource) -> Result<(), ExecutionError> { + T::push_stack(self, source) } - fn insert_into_map(&mut self, key: Word, values: Vec) -> Result<(), ExecutionError> { - T::insert_into_map(self, key, values) + fn insert_into_map(&mut self, key: Word, map: Vec) -> Result<(), ExecutionError> { + T::insert_into_map(self, key, map) } - fn get_tree_node(&self, root: Word, depth: Felt, index: Felt) -> Result { + fn get_tree_node( + &self, + root: Word, + depth: &Felt, + index: &Felt, + ) -> Result { T::get_tree_node(self, root, depth, index) } fn get_merkle_path( &self, root: Word, - depth: Felt, - index: Felt, + depth: &Felt, + index: &Felt, ) -> Result { T::get_merkle_path(self, root, depth, index) } diff --git a/processor/src/advice/source.rs b/processor/src/advice/source.rs index 9ef52730ca..aba33d3eb3 100644 --- a/processor/src/advice/source.rs +++ b/processor/src/advice/source.rs @@ -3,20 +3,22 @@ use super::{Felt, Word}; // ADVICE SOURCE // ================================================================================================ -/// Placeholder for advice provider tape mutation. +/// Placeholder for advice provider stack mutation. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum AdviceSource { - /// Writes a single value to the head of the advice tape. + /// Puts a single value onto the advice stack. Value(Felt), - /// Fetch a keyed tape from the values map, reversing and appending it to the advice tape. + /// Fetches a list of elements under the specified key from the advice map and pushes them onto + /// the stack. /// /// Note: this operation shouldn't consume the map element so it can be called multiple times /// for the same key. /// /// # Example - /// Given an advice tape `[a,b,c]`, and a map `x |-> [d,e,f]`, a call `write_tape_from_map(x)` - /// will result in `[a,b,c,f,e,d]` for the advice tape, and will preserve `x |-> [d,e,f]`. + /// Given an advice stack `[a,b,c]`, and a map `x |-> [d,e,f]`, a call + /// `push_stack(AdviceSource::Map { key: x })` will result in `[a,b,c,f,e,d]` for the advice + /// stack, and will preserve `x |-> [d,e,f]`. /// /// # Errors /// Returns an error if the key was not found in the key-value map. diff --git a/processor/src/decorators/mod.rs b/processor/src/decorators/mod.rs index f3f1287281..85711a638c 100644 --- a/processor/src/decorators/mod.rs +++ b/processor/src/decorators/mod.rs @@ -52,8 +52,8 @@ where // INJECTOR HELPERS // -------------------------------------------------------------------------------------------- - /// Injects a node of the Merkle tree specified by the values on the stack at the head of the - /// advice tape. The stack is expected to be arranged as follows (from the top): + /// Pushes a node of the Merkle tree specified by the word on the top of the operand stack onto + /// the advice stack. The operand stack is expected to be arranged as follows (from the top): /// - depth of the node, 1 element /// - index of the node, 1 element /// - root of the tree, 4 elements @@ -71,26 +71,26 @@ where let root = [self.stack.get(5), self.stack.get(4), self.stack.get(3), self.stack.get(2)]; // look up the node in the advice provider - let node = self.advice_provider.get_tree_node(root, depth, index)?; + let node = self.advice_provider.get_tree_node(root, &depth, &index)?; - // write the node into the advice tape with first element written last so that it can be - // removed first - self.advice_provider.write_tape(AdviceSource::Value(node[3]))?; - self.advice_provider.write_tape(AdviceSource::Value(node[2]))?; - self.advice_provider.write_tape(AdviceSource::Value(node[1]))?; - self.advice_provider.write_tape(AdviceSource::Value(node[0]))?; + // push the node onto the advice stack with the first element pushed last so that it can + // be popped first (i.e. stack behavior for word) + self.advice_provider.push_stack(AdviceSource::Value(node[3]))?; + self.advice_provider.push_stack(AdviceSource::Value(node[2]))?; + self.advice_provider.push_stack(AdviceSource::Value(node[1]))?; + self.advice_provider.push_stack(AdviceSource::Value(node[0]))?; Ok(()) } - /// Injects the result of u64 division (both the quotient and the remainder) at the head of - /// the advice tape. The stack is expected to be arranged as follows (from the top): + /// Pushes the result of [u64] division (both the quotient and the remainder) onto the advice + /// stack. The operand stack is expected to be arranged as follows (from the top): /// - divisor split into two 32-bit elements /// - dividend split into two 32-bit elements /// - /// The result is injected into the advice tape as follows: first the remainder is injected, - /// then the quotient is injected. This guarantees that when reading values from the advice - /// tape, first the quotient will be read, and then the remainder. + /// The result is pushed onto the advice stack as follows: the remainder is pushed first, then + /// the quotient is pushed. This guarantees that when popping values from the advice stack, the + /// quotient will be returned first, and the remainder will be returned next. /// /// # Errors /// Returns an error if the divisor is ZERO. @@ -113,23 +113,22 @@ where let (q_hi, q_lo) = u64_to_u32_elements(quotient); let (r_hi, r_lo) = u64_to_u32_elements(remainder); - self.advice_provider.write_tape(AdviceSource::Value(r_hi))?; - self.advice_provider.write_tape(AdviceSource::Value(r_lo))?; - self.advice_provider.write_tape(AdviceSource::Value(q_hi))?; - self.advice_provider.write_tape(AdviceSource::Value(q_lo))?; + self.advice_provider.push_stack(AdviceSource::Value(r_hi))?; + self.advice_provider.push_stack(AdviceSource::Value(r_lo))?; + self.advice_provider.push_stack(AdviceSource::Value(q_hi))?; + self.advice_provider.push_stack(AdviceSource::Value(q_lo))?; Ok(()) } - /// Injects a list of field elements at the front of the advice tape. The list is looked up in - /// the key-value map maintained by the advice provider using the top 4 elements on the stack - /// as the key. + /// Pushes a list of field elements onto the advice stack. The list is looked up in the advice + /// map using the top 4 elements (i.e. word) from the operand stack as the key. /// /// # Errors /// Returns an error if the required key was not found in the key-value map. fn inject_map_value(&mut self) -> Result<(), ExecutionError> { let top_word = self.stack.get_top_word(); - self.advice_provider.write_tape(AdviceSource::Map { key: top_word })?; + self.advice_provider.push_stack(AdviceSource::Map { key: top_word })?; Ok(()) } @@ -170,7 +169,7 @@ where /// /// [coeff'_0, coeff'_1, ...] /// - /// Meaning when a Miden program is going to read it from advice tape, it'll see + /// Meaning when a Miden program is going to read it from advice stack, it'll see /// coefficient_0 first and then coefficient_1. /// /// Note, in case input operand is zero, division by zero error is returned, because @@ -189,18 +188,18 @@ where let elm_arr = [inv_elm]; let coeffs = Ext2Element::as_base_elements(&elm_arr); - self.advice_provider.write_tape(AdviceSource::Value(coeffs[1]))?; - self.advice_provider.write_tape(AdviceSource::Value(coeffs[0]))?; + self.advice_provider.push_stack(AdviceSource::Value(coeffs[1]))?; + self.advice_provider.push_stack(AdviceSource::Value(coeffs[0]))?; Ok(()) } - /// Given evaluations of a polynomial over some specified domain, this routine - /// interpolates the evaluations into a polynomial in coefficient form and writes - /// the result into the advice tape. The interpolation is performed using the iNTT - /// algorithm. The evaluations are expected to be in the quadratic extension field of - /// Z_q and the resulting coefficients are in the quadratic extension field as - /// well | q = 2^64 - 2^32 + 1. + /// Given evaluations of a polynomial over some specified domain, this routine interpolates the + /// evaluations into a polynomial in coefficient form, and pushes the results onto the advice + /// stack. + /// + /// The interpolation is performed using the iNTT algorithm. The evaluations are expected to be + /// in the quadratic extension field | q = 2^64 - 2^32 + 1. /// /// Input stack state should look like /// @@ -213,7 +212,7 @@ where /// remaining `input_eval_len - 2` many evaluations. /// - Each memory address holds two evaluations of the polynomial at adjacent points /// - /// Final advice tape should look like + /// Final advice stack should look like /// /// `[coeff_0, coeff_1, ..., coeff_{n-1}, ...]` | n = output_poly_len /// @@ -253,7 +252,7 @@ where fft::interpolate_poly::(&mut poly, &twiddles); for i in Ext2Element::as_base_elements(&poly[..out_poly_len]).iter().rev().copied() { - self.advice_provider.write_tape(AdviceSource::Value(i))?; + self.advice_provider.push_stack(AdviceSource::Value(i))?; } Ok(()) @@ -284,7 +283,6 @@ mod tests { #[test] fn inject_merkle_node() { let leaves = [init_leaf(1), init_leaf(2), init_leaf(3), init_leaf(4)]; - let tree = MerkleSet::new_merkle_tree(leaves.to_vec()).unwrap(); let stack_inputs = [ tree.root()[0].as_int(), @@ -301,12 +299,12 @@ mod tests { let mut process = Process::new(Kernel::default(), stack_inputs, advice_provider); process.execute_op(Operation::Noop).unwrap(); - // inject the node into the advice tape + // push the node onto the advice stack process .execute_decorator(&Decorator::Advice(AdviceInjector::MerkleNode)) .unwrap(); - // read the node from the tape onto the stack + // pop the node from the advice stack and push it onto the operand stack process.execute_op(Operation::Read).unwrap(); process.execute_op(Operation::Read).unwrap(); process.execute_op(Operation::Read).unwrap(); diff --git a/processor/src/errors.rs b/processor/src/errors.rs index 98bbde8366..b896fc476a 100644 --- a/processor/src/errors.rs +++ b/processor/src/errors.rs @@ -15,7 +15,7 @@ use std::error::Error; #[derive(Debug)] pub enum ExecutionError { AdviceKeyNotFound(Word), - AdviceTapeReadFailed(u32), + AdviceStackReadFailed(u32), MerkleSetLookupFailed(MerkleError), MerkleSetNotFound([u8; 32]), MerkleSetUpdateFailed(MerkleError), @@ -46,12 +46,12 @@ impl Display for ExecutionError { match self { AdviceKeyNotFound(key) => { let hex = to_hex(Felt::elements_as_bytes(key))?; - write!(fmt, "Can't write to advice tape: value for key {hex} not present in the advice map.") + write!(fmt, "Can't push values onto the advice stack: value for key {hex} not present in the advice map.") } MerkleSetLookupFailed(reason) => write!(fmt, "Advice set lookup failed: {reason}"), MerkleSetNotFound(root) => write!(fmt, "Advice set with root {root:x?} not found"), MerkleSetUpdateFailed(reason) => write!(fmt, "Advice set update failed: {reason}"), - AdviceTapeReadFailed(step) => write!(fmt, "Advice tape read failed at step {step}"), + AdviceStackReadFailed(step) => write!(fmt, "Advice stack read failed at step {step}"), CodeBlockNotFound(digest) => { let hex = to_hex(&digest.as_bytes())?; write!( diff --git a/processor/src/operations/crypto_ops.rs b/processor/src/operations/crypto_ops.rs index 99af29cf90..25848dfc4b 100644 --- a/processor/src/operations/crypto_ops.rs +++ b/processor/src/operations/crypto_ops.rs @@ -75,7 +75,7 @@ where // get a Merkle path from the advice provider for the specified root and node index. // the path is expected to be of the specified depth. - let path = self.advice_provider.get_merkle_path(provided_root, depth, index)?; + let path = self.advice_provider.get_merkle_path(provided_root, &depth, &index)?; // use hasher to compute the Merkle root of the path let (addr, computed_root) = self.chiplets.build_merkle_root(node, &path, index); diff --git a/processor/src/operations/io_ops.rs b/processor/src/operations/io_ops.rs index 444516f56e..92ac6e9ad0 100644 --- a/processor/src/operations/io_ops.rs +++ b/processor/src/operations/io_ops.rs @@ -181,10 +181,10 @@ where Ok(()) } - /// Moves 8 elements from the head of the advice tape to memory via the stack. + /// Moves 8 elements from the advice stack to the memory, via the operand stack. /// /// The operation works as follows: - /// - Two words are read from the head of the advice tape. + /// - Two words are popped from the top of the advice stack. /// - The destination memory address for the first word is retrieved from the 13th stack element /// (position 12). /// - The two words are written to memory consecutively, starting at this address. @@ -196,8 +196,8 @@ where let ctx = self.system.ctx(); let addr = self.stack.get(12); - // read two words from the advice tape - let words = self.advice_provider.read_tape_dw()?; + // pop two words from the advice stack + let words = self.advice_provider.pop_stack_dword()?; // write the words memory self.chiplets.write_mem_double(ctx, addr, words); @@ -225,24 +225,24 @@ where // ADVICE INPUTS // -------------------------------------------------------------------------------------------- - /// Removes the next element from the advice tape and pushes it onto the stack. + /// Pops an element from the advice stack and pushes it onto the operand stack. /// /// # Errors - /// Returns an error if the advice tape is empty. + /// Returns an error if the advice stack is empty. pub(super) fn op_read(&mut self) -> Result<(), ExecutionError> { - let value = self.advice_provider.read_tape()?; + let value = self.advice_provider.pop_stack()?; self.stack.set(0, value); self.stack.shift_right(0); Ok(()) } - /// Removes a word (4 elements) from the advice tape and overwrites the top four stack - /// elements with it. + /// Pops a word (4 elements) from the advice stack and overwrites the top word on the operand + /// stack with it. /// /// # Errors - /// Returns an error if the advice tape contains fewer than four elements. + /// Returns an error if the advice stack contains fewer than four elements. pub(super) fn op_readw(&mut self) -> Result<(), ExecutionError> { - let word = self.advice_provider.read_tape_w()?; + let word = self.advice_provider.pop_stack_word()?; self.stack.set(0, word[3]); self.stack.set(1, word[2]); @@ -478,21 +478,21 @@ mod tests { fn op_pipe() { let mut process = Process::new_dummy_with_decoder_helpers_and_empty_stack(); - // write words to the advice tape + // push words onto the advice stack let word1 = [30, 29, 28, 27]; let word2 = [26, 25, 24, 23]; let word1_felts: Word = word1.to_elements().try_into().unwrap(); let word2_felts: Word = word2.to_elements().try_into().unwrap(); for element in word2_felts.iter().rev().chain(word1_felts.iter().rev()).copied() { - // reverse the word order, since elements are pushed onto the advice tape. - process.advice_provider.write_tape(AdviceSource::Value(element)).unwrap(); + // reverse the word order, since elements are pushed onto the advice stack. + process.advice_provider.push_stack(AdviceSource::Value(element)).unwrap(); } // arrange the stack such that: // - 101 is at position 13 (to make sure it is not overwritten) // - 1 (the address) is at position 12 // - values 1 - 12 are at positions 0 - 11. Replacing the first 8 of these values with the - // values from the advice tape should result in 30 through 23 in stack order (with 23 at + // values from the advice stack should result in 30 through 23 in stack order (with 23 at // stack[0]). process.execute_op(Operation::Push(Felt::new(101))).unwrap(); process.execute_op(Operation::Push(ONE)).unwrap(); @@ -503,12 +503,12 @@ mod tests { // execute the PIPE operation process.execute_op(Operation::Pipe).unwrap(); - // check memory state contains the words from the advice tape + // check memory state contains the words from the advice stack assert_eq!(2, process.chiplets.get_mem_size()); assert_eq!(word1_felts, process.chiplets.get_mem_value(0, 1).unwrap()); assert_eq!(word2_felts, process.chiplets.get_mem_value(0, 2).unwrap()); - // the first 8 values should be the values from the advice tape. the next 4 values should + // the first 8 values should be the values from the advice stack. the next 4 values should // remain unchanged, and the address should be incremented by 2 (i.e., 1 -> 3). let stack_values = [ word2[3], word2[2], word2[1], word2[0], word1[3], word1[2], word1[1], word1[0], 4, 3, @@ -523,21 +523,22 @@ mod tests { #[test] fn op_read() { - // reading from tape should push the value onto the stack - let mut process = Process::new_dummy_with_advice_tape(&[3]); + // popping from the advice stack should push the value onto the operand stack + let mut process = Process::new_dummy_with_advice_stack(&[3]); process.execute_op(Operation::Push(ONE)).unwrap(); process.execute_op(Operation::Read).unwrap(); let expected = build_expected_stack(&[3, 1]); assert_eq!(expected, process.stack.trace_state()); - // reading again should result in an error because advice tape is empty + // reading again should result in an error because advice stack is empty assert!(process.execute_op(Operation::Read).is_err()); } #[test] fn op_readw() { - // reading from tape should overwrite top 4 values - let mut process = Process::new_dummy_with_advice_tape(&[3, 4, 5, 6]); + // popping a word from the advice stack should overwrite top 4 elements of the operand + // stack + let mut process = Process::new_dummy_with_advice_stack(&[3, 4, 5, 6]); process.execute_op(Operation::Push(ONE)).unwrap(); process.execute_op(Operation::Pad).unwrap(); process.execute_op(Operation::Pad).unwrap(); @@ -546,20 +547,6 @@ mod tests { process.execute_op(Operation::ReadW).unwrap(); let expected = build_expected_stack(&[6, 5, 4, 3, 1]); assert_eq!(expected, process.stack.trace_state()); - /* - - // reading again should result in an error because advice tape is empty - assert!(process.execute_op(Operation::ReadW).is_err()); - - // should not return an error if the stack has fewer than 4 values - let mut process = Process::new_dummy_with_advice_tape(&[3, 4, 5, 6]); - process.execute_op(Operation::Push(ONE)).unwrap(); - process.execute_op(Operation::Pad).unwrap(); - process.execute_op(Operation::Pad).unwrap(); - assert!(process.execute_op(Operation::ReadW).is_ok()); - let expected = build_expected_stack(&[6, 5, 4, 3]); - assert_eq!(expected, process.stack.trace_state()); - */ } // HELPER METHODS diff --git a/processor/src/operations/mod.rs b/processor/src/operations/mod.rs index 0640566c21..237f8a751f 100644 --- a/processor/src/operations/mod.rs +++ b/processor/src/operations/mod.rs @@ -189,11 +189,11 @@ impl Process { Self::new_dummy(stack) } - /// Instantiates a new process with an advice tape for testing purposes. - fn new_dummy_with_advice_tape(advice_tape: &[u64]) -> Self { + /// Instantiates a new process with an advice stack for testing purposes. + fn new_dummy_with_advice_stack(advice_stack: &[u64]) -> Self { let stack_inputs = super::StackInputs::default(); let advice_inputs = super::AdviceInputs::default() - .with_tape_values(advice_tape.iter().copied()) + .with_stack_values(advice_stack.iter().copied()) .unwrap(); let advice_provider = super::MemAdviceProvider::from(advice_inputs); let mut process = Self::new(Kernel::default(), stack_inputs, advice_provider); diff --git a/stdlib/asm/math/u64.masm b/stdlib/asm/math/u64.masm index 1bc479f221..17a678cee9 100644 --- a/stdlib/asm/math/u64.masm +++ b/stdlib/asm/math/u64.masm @@ -427,9 +427,9 @@ end #! Stack transition looks as follows: #! [b_hi, b_lo, a_hi, a_lo, ...] -> [c_hi, c_lo, ...], where c = a // b export.unchecked_div - adv.u64div # inject the quotient and the remainder into the advice tape + adv.u64div # push the quotient and the remainder onto the advice stack - adv_push.2 # read the quotient from the advice tape and make sure it consists of + adv_push.2 # pop the quotient from the advice stack and assert it consists of u32assert.2 # 32-bit limbs dup.3 # multiply quotient by the divisor and make sure the resulting value @@ -451,7 +451,7 @@ export.unchecked_div eq.0 assert - adv_push.2 # read the remainder from the advice tape and make sure it consists of + adv_push.2 # pop the remainder from the advice stack and assert it consists of u32assert.2 # 32-bit limbs movup.7 # make sure the divisor is greater than the remainder. this also consumes @@ -492,9 +492,9 @@ end #! Stack transition looks as follows: #! [b_hi, b_lo, a_hi, a_lo, ...] -> [c_hi, c_lo, ...], where c = a % b export.unchecked_mod - adv.u64div # inject the quotient and the remainder into the advice tape + adv.u64div # push the quotient and the remainder onto the advice stack - adv_push.2 # read the quotient from the advice tape and make sure it consists of + adv_push.2 # pop the quotient from the advice stack and assert it consists of u32assert.2 # 32-bit limbs dup.3 # multiply quotient by the divisor and make sure the resulting value @@ -516,7 +516,7 @@ export.unchecked_mod eq.0 assert - adv_push.2 # read the remainder from the advice tape and make sure it consists of + adv_push.2 # pop the quotient from the advice stack and assert it consists of u32assert.2 # 32-bit limbs movup.5 # make sure the divisor is greater than the remainder. this also consumes @@ -557,9 +557,9 @@ end #! Stack transition looks as follows: #! [b_hi, b_lo, a_hi, a_lo, ...] -> [r_hi, r_lo, q_hi, q_lo ...], where r = a % b, q = a / b export.unchecked_divmod - adv.u64div # inject the quotient and the remainder into the advice tape + adv.u64div # push the quotient and the remainder onto the advice stack - adv_push.2 # read the quotient from the advice tape and make sure it consists of + adv_push.2 # pop the quotient from the advice stack and assert it consists of u32assert.2 # 32-bit limbs dup.3 # multiply quotient by the divisor and make sure the resulting value @@ -581,7 +581,7 @@ export.unchecked_divmod eq.0 assert - adv_push.2 # read the remainder from the advice tape and make sure it consists of + adv_push.2 # pop the quotient from the advice stack and assert it consists of u32assert.2 # 32-bit limbs movup.7 # make sure the divisor is greater than the remainder. this also consumes From 96dcee9c1910841de65123b0085aac0013ef419e Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Mon, 20 Mar 2023 21:18:01 +0100 Subject: [PATCH 30/47] feat: add miden-crypto merkle store as advice provider backend related issue: #774 --- .../src/assembler/instruction/crypto_ops.rs | 2 +- core/src/lib.rs | 2 +- docs/src/intro/overview.md | 2 +- miden/src/lib.rs | 2 +- .../tests/integration/air/chiplets/hasher.rs | 32 ++--- miden/tests/integration/helpers/crypto.rs | 8 +- miden/tests/integration/helpers/mod.rs | 4 +- miden/tests/integration/main.rs | 46 +++++-- .../integration/operations/crypto_ops.rs | 23 ++-- .../integration/operations/io_ops/adv_ops.rs | 6 +- .../integration/operations/io_ops/mod.rs | 4 +- .../integration/stdlib/crypto/fri/channel.rs | 5 +- .../integration/stdlib/crypto/fri/mod.rs | 28 ++-- .../stdlib/crypto/fri/verifier_fri_e2f4.rs | 26 ++-- processor/src/advice/inputs.rs | 40 ++---- processor/src/advice/mem_provider.rs | 87 ++++--------- processor/src/advice/mod.rs | 33 ++--- processor/src/chiplets/hasher/tests.rs | 15 +-- processor/src/decorators/mod.rs | 12 +- processor/src/errors.rs | 4 + processor/src/lib.rs | 2 +- processor/src/operations/crypto_ops.rs | 123 +++++------------- processor/src/trace/tests/chiplets/hasher.rs | 9 +- processor/src/trace/tests/hasher.rs | 23 ++-- prover/src/lib.rs | 2 +- 25 files changed, 235 insertions(+), 305 deletions(-) diff --git a/assembly/src/assembler/instruction/crypto_ops.rs b/assembly/src/assembler/instruction/crypto_ops.rs index 60da913a13..68cd635ec1 100644 --- a/assembly/src/assembler/instruction/crypto_ops.rs +++ b/assembly/src/assembler/instruction/crypto_ops.rs @@ -145,7 +145,7 @@ pub(super) fn mtree_set(span: &mut SpanBuilder) -> Result, Ass // stack: [d, i, R_old, V_new, ...] // stack: [V_old, R_new, ...] (29 cycles) - update_mtree(span, false) + update_mtree(span, true) } /// Appends the MRUPDATE op with a parameter of "true" and stack manipulations to the span block as diff --git a/core/src/lib.rs b/core/src/lib.rs index 58c48aa0fd..b7856f1571 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -14,7 +14,7 @@ pub use ::crypto::{Word, ONE, WORD_SIZE, ZERO}; pub mod crypto { pub mod merkle { pub use ::crypto::merkle::{ - MerkleError, MerklePath, MerklePathSet, MerkleTree, NodeIndex, SimpleSmt, + MerkleError, MerklePath, MerklePathSet, MerkleStore, MerkleTree, NodeIndex, SimpleSmt, }; } diff --git a/docs/src/intro/overview.md b/docs/src/intro/overview.md index 6139ef5e6f..85c35b6d25 100644 --- a/docs/src/intro/overview.md +++ b/docs/src/intro/overview.md @@ -43,6 +43,6 @@ The *advice provider* component is responsible for supplying nondeterministic in The advice provider consists of three components: * **Advice stack** which is a one-dimensional array of field elements. Being a stack, the VM can either push new elements onto the advice stack, or pop the elements from its top. * **Advice map** which is a key-value map where keys are words and values are vectors of field elements. The VM can copy values from the advice map onto the advice stack as well as insert new values into the advice map (e.g., from a region of memory). -* **Merkle sets** which contain structured data reducible to Merkle paths. Some examples of merkle sets are: Merkle tree, Sparse Merkle tree, a collection of Merkle paths. Every merkle set can be uniquely identified by its root. The VM can request Merkle paths from an merkle set, as well as update an merkle set by modifying one of its nodes (this will also change the root of the modified merkle set). +* **Merkle store** which contain structured data reducible to Merkle paths. Some examples of such structures are: Merkle tree, Sparse Merkle tree, and a collection of Merkle paths. The VM can request Merkle paths from the Merkle store, as well as mutate it by updating or merging nodes contained in the store. The prover initializes the advice provider prior to executing a program, and from that point on the advice provider is manipulated solely by executing operations on the VM. diff --git a/miden/src/lib.rs b/miden/src/lib.rs index 86ab99d243..2e65cb8355 100644 --- a/miden/src/lib.rs +++ b/miden/src/lib.rs @@ -12,6 +12,6 @@ pub use processor::{ }; pub use prover::{ math, prove, Digest, ExecutionProof, FieldExtension, HashFunction, InputError, MerkleError, - MerkleSet, Program, ProofOptions, StackOutputs, StarkProof, Word, + Program, ProofOptions, StackOutputs, StarkProof, Word, }; pub use verifier::{verify, VerificationError}; diff --git a/miden/tests/integration/air/chiplets/hasher.rs b/miden/tests/integration/air/chiplets/hasher.rs index b7a86031fd..230099c572 100644 --- a/miden/tests/integration/air/chiplets/hasher.rs +++ b/miden/tests/integration/air/chiplets/hasher.rs @@ -1,8 +1,10 @@ use crate::build_op_test; -use crate::helpers::crypto::{init_merkle_leaf, init_merkle_leaves}; -use processor::MerkleSet; +use crate::helpers::crypto::{init_merkle_leaf, init_merkle_store}; use rand_utils::rand_vector; -use vm_core::StarkField; +use vm_core::{ + crypto::merkle::{MerkleStore, MerkleTree}, + StarkField, Word, +}; #[test] fn hperm() { @@ -25,8 +27,8 @@ fn mtree_get() { let asm_op = "mtree_get"; let index = 3usize; - let leaves = init_merkle_leaves(&[1, 2, 3, 4, 5, 6, 7, 8]); - let tree = MerkleSet::new_merkle_tree(leaves).unwrap(); + let (leaves, store) = init_merkle_store(&[1, 2, 3, 4, 5, 6, 7, 8]); + let tree = MerkleTree::new(leaves.clone()).unwrap(); let stack_inputs = [ tree.root()[0].as_int(), @@ -37,35 +39,35 @@ fn mtree_get() { tree.depth() as u64, ]; - build_op_test!(asm_op, &stack_inputs, &[], vec![tree]) + build_op_test!(asm_op, &stack_inputs, &[], store) .prove_and_verify(stack_inputs.to_vec(), false); } #[test] fn mtree_set() { let asm_op = "mtree_set"; - let (stack_inputs, tree) = build_mtree_update_test_inputs(); + let (stack_inputs, store, _leaves) = build_mtree_update_test_inputs(); - build_op_test!(asm_op, &stack_inputs, &[], vec![tree]) + build_op_test!(asm_op, &stack_inputs, &[], store) .prove_and_verify(stack_inputs.to_vec(), false); } #[test] fn mtree_cwm() { let asm_op = "mtree_cwm"; - let (stack_inputs, tree) = build_mtree_update_test_inputs(); + let (stack_inputs, store, _leaves) = build_mtree_update_test_inputs(); - build_op_test!(asm_op, &stack_inputs, &[], vec![tree]).prove_and_verify(stack_inputs, false); + build_op_test!(asm_op, &stack_inputs, &[], store).prove_and_verify(stack_inputs, false); } /// Helper function that builds a test stack and Merkle tree for testing mtree updates. -fn build_mtree_update_test_inputs() -> (Vec, MerkleSet) { +fn build_mtree_update_test_inputs() -> (Vec, MerkleStore, Vec) { let index = 5_usize; - let leaves = init_merkle_leaves(&[1, 2, 3, 4, 5, 6, 7, 8]); - let tree = MerkleSet::new_merkle_tree(leaves.clone()).unwrap(); + let (leaves, store) = init_merkle_store(&[1, 2, 3, 4, 5, 6, 7, 8]); + let tree = MerkleTree::new(leaves.clone()).unwrap(); let new_node = init_merkle_leaf(9); - let mut new_leaves = leaves; + let mut new_leaves = leaves.clone(); new_leaves[index] = new_node; let stack_inputs = vec![ @@ -81,5 +83,5 @@ fn build_mtree_update_test_inputs() -> (Vec, MerkleSet) { tree.depth() as u64, ]; - (stack_inputs, tree) + (stack_inputs, store, leaves) } diff --git a/miden/tests/integration/helpers/crypto.rs b/miden/tests/integration/helpers/crypto.rs index cf121cc98f..1a3b944fed 100644 --- a/miden/tests/integration/helpers/crypto.rs +++ b/miden/tests/integration/helpers/crypto.rs @@ -1,9 +1,15 @@ use super::Felt; -use vm_core::{FieldElement, Word}; +use vm_core::{crypto::merkle::MerkleStore, FieldElement, Word}; // CRYPTO HELPER FUNCTIONS // ================================================================================================ +pub fn init_merkle_store(values: &[u64]) -> (Vec, MerkleStore) { + let leaves = init_merkle_leaves(values); + let store = MerkleStore::new().with_merkle_tree(leaves.clone()).unwrap(); + (leaves, store) +} + pub fn init_merkle_leaves(values: &[u64]) -> Vec { values.iter().map(|&v| init_merkle_leaf(v)).collect() } diff --git a/miden/tests/integration/helpers/mod.rs b/miden/tests/integration/helpers/mod.rs index 1cd5e71dae..3a2e98c893 100644 --- a/miden/tests/integration/helpers/mod.rs +++ b/miden/tests/integration/helpers/mod.rs @@ -3,7 +3,9 @@ pub use processor::{AdviceInputs, StackInputs}; use processor::{ExecutionError, ExecutionTrace, Process, VmStateIterator}; use proptest::prelude::*; use stdlib::StdLibrary; -pub use vm_core::{stack::STACK_TOP_SIZE, Felt, FieldElement, Program, StackOutputs}; +pub use vm_core::{ + crypto::merkle::MerkleStore, stack::STACK_TOP_SIZE, Felt, FieldElement, Program, StackOutputs, +}; pub mod crypto; diff --git a/miden/tests/integration/main.rs b/miden/tests/integration/main.rs index 0593536622..1ac4dac19c 100644 --- a/miden/tests/integration/main.rs +++ b/miden/tests/integration/main.rs @@ -34,9 +34,9 @@ fn multi_output_program() { /// * `stack_inputs` (optional): the initial inputs which must be at the top of the stack before /// executing the `source`. Stack inputs can be provided independently without any advice inputs. /// * `advice_stack` (optional): the initial advice stack values. When provided, `stack_inputs` and -/// `merkle_sets` are also expected. -/// * `merkle_sets` (optional): the initial merkle set values. When provided, `stack_inputs` and -/// `advice_tape` are also expected. +/// `merkle_store` are also expected. +/// * `merkle_store` (optional): the initial merkle set values. When provided, `stack_inputs` and +/// `advice_stack` are also expected. #[macro_export] macro_rules! build_op_test { ($op_str:expr) => {{ @@ -59,9 +59,9 @@ macro_rules! build_op_test { /// * `stack_inputs` (optional): the initial inputs which must be at the top of the stack before /// executing the `source`. Stack inputs can be provided independently without any advice inputs. /// * `advice_stack` (optional): the initial advice stack values. When provided, `stack_inputs` and -/// `merkle_sets` are also expected. -/// * `merkle_sets` (optional): the initial merkle set values. When provided, `stack_inputs` and -/// `advice_tape` are also expected. +/// `merkle_store` are also expected. +/// * `merkle_store` (optional): the initial merkle set values. When provided, `stack_inputs` and +/// `advice_stack` are also expected. #[macro_export] macro_rules! build_test { ($($params:tt)+) => {{ @@ -79,9 +79,9 @@ macro_rules! build_test { /// * `stack_inputs` (optional): the initial inputs which must be at the top of the stack before /// executing the `source`. Stack inputs can be provided independently without any advice inputs. /// * `advice_stack` (optional): the initial advice stack values. When provided, `stack_inputs` and -/// `merkle_sets` are also expected. -/// * `merkle_sets` (optional): the initial merkle set values. When provided, `stack_inputs` and -/// `advice_tape` are also expected. +/// `merkle_store` are also expected. +/// * `merkle_store` (optional): the initial merkle set values. When provided, `stack_inputs` and +/// `advice_stack` are also expected. #[macro_export] macro_rules! build_debug_test { ($($params:tt)+) => {{ @@ -117,16 +117,16 @@ macro_rules! build_test_by_mode { } }}; ( - $in_debug_mode:expr, $source:expr, $stack_inputs:expr, $advice_stack:expr, $merkle_sets:expr + $in_debug_mode:expr, $source:expr, $stack_inputs:expr, $advice_stack:expr ) => {{ let stack_inputs: Vec = $stack_inputs.to_vec(); let stack_inputs = $crate::helpers::StackInputs::try_from_values(stack_inputs).unwrap(); let stack_values: Vec = $advice_stack.to_vec(); + let store = $crate::helpers::MerkleStore::new(); let advice_inputs = $crate::helpers::AdviceInputs::default() .with_stack_values(stack_values) .unwrap() - .with_merkle_sets($merkle_sets) - .unwrap(); + .with_merkle_store(store); $crate::helpers::Test { source: String::from($source), @@ -136,15 +136,33 @@ macro_rules! build_test_by_mode { in_debug_mode: $in_debug_mode, } }}; - ($in_debug_mode:expr, $source:expr, $stack_inputs:expr, $advice_stack:expr, $advice_sets:expr, $advice_map:expr) => {{ + ( + $in_debug_mode:expr, $source:expr, $stack_inputs:expr, $advice_stack:expr, $advice_merkle_store:expr + ) => {{ let stack_inputs: Vec = $stack_inputs.to_vec(); let stack_inputs = $crate::helpers::StackInputs::try_from_values(stack_inputs).unwrap(); let stack_values: Vec = $advice_stack.to_vec(); let advice_inputs = $crate::helpers::AdviceInputs::default() .with_stack_values(stack_values) .unwrap() - .with_merkle_sets($advice_sets) + .with_merkle_store($advice_merkle_store); + + $crate::helpers::Test { + source: String::from($source), + kernel: None, + stack_inputs, + advice_inputs, + in_debug_mode: $in_debug_mode, + } + }}; + ($in_debug_mode:expr, $source:expr, $stack_inputs:expr, $advice_stack:expr, $advice_merkle_store:expr, $advice_map:expr) => {{ + let stack_inputs: Vec = $stack_inputs.to_vec(); + let stack_inputs = $crate::helpers::StackInputs::try_from_values(stack_inputs).unwrap(); + let stack_values: Vec = $advice_stack.to_vec(); + let advice_inputs = $crate::helpers::AdviceInputs::default() + .with_stack_values(stack_values) .unwrap() + .with_merkle_store($advice_merkle_store) .with_map($advice_map); $crate::helpers::Test { diff --git a/miden/tests/integration/operations/crypto_ops.rs b/miden/tests/integration/operations/crypto_ops.rs index 3229e256d3..3e8d5e7827 100644 --- a/miden/tests/integration/operations/crypto_ops.rs +++ b/miden/tests/integration/operations/crypto_ops.rs @@ -1,13 +1,12 @@ -use processor::MerkleSet; use rand_utils::rand_vector; use vm_core::{ chiplets::hasher::{apply_permutation, hash_elements, STATE_WIDTH}, - crypto::merkle::NodeIndex, + crypto::merkle::{MerkleTree, NodeIndex}, Felt, FieldElement, StarkField, }; use crate::build_op_test; -use crate::helpers::crypto::{init_merkle_leaf, init_merkle_leaves}; +use crate::helpers::crypto::{init_merkle_leaf, init_merkle_store}; // TESTS // ================================================================================================ @@ -105,8 +104,8 @@ fn mtree_get() { let asm_op = "mtree_get"; let index = 3usize; - let leaves = init_merkle_leaves(&[1, 2, 3, 4, 5, 6, 7, 8]); - let tree = MerkleSet::new_merkle_tree(leaves.clone()).unwrap(); + let (leaves, store) = init_merkle_store(&[1, 2, 3, 4, 5, 6, 7, 8]); + let tree = MerkleTree::new(leaves.clone()).unwrap(); let stack_inputs = [ tree.root()[0].as_int(), @@ -128,20 +127,20 @@ fn mtree_get() { tree.root()[0].as_int(), ]; - let test = build_op_test!(asm_op, &stack_inputs, &[], vec![tree]); + let test = build_op_test!(asm_op, &stack_inputs, &[], store); test.expect_stack(&final_stack); } #[test] fn mtree_update() { let index = 5usize; - let leaves = init_merkle_leaves(&[1, 2, 3, 4, 5, 6, 7, 8]); - let tree = MerkleSet::new_merkle_tree(leaves.clone()).unwrap(); + let (leaves, store) = init_merkle_store(&[1, 2, 3, 4, 5, 6, 7, 8]); + let tree = MerkleTree::new(leaves.clone()).unwrap(); let new_node = init_merkle_leaf(9); - let mut new_leaves = leaves; + let mut new_leaves = leaves.clone(); new_leaves[index] = new_node; - let new_tree = MerkleSet::new_merkle_tree(new_leaves).unwrap(); + let new_tree = MerkleTree::new(new_leaves).unwrap(); let stack_inputs = [ new_node[0].as_int(), @@ -176,7 +175,7 @@ fn mtree_update() { new_tree.root()[0].as_int(), ]; - let test = build_op_test!(asm_op, &stack_inputs, &[], vec![tree.clone()]); + let test = build_op_test!(asm_op, &stack_inputs, &[], store.clone()); test.expect_stack(&final_stack); // --- mtree_cwm ---------------------------------------------------------------------- @@ -195,7 +194,7 @@ fn mtree_update() { new_tree.root()[0].as_int(), ]; - let test = build_op_test!(asm_op, &stack_inputs, &[], vec![tree]); + let test = build_op_test!(asm_op, &stack_inputs, &[], store); test.expect_stack(&final_stack); } diff --git a/miden/tests/integration/operations/io_ops/adv_ops.rs b/miden/tests/integration/operations/io_ops/adv_ops.rs index 0f3c5b513d..29a50b3ac0 100644 --- a/miden/tests/integration/operations/io_ops/adv_ops.rs +++ b/miden/tests/integration/operations/io_ops/adv_ops.rs @@ -14,7 +14,7 @@ fn adv_push() { final_stack.copy_from_slice(&advice_stack[..n]); final_stack.reverse(); - let test = build_op_test!(source, &[], &advice_stack, vec![]); + let test = build_op_test!(source, &[], &advice_stack); test.expect_stack(&final_stack); }; @@ -42,7 +42,7 @@ fn adv_loadw() { let mut final_stack = advice_stack; final_stack.reverse(); - let test = build_op_test!(asm_op, &[8, 7, 6, 5], &advice_stack, vec![]); + let test = build_op_test!(asm_op, &[8, 7, 6, 5], &advice_stack); test.expect_stack(&final_stack); } @@ -82,6 +82,6 @@ fn adv_pipe() { final_stack.reverse(); final_stack.push(2); - let test = build_test!(source, &[], &advice_stack, vec![]); + let test = build_test!(source, &[], &advice_stack); test.expect_stack(&final_stack); } diff --git a/miden/tests/integration/operations/io_ops/mod.rs b/miden/tests/integration/operations/io_ops/mod.rs index 9d03101e04..22d5450e75 100644 --- a/miden/tests/integration/operations/io_ops/mod.rs +++ b/miden/tests/integration/operations/io_ops/mod.rs @@ -47,13 +47,13 @@ fn mem_stream_pipe() { // initialize with anything other than zeros, since the stack is set to 0s between the adv_pipe // and mem_stream operations in the source script. let stack_inputs = [1, 1, 1, 1, 1, 1, 1, 1]; - let test = build_test!(source, &stack_inputs, &advice_stack, vec![]); + let test = build_test!(source, &stack_inputs, &advice_stack); let final_stack = test.get_last_stack_state(); assert_eq!(final_stack[0..4], final_stack[4..8]); // --- the same stack values should yield the same results from adv_pipe and mem_stream ------- // initialize with all zeros, just like between the adv_pipe and mem_stream operations above. - let test = build_test!(source, &[], &advice_stack, vec![]); + let test = build_test!(source, &[], &advice_stack); let final_stack = test.get_last_stack_state(); assert_eq!(final_stack[0..4], final_stack[4..8]); diff --git a/miden/tests/integration/stdlib/crypto/fri/channel.rs b/miden/tests/integration/stdlib/crypto/fri/channel.rs index b3acaae45b..5642bbb424 100644 --- a/miden/tests/integration/stdlib/crypto/fri/channel.rs +++ b/miden/tests/integration/stdlib/crypto/fri/channel.rs @@ -1,5 +1,4 @@ -use miden::MerkleSet; -use vm_core::{Felt, FieldElement}; +use vm_core::{crypto::merkle::MerklePathSet, Felt, FieldElement}; use winter_fri::{FriProof, VerifierError}; use winterfell::{ @@ -13,7 +12,7 @@ pub trait UnBatch { positions: &[usize], domain_size: usize, layer_commitments: Vec<::Digest>, - ) -> (Vec, Vec<([u8; 32], Vec)>); + ) -> (Vec, Vec<([u8; 32], Vec)>); } pub struct MidenFriVerifierChannel> { diff --git a/miden/tests/integration/stdlib/crypto/fri/mod.rs b/miden/tests/integration/stdlib/crypto/fri/mod.rs index 62a1f1aae2..00d10e1d08 100644 --- a/miden/tests/integration/stdlib/crypto/fri/mod.rs +++ b/miden/tests/integration/stdlib/crypto/fri/mod.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use super::{build_test, Felt}; -use vm_core::StarkField; +use vm_core::{crypto::merkle::MerkleStore, StarkField}; use math::log2; use miden::utils::math; @@ -15,6 +15,7 @@ pub use verifier_fri_e2f4::*; mod remainder; #[test] +#[ignore = "the unbound integrated test struct needs to be refactored to use merkle store"] fn fri_fold4_ext2_remainder32() { let source = " use.std::crypto::fri::frie2f4 @@ -46,18 +47,17 @@ fn fri_fold4_ext2_remainder32() { let advice_map: BTreeMap<[u8; 32], Vec> = BTreeMap::from_iter(advice_provider.1); let domain_generator = Felt::get_root_of_unity(log2(domain_size as usize)).as_int(); - let test = build_test!( - source, - &[domain_generator], - &advice_stack, - advice_provider.0.clone(), - advice_map.clone() - ); + let mut store = MerkleStore::new(); + for path_set in &advice_provider.0 { + store.add_merkle_path_set(&path_set).unwrap(); + } + let test = build_test!(source, &[domain_generator], &advice_stack, store, advice_map.clone()); test.expect_stack(&[]); } #[test] +#[ignore = "the unbound integrated test struct needs to be refactored to use merkle store"] fn fri_fold4_ext2_remainder64() { let source = " use.std::crypto::fri::frie2f4 @@ -89,13 +89,11 @@ fn fri_fold4_ext2_remainder64() { let advice_map: BTreeMap<[u8; 32], Vec> = BTreeMap::from_iter(advice_provider.1); let domain_generator = Felt::get_root_of_unity(log2(domain_size as usize)).as_int(); - let test = build_test!( - source, - &[domain_generator], - &stack, - advice_provider.0.clone(), - advice_map.clone() - ); + let mut store = MerkleStore::new(); + for path_set in &advice_provider.0 { + store.add_merkle_path_set(&path_set).unwrap(); + } + let test = build_test!(source, &[domain_generator], &stack, store, advice_map.clone()); test.expect_stack(&[]); } diff --git a/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs b/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs index f3c7d46df2..1165ba7484 100644 --- a/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs +++ b/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs @@ -3,7 +3,6 @@ use std::mem; use miden::{math::fft, utils::math::log2, Digest as MidenDigest}; use processor::Hasher; -use prover::MerkleSet; use vm_core::{ chiplets::hasher::Hasher as MidenHasher, crypto::merkle::{MerklePath, MerklePathSet, NodeIndex}, @@ -27,7 +26,7 @@ type QuadExt = QuadExtension; // The main purpose of this function is to build the non-deterministic inputs needed to verify // a FRI proof inside the Miden VM. // The output is organized as follows: -// 1) `(merkle_sets, advice_maps): (Vec, Vec<([u8; 32], Vec)>)` where +// 1) `(merkle_sets, advice_maps): (Vec, Vec<([u8; 32], Vec)>)` where // merkle_sets contains the Merkle authentication paths used to authenticate the queries. // advice_maps is used to unhash Merkle nodes to a sequence of field elements representing // the query-values. TODO: Make use of the advice_maps. @@ -45,7 +44,7 @@ pub fn fri_prove_verify_fold4_ext2( trace_length_e: usize, ) -> Result< ( - (Vec, Vec<([u8; 32], Vec)>), + (Vec, Vec<([u8; 32], Vec)>), Vec, Vec, Vec, @@ -102,8 +101,16 @@ pub fn fri_prove_verify_fold4_ext2( .collect(); match result { - Ok(res) => { - return Ok((res.0, res.1, res.2, res.3, commitments, remainder, positions.len())) + Ok(((merkle_path_set, advice_values), full_stack, all_position_evaluation, all_alphas)) => { + return Ok(( + (merkle_path_set, advice_values), + full_stack, + all_position_evaluation, + all_alphas, + commitments, + remainder, + positions.len(), + )); } Err(err) => return Err(err), } @@ -142,7 +149,7 @@ fn verify_proof( positions: &[usize], options: &FriOptions, ) -> Result< - ((Vec, Vec<([u8; 32], Vec)>), Vec, Vec, Vec), + ((Vec, Vec<([u8; 32], Vec)>), Vec, Vec, Vec), VerifierError, > { let mut channel = MidenFriVerifierChannel::::new( @@ -242,7 +249,7 @@ impl FriVerifierFold4Ext2 { evaluations: &[QuadExt], positions: &[usize], ) -> Result< - ((Vec, Vec<([u8; 32], Vec)>), Vec, Vec, Vec), + ((Vec, Vec<([u8; 32], Vec)>), Vec, Vec, Vec), VerifierError, > { // 1 ----- verify the recursive components of the FRI proof ----------------------------------- @@ -294,7 +301,7 @@ impl FriVerifierFold4Ext2 { fn iterate_query_fold_4_quad_ext( layer_alphas: &Vec, - m_path_sets: &Vec, + m_path_sets: &Vec, key_val_map: &Vec<([u8; 32], Vec)>, position: usize, number_of_layers: usize, @@ -409,7 +416,7 @@ impl UnBatch for MidenFriVerifierChannel, - ) -> (Vec, Vec<([u8; 32], Vec)>) { + ) -> (Vec, Vec<([u8; 32], Vec)>) { let queries = self.layer_queries().clone(); let mut current_domain_size = domain_size; let mut positions = positions_.to_vec(); @@ -461,7 +468,6 @@ impl UnBatch for MidenFriVerifierChannel, map: BTreeMap<[u8; 32], Vec>, - merkle_sets: BTreeMap<[u8; 32], MerkleSet>, + store: MerkleStore, } impl AdviceInputs { @@ -61,20 +61,10 @@ impl AdviceInputs { self } - /// Attempts to extend the Merkle sets with the given argument, failing if a duplicated root is - /// provided. - pub fn with_merkle_sets(mut self, iter: I) -> Result - where - I: IntoIterator, - { - for set in iter.into_iter() { - let key = set.root().into_bytes(); - if self.merkle_sets.contains_key(&key) { - return Err(InputError::DuplicateAdviceRoot(key)); - } - self.merkle_sets.insert(key, set); - } - Ok(self) + /// Replaces the [MerkleStore] with the provided argument. + pub fn with_merkle_store(mut self, store: MerkleStore) -> Self { + self.store = store; + self } // PUBLIC ACCESSORS @@ -90,9 +80,9 @@ impl AdviceInputs { self.map.get(key).map(Vec::as_slice) } - /// Fetch a Merkle set mapped by the given key. - pub fn merkle_set(&self, key: &[u8; 32]) -> Option<&MerkleSet> { - self.merkle_sets.get(key) + /// Returns the underlying [MerkleStore]. + pub const fn merkle_store(&self) -> &MerkleStore { + &self.store } // DESTRUCTORS @@ -100,14 +90,8 @@ impl AdviceInputs { /// Decomposes these `[Self]` into their raw components. #[allow(clippy::type_complexity)] - pub(crate) fn into_parts( - self, - ) -> (Vec, BTreeMap<[u8; 32], Vec>, BTreeMap<[u8; 32], MerkleSet>) { - let Self { - stack, - map, - merkle_sets, - } = self; - (stack, map, merkle_sets) + pub(crate) fn into_parts(self) -> (Vec, BTreeMap<[u8; 32], Vec>, MerkleStore) { + let Self { stack, map, store } = self; + (stack, map, store) } } diff --git a/processor/src/advice/mem_provider.rs b/processor/src/advice/mem_provider.rs index e4233e01f0..3f8b0f33bf 100644 --- a/processor/src/advice/mem_provider.rs +++ b/processor/src/advice/mem_provider.rs @@ -1,6 +1,6 @@ use super::{ AdviceInputs, AdviceProvider, AdviceSource, BTreeMap, ExecutionError, Felt, IntoBytes, - MerklePath, MerkleSet, NodeIndex, StarkField, Vec, Word, + MerklePath, MerkleStore, NodeIndex, Vec, Word, }; // MEMORY ADVICE PROVIDER @@ -14,18 +14,18 @@ pub struct MemAdviceProvider { step: u32, stack: Vec, map: BTreeMap<[u8; 32], Vec>, - sets: BTreeMap<[u8; 32], MerkleSet>, + store: MerkleStore, } impl From for MemAdviceProvider { fn from(inputs: AdviceInputs) -> Self { - let (mut stack, map, sets) = inputs.into_parts(); + let (mut stack, map, store) = inputs.into_parts(); stack.reverse(); Self { step: 0, stack, map, - sets, + store, } } } @@ -93,18 +93,9 @@ impl AdviceProvider for MemAdviceProvider { depth: &Felt, index: &Felt, ) -> Result { - // look up the merkle set and return an error if none is found - let merkle_set = self - .sets - .get(&root.into_bytes()) - .ok_or_else(|| ExecutionError::MerkleSetNotFound(root.into_bytes()))?; - - // get the tree node from the merkle set based on depth and index let index = NodeIndex::from_elements(depth, index) - .map_err(ExecutionError::MerkleSetLookupFailed)?; - let node = merkle_set.get_node(index).map_err(ExecutionError::MerkleSetLookupFailed)?; - - Ok(node) + .map_err(|_| ExecutionError::MerkleNodeIndex(*depth, *index))?; + self.store.get_node(root, index).map_err(ExecutionError::MerkleSetLookupFailed) } fn get_merkle_path( @@ -113,53 +104,27 @@ impl AdviceProvider for MemAdviceProvider { depth: &Felt, index: &Felt, ) -> Result { - // look up the merkle set and return an error if none is found - let merkle_set = self - .sets - .get(&root.into_bytes()) - .ok_or_else(|| ExecutionError::MerkleSetNotFound(root.into_bytes()))?; - - // get the Merkle path from the merkle set based on depth and index let index = NodeIndex::from_elements(depth, index) - .map_err(ExecutionError::MerkleSetLookupFailed)?; - let path = merkle_set.get_path(index).map_err(ExecutionError::MerkleSetLookupFailed)?; - - Ok(path) + .map_err(|_| ExecutionError::MerkleNodeIndex(*depth, *index))?; + self.store + .get_path(root, index) + .map(|value| value.path) + .map_err(ExecutionError::MerkleSetLookupFailed) } - fn update_merkle_leaf( + fn update_merkle_node( &mut self, root: Word, - index: Felt, - leaf_value: Word, - update_in_copy: bool, + depth: &Felt, + index: &Felt, + value: Word, ) -> Result { - // look up the merkle set and return error if none is found. if we are updating a copy, - // clone the merkle set; otherwise remove it from the map because the root will change, - // and we'll re-insert the set later under a different root. - let mut merkle_set = if update_in_copy { - // look up the merkle set and return an error if none is found - self.sets - .get(&root.into_bytes()) - .ok_or_else(|| ExecutionError::MerkleSetNotFound(root.into_bytes()))? - .clone() - } else { - self.sets - .remove(&root.into_bytes()) - .ok_or_else(|| ExecutionError::MerkleSetNotFound(root.into_bytes()))? - }; - - // get the Merkle path from the merkle set for the leaf at the specified index - let index = NodeIndex::new(merkle_set.depth(), index.as_int()); - let path = merkle_set.get_path(index).map_err(ExecutionError::MerkleSetLookupFailed)?; - - // update the merkle set and re-insert it into the map - merkle_set - .update_leaf(index.value(), leaf_value) - .map_err(ExecutionError::MerkleSetLookupFailed)?; - self.sets.insert(merkle_set.root().into_bytes(), merkle_set); - - Ok(path) + let node_index = NodeIndex::from_elements(depth, index) + .map_err(|_| ExecutionError::MerkleNodeIndex(*depth, *index))?; + self.store + .set_node(root, node_index, value) + .map(|root| root.path) + .map_err(ExecutionError::MerkleSetLookupFailed) } // CONTEXT MANAGEMENT @@ -170,13 +135,13 @@ impl AdviceProvider for MemAdviceProvider { } } -#[cfg(test)] impl MemAdviceProvider { - // ADVISE SETS + // ADVISE SETS TEST HELPERS // -------------------------------------------------------------------------------------------- - /// Returns true if the merkle set with the specified root is present in this advice provider. - pub fn has_merkle_set(&self, root: Word) -> bool { - self.sets.contains_key(&root.into_bytes()) + /// Returns true if the Merkle root exists for the advice provider Merkle store. + #[cfg(test)] + pub fn has_merkle_root(&self, root: Word) -> bool { + self.store.get_node(root, NodeIndex::root()).is_ok() } } diff --git a/processor/src/advice/mod.rs b/processor/src/advice/mod.rs index 9b9ae9729a..e7911710a0 100644 --- a/processor/src/advice/mod.rs +++ b/processor/src/advice/mod.rs @@ -1,11 +1,10 @@ -use super::{utils, ExecutionError, Felt, InputError, Word}; +use super::{ExecutionError, Felt, InputError, Word}; use vm_core::{ - crypto::merkle::{MerkleError, MerklePath, MerklePathSet, MerkleTree, NodeIndex, SimpleSmt}, + crypto::merkle::{MerklePath, MerkleStore, NodeIndex}, utils::{ collections::{BTreeMap, Vec}, IntoBytes, }, - StarkField, }; mod inputs; @@ -14,9 +13,6 @@ pub use inputs::AdviceInputs; mod mem_provider; pub use mem_provider::MemAdviceProvider; -mod merkle_set; -pub use merkle_set::MerkleSet; - mod source; pub use source::AdviceSource; @@ -47,8 +43,7 @@ pub use source::AdviceSource; /// implementation should error if the user attempts to insert this key again, instead of the /// common behavior of the maps to simply override the previous contents. This is a design /// decision to increase the runtime robustness of the execution. -/// 3. Provide merkle sets, that are mappings from a Merkle root its tree. The tree should yield -/// nodes & leaves, and will provide a Merkle path if a leaf is updated. +/// 3. Provide merkle tree interfaces, backed by a [MerkleStore]. pub trait AdviceProvider { // ACCESSORS // -------------------------------------------------------------------------------------------- @@ -148,12 +143,12 @@ pub trait AdviceProvider { /// identified by the specified root. /// - Path to the leaf at the specified index in the specified Merkle tree is not known to this /// advice provider. - fn update_merkle_leaf( + fn update_merkle_node( &mut self, root: Word, - index: Felt, - leaf_value: Word, - update_in_copy: bool, + depth: &Felt, + index: &Felt, + value: Word, ) -> Result; // CONTEXT MANAGEMENT @@ -186,8 +181,8 @@ where T::push_stack(self, source) } - fn insert_into_map(&mut self, key: Word, map: Vec) -> Result<(), ExecutionError> { - T::insert_into_map(self, key, map) + fn insert_into_map(&mut self, key: Word, values: Vec) -> Result<(), ExecutionError> { + T::insert_into_map(self, key, values) } fn get_tree_node( @@ -208,14 +203,14 @@ where T::get_merkle_path(self, root, depth, index) } - fn update_merkle_leaf( + fn update_merkle_node( &mut self, root: Word, - index: Felt, - leaf_value: Word, - update_in_copy: bool, + depth: &Felt, + index: &Felt, + value: Word, ) -> Result { - T::update_merkle_leaf(self, root, index, leaf_value, update_in_copy) + T::update_merkle_node(self, root, depth, index, value) } fn advance_clock(&mut self) { diff --git a/processor/src/chiplets/hasher/tests.rs b/processor/src/chiplets/hasher/tests.rs index 348445fd90..caf0dba541 100644 --- a/processor/src/chiplets/hasher/tests.rs +++ b/processor/src/chiplets/hasher/tests.rs @@ -3,10 +3,7 @@ use super::{ SiblingTableRow, SiblingTableUpdate, TraceFragment, Vec, Word, LINEAR_HASH, MP_VERIFY, MR_UPDATE_NEW, MR_UPDATE_OLD, RETURN_HASH, RETURN_STATE, TRACE_WIDTH, }; -use crate::{ - chiplets::hasher::{lookups::HasherLookupContext, HasherLookup}, - MerkleSet, -}; +use crate::chiplets::hasher::{lookups::HasherLookupContext, HasherLookup}; use rand_utils::rand_array; use vm_core::{ chiplets::hasher::{ @@ -15,7 +12,7 @@ use vm_core::{ STATE_COL_RANGE, }, code_blocks::CodeBlock, - crypto::merkle::NodeIndex, + crypto::merkle::{MerkleTree, NodeIndex}, Operation, StarkField, ONE, ZERO, }; @@ -119,7 +116,7 @@ fn hasher_build_merkle_root() { // build a Merkle tree let leaves = init_leaves(&[1, 2]); - let tree = MerkleSet::new_merkle_tree(leaves.to_vec()).unwrap(); + let tree = MerkleTree::new(leaves.to_vec()).unwrap(); // initialize the hasher and perform two Merkle branch verifications let mut hasher = Hasher::default(); @@ -189,7 +186,7 @@ fn hasher_build_merkle_root() { // build a Merkle tree let leaves = init_leaves(&[1, 2, 3, 4, 5, 6, 7, 8]); - let tree = MerkleSet::new_merkle_tree(leaves.to_vec()).unwrap(); + let tree = MerkleTree::new(leaves.to_vec()).unwrap(); // initialize the hasher and perform one Merkle branch verifications let mut hasher = Hasher::default(); @@ -350,7 +347,7 @@ fn hasher_update_merkle_root() { // build a Merkle tree let leaves = init_leaves(&[1, 2]); - let mut tree = MerkleSet::new_merkle_tree(leaves.to_vec()).unwrap(); + let mut tree = MerkleTree::new(leaves.to_vec()).unwrap(); // initialize the hasher and update both leaves let mut hasher = Hasher::default(); @@ -459,7 +456,7 @@ fn hasher_update_merkle_root() { // build a Merkle tree let leaves = init_leaves(&[1, 2, 3, 4, 5, 6, 7, 8]); - let mut tree = MerkleSet::new_merkle_tree(leaves.to_vec()).unwrap(); + let mut tree = MerkleTree::new(leaves.to_vec()).unwrap(); // initialize the hasher let mut hasher = Hasher::default(); diff --git a/processor/src/decorators/mod.rs b/processor/src/decorators/mod.rs index 85711a638c..6e673aa78b 100644 --- a/processor/src/decorators/mod.rs +++ b/processor/src/decorators/mod.rs @@ -277,13 +277,17 @@ mod tests { super::{AdviceInputs, Felt, FieldElement, Kernel, Operation, StarkField}, Process, }; - use crate::{MemAdviceProvider, MerkleSet, StackInputs, Word}; - use vm_core::{AdviceInjector, Decorator}; + use crate::{MemAdviceProvider, StackInputs, Word}; + use vm_core::{ + crypto::merkle::{MerkleStore, MerkleTree}, + AdviceInjector, Decorator, + }; #[test] fn inject_merkle_node() { let leaves = [init_leaf(1), init_leaf(2), init_leaf(3), init_leaf(4)]; - let tree = MerkleSet::new_merkle_tree(leaves.to_vec()).unwrap(); + let tree = MerkleTree::new(leaves.to_vec()).unwrap(); + let store = MerkleStore::default().with_merkle_tree(leaves).unwrap(); let stack_inputs = [ tree.root()[0].as_int(), tree.root()[1].as_int(), @@ -294,7 +298,7 @@ mod tests { ]; let stack_inputs = StackInputs::try_from_values(stack_inputs).unwrap(); - let advice_inputs = AdviceInputs::default().with_merkle_sets(vec![tree.clone()]).unwrap(); + let advice_inputs = AdviceInputs::default().with_merkle_store(store); let advice_provider = MemAdviceProvider::from(advice_inputs); let mut process = Process::new(Kernel::default(), stack_inputs, advice_provider); process.execute_op(Operation::Noop).unwrap(); diff --git a/processor/src/errors.rs b/processor/src/errors.rs index b896fc476a..99568d9531 100644 --- a/processor/src/errors.rs +++ b/processor/src/errors.rs @@ -16,6 +16,8 @@ use std::error::Error; pub enum ExecutionError { AdviceKeyNotFound(Word), AdviceStackReadFailed(u32), + MerkleNodeIndex(Felt, Felt), + MerkleUpdateInPlace, MerkleSetLookupFailed(MerkleError), MerkleSetNotFound([u8; 32]), MerkleSetUpdateFailed(MerkleError), @@ -48,6 +50,8 @@ impl Display for ExecutionError { let hex = to_hex(Felt::elements_as_bytes(key))?; write!(fmt, "Can't push values onto the advice stack: value for key {hex} not present in the advice map.") } + MerkleNodeIndex(depth, index) => write!(fmt, "The provided depth {depth} cannot be represented as Merkle index with the value {index}"), + MerkleUpdateInPlace => write!(fmt, "Update in place is not supported"), MerkleSetLookupFailed(reason) => write!(fmt, "Advice set lookup failed: {reason}"), MerkleSetNotFound(root) => write!(fmt, "Advice set with root {root:x?} not found"), MerkleSetUpdateFailed(reason) => write!(fmt, "Advice set update failed: {reason}"), diff --git a/processor/src/lib.rs b/processor/src/lib.rs index 140feff136..d4b6336d92 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -43,7 +43,7 @@ mod range; use range::RangeChecker; mod advice; -pub use advice::{AdviceInputs, AdviceProvider, AdviceSource, MemAdviceProvider, MerkleSet}; +pub use advice::{AdviceInputs, AdviceProvider, AdviceSource, MemAdviceProvider}; mod chiplets; use chiplets::Chiplets; diff --git a/processor/src/operations/crypto_ops.rs b/processor/src/operations/crypto_ops.rs index 25848dfc4b..31ab54df28 100644 --- a/processor/src/operations/crypto_ops.rs +++ b/processor/src/operations/crypto_ops.rs @@ -130,6 +130,11 @@ where /// # Panics /// Panics if the computed old root does not match the input root provided via the stack. pub(super) fn op_mrupdate(&mut self, copy: bool) -> Result<(), ExecutionError> { + // TODO https://github.com/0xPolygonMiden/miden-vm/issues/775 + if !copy { + return Err(ExecutionError::MerkleUpdateInPlace); + } + // read old node value, depth, index, tree root and new node values from the stack let old_node = [self.stack.get(3), self.stack.get(2), self.stack.get(1), self.stack.get(0)]; let depth = self.stack.get(4); @@ -143,7 +148,7 @@ where // match the specified depth. // TODO: in the future, we should be able to replace sub-trees and not just the leaves, // and, thus, the assert on depth would not be needed. - let path = self.advice_provider.update_merkle_leaf(old_root, index, new_node, copy)?; + let path = self.advice_provider.update_merkle_node(old_root, &depth, &index, new_node)?; assert_eq!(path.len(), depth.as_int() as usize); let merkle_tree_update = self.chiplets.update_merkle_root(old_node, new_node, &path, index); @@ -177,10 +182,11 @@ mod tests { super::{Felt, FieldElement, Operation, StarkField}, Process, }; - use crate::{AdviceInputs, MerkleSet, StackInputs, Word}; + use crate::{AdviceInputs, StackInputs, Word}; use rand_utils::rand_vector; use vm_core::{ chiplets::hasher::{apply_permutation, STATE_WIDTH}, + crypto::merkle::{MerkleStore, MerkleTree}, utils::collections::Vec, }; @@ -223,99 +229,39 @@ mod tests { fn op_mpverify() { let index = 5usize; let leaves = init_leaves(&[1, 2, 3, 4, 5, 6, 7, 8]); - let tree = MerkleSet::new_merkle_tree(leaves.to_vec()).unwrap(); + let tree = MerkleTree::new(leaves.to_vec()).unwrap(); + let store = MerkleStore::new().with_merkle_tree(leaves.clone()).unwrap(); + let root = tree.root(); + let leaf = leaves[index]; + let index = index as u64; + let depth = tree.depth() as u64; let stack_inputs = [ - tree.root()[0].as_int(), - tree.root()[1].as_int(), - tree.root()[2].as_int(), - tree.root()[3].as_int(), - index as u64, - tree.depth() as u64, - leaves[index][0].as_int(), - leaves[index][1].as_int(), - leaves[index][2].as_int(), - leaves[index][3].as_int(), + root[0].as_int(), + root[1].as_int(), + root[2].as_int(), + root[3].as_int(), + index, + depth, + leaf[0].as_int(), + leaf[1].as_int(), + leaf[2].as_int(), + leaf[3].as_int(), ]; - let advice_inputs = AdviceInputs::default().with_merkle_sets(vec![tree.clone()]).unwrap(); - let stack_inputs = StackInputs::try_from_values(stack_inputs).unwrap(); - let mut process = - Process::new_dummy_with_inputs_and_decoder_helpers(stack_inputs, advice_inputs); - - process.execute_op(Operation::MpVerify).unwrap(); - let expected_stack = build_expected(&[ - leaves[index][3], - leaves[index][2], - leaves[index][1], - leaves[index][0], - Felt::new(tree.depth() as u64), - Felt::new(index as u64), - tree.root()[3], - tree.root()[2], - tree.root()[1], - tree.root()[0], - ]); - assert_eq!(expected_stack, process.stack.trace_state()); - } - - #[test] - fn op_mrupdate_move() { - let leaves = init_leaves(&[1, 2, 3, 4, 5, 6, 7, 8]); - - let node_index = 1usize; - let new_node = init_leaf(9); - let mut new_leaves = leaves.clone(); - new_leaves[node_index] = new_node; - - let tree = MerkleSet::new_merkle_tree(leaves.clone()).unwrap(); - let new_tree = MerkleSet::new_merkle_tree(new_leaves).unwrap(); - - let stack_inputs = [ - new_node[0].as_int(), - new_node[1].as_int(), - new_node[2].as_int(), - new_node[3].as_int(), - tree.root()[0].as_int(), - tree.root()[1].as_int(), - tree.root()[2].as_int(), - tree.root()[3].as_int(), - node_index as u64, - tree.depth() as u64, - leaves[node_index][0].as_int(), - leaves[node_index][1].as_int(), - leaves[node_index][2].as_int(), - leaves[node_index][3].as_int(), - ]; + let depth = Felt::new(depth); + let index = Felt::new(index); - let advice_inputs = AdviceInputs::default().with_merkle_sets(vec![tree.clone()]).unwrap(); + let advice_inputs = AdviceInputs::default().with_merkle_store(store); let stack_inputs = StackInputs::try_from_values(stack_inputs).unwrap(); let mut process = Process::new_dummy_with_inputs_and_decoder_helpers(stack_inputs, advice_inputs); - // update the Merkle tree and discard the old copy - process.execute_op(Operation::MrUpdate(false)).unwrap(); + process.execute_op(Operation::MpVerify).unwrap(); let expected_stack = build_expected(&[ - new_tree.root()[3], - new_tree.root()[2], - new_tree.root()[1], - new_tree.root()[0], - Felt::new(tree.depth() as u64), - Felt::new(node_index as u64), - tree.root()[3], - tree.root()[2], - tree.root()[1], - tree.root()[0], - new_node[3], - new_node[2], - new_node[1], - new_node[0], + leaf[3], leaf[2], leaf[1], leaf[0], depth, index, root[3], root[2], root[1], root[0], ]); assert_eq!(expected_stack, process.stack.trace_state()); - - // make sure the old Merkle tree was discarded - assert!(!process.advice_provider.has_merkle_set(tree.root())); - assert!(process.advice_provider.has_merkle_set(new_tree.root())); } #[test] @@ -327,8 +273,8 @@ mod tests { let mut new_leaves = leaves.clone(); new_leaves[node_index] = new_node; - let tree = MerkleSet::new_merkle_tree(leaves.clone()).unwrap(); - let new_tree = MerkleSet::new_merkle_tree(new_leaves).unwrap(); + let tree = MerkleTree::new(leaves.clone()).unwrap(); + let new_tree = MerkleTree::new(new_leaves).unwrap(); let stack_inputs = [ new_node[0].as_int(), @@ -347,7 +293,8 @@ mod tests { leaves[node_index][3].as_int(), ]; - let advice_inputs = AdviceInputs::default().with_merkle_sets(vec![tree.clone()]).unwrap(); + let store = MerkleStore::new().with_merkle_tree(leaves.to_vec()).unwrap(); + let advice_inputs = AdviceInputs::default().with_merkle_store(store); let stack_inputs = StackInputs::try_from_values(stack_inputs).unwrap(); let mut process = Process::new_dummy_with_inputs_and_decoder_helpers(stack_inputs, advice_inputs); @@ -373,8 +320,8 @@ mod tests { assert_eq!(expected_stack, process.stack.trace_state()); // make sure both Merkle trees are still in the merkle set - assert!(process.advice_provider.has_merkle_set(tree.root())); - assert!(process.advice_provider.has_merkle_set(new_tree.root())); + assert!(process.advice_provider.has_merkle_root(tree.root())); + assert!(process.advice_provider.has_merkle_root(new_tree.root())); } // HELPER FUNCTIONS diff --git a/processor/src/trace/tests/chiplets/hasher.rs b/processor/src/trace/tests/chiplets/hasher.rs index fb073066ce..0c7611a49b 100644 --- a/processor/src/trace/tests/chiplets/hasher.rs +++ b/processor/src/trace/tests/chiplets/hasher.rs @@ -3,7 +3,7 @@ use super::{ rand_array, AdviceInputs, ExecutionTrace, Felt, FieldElement, Operation, Trace, AUX_TRACE_RAND_ELEMENTS, CHIPLETS_AUX_TRACE_OFFSET, NUM_RAND_ROWS, ONE, ZERO, }; -use crate::{MerkleSet, StackInputs}; +use crate::StackInputs; use core::ops::Range; use vm_core::{ chiplets::{ @@ -17,7 +17,7 @@ use vm_core::{ HASHER_NODE_INDEX_COL_IDX, HASHER_ROW_COL_IDX, HASHER_STATE_COL_RANGE, HASHER_TRACE_OFFSET, }, code_blocks::CodeBlock, - crypto::merkle::NodeIndex, + crypto::merkle::{MerkleStore, MerkleTree, NodeIndex}, decoder::{NUM_OP_BITS, OP_BITS_OFFSET}, utils::{collections::Vec, range}, StarkField, Word, DECODER_TRACE_OFFSET, @@ -405,7 +405,7 @@ pub fn b_chip_permutation() { fn b_chip_mpverify() { let index = 5usize; let leaves = init_leaves(&[1, 2, 3, 4, 5, 6, 7, 8]); - let tree = MerkleSet::new_merkle_tree(leaves.to_vec()).unwrap(); + let tree = MerkleTree::new(leaves.to_vec()).unwrap(); let stack_inputs = [ tree.root()[0].as_int(), @@ -420,7 +420,8 @@ fn b_chip_mpverify() { leaves[index][3].as_int(), ]; let stack_inputs = StackInputs::try_from_values(stack_inputs).unwrap(); - let advice_inputs = AdviceInputs::default().with_merkle_sets(vec![tree.clone()]).unwrap(); + let store = MerkleStore::new().with_merkle_tree(leaves.clone()).unwrap(); + let advice_inputs = AdviceInputs::default().with_merkle_store(store); let mut trace = build_trace_from_ops_with_inputs(vec![Operation::MpVerify], stack_inputs, advice_inputs); diff --git a/processor/src/trace/tests/hasher.rs b/processor/src/trace/tests/hasher.rs index 672b57a833..95be3f1cd9 100644 --- a/processor/src/trace/tests/hasher.rs +++ b/processor/src/trace/tests/hasher.rs @@ -3,10 +3,11 @@ use super::{ build_trace_from_ops_with_inputs, rand_array, AdviceInputs, Felt, LookupTableRow, Operation, Vec, Word, ONE, ZERO, }; -use crate::{chiplets::SiblingTableRow, MerkleSet, StackInputs}; +use crate::{chiplets::SiblingTableRow, StackInputs}; use vm_core::{ - chiplets::hasher::P1_COL_IDX, crypto::merkle::NodeIndex, FieldElement, StarkField, - AUX_TRACE_RAND_ELEMENTS, + chiplets::hasher::P1_COL_IDX, + crypto::merkle::{MerkleStore, MerkleTree, NodeIndex}, + FieldElement, StarkField, AUX_TRACE_RAND_ELEMENTS, }; // SIBLING TABLE TESTS @@ -15,7 +16,8 @@ use vm_core::{ #[test] #[allow(clippy::needless_range_loop)] fn hasher_p1_mp_verify() { - let tree = build_merkle_tree(); + let (tree, leaves) = build_merkle_tree(); + let store = MerkleStore::new().with_merkle_tree(leaves).unwrap(); let node = tree.get_node(NodeIndex::new(3, 1)).unwrap(); // build program inputs @@ -25,7 +27,7 @@ fn hasher_p1_mp_verify() { append_word(&mut init_stack, tree.root()); init_stack.reverse(); let stack_inputs = StackInputs::try_from_values(init_stack).unwrap(); - let advice_inputs = AdviceInputs::default().with_merkle_sets(vec![tree]).unwrap(); + let advice_inputs = AdviceInputs::default().with_merkle_store(store); // build execution trace and extract the sibling table column from it let ops = vec![Operation::MpVerify]; @@ -44,7 +46,7 @@ fn hasher_p1_mp_verify() { #[test] #[allow(clippy::needless_range_loop)] fn hasher_p1_mr_update() { - let tree = build_merkle_tree(); + let (tree, leaves) = build_merkle_tree(); let index = 5_u64; let old_node = tree.get_node(NodeIndex::new(3, index)).unwrap(); let new_node = init_leaf(11); @@ -59,10 +61,11 @@ fn hasher_p1_mr_update() { init_stack.reverse(); let stack_inputs = StackInputs::try_from_values(init_stack).unwrap(); - let advice_inputs = AdviceInputs::default().with_merkle_sets(vec![tree]).unwrap(); + let store = MerkleStore::new().with_merkle_tree(leaves).unwrap(); + let advice_inputs = AdviceInputs::default().with_merkle_store(store); // build execution trace and extract the sibling table column from it - let ops = vec![Operation::MrUpdate(false)]; + let ops = vec![Operation::MrUpdate(true)]; let mut trace = build_trace_from_ops_with_inputs(ops, stack_inputs, advice_inputs); let alphas = rand_array::(); let aux_columns = trace.build_aux_segment(&[], &alphas).unwrap(); @@ -145,10 +148,10 @@ fn hasher_p1_mr_update() { // HELPER FUNCTIONS // ================================================================================================ -fn build_merkle_tree() -> MerkleSet { +fn build_merkle_tree() -> (MerkleTree, Vec) { // build a Merkle tree let leaves = init_leaves(&[1, 2, 3, 4, 5, 6, 7, 8]); - MerkleSet::new_merkle_tree(leaves.to_vec()).unwrap() + (MerkleTree::new(leaves.clone()).unwrap(), leaves) } fn init_leaves(values: &[u64]) -> Vec { diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 633c253a16..b04271569c 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -18,7 +18,7 @@ use winter_prover::Trace; pub use air::{DeserializationError, ExecutionProof, FieldExtension, HashFunction, ProofOptions}; pub use processor::{ math, utils, AdviceInputs, AdviceProvider, Digest, ExecutionError, Hasher, InputError, - MemAdviceProvider, MerkleError, MerkleSet, Program, StackInputs, StackOutputs, Word, + MemAdviceProvider, MerkleError, Program, StackInputs, StackOutputs, Word, }; pub use winter_prover::StarkProof; From e1ebfd9592ed815675fad3d98e3a892e689f0ce0 Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Tue, 21 Mar 2023 21:30:58 +0100 Subject: [PATCH 31/47] refactor: remove unused processor error variants related issue: #774 --- .../integration/stdlib/crypto/fri/mod.rs | 4 +-- processor/src/advice/mem_provider.rs | 32 +++++++++++++------ processor/src/errors.rs | 21 +++++++----- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/miden/tests/integration/stdlib/crypto/fri/mod.rs b/miden/tests/integration/stdlib/crypto/fri/mod.rs index 00d10e1d08..3734d20949 100644 --- a/miden/tests/integration/stdlib/crypto/fri/mod.rs +++ b/miden/tests/integration/stdlib/crypto/fri/mod.rs @@ -76,7 +76,7 @@ fn fri_fold4_ext2_remainder64() { let (advice_provider, stack, position_eval, alphas, commitments, remainder, num_queries) = fri_prove_verify_fold4_ext2(trace_len_e).expect("should not panic"); - let stack = prepare_advice( + let advice_stack = prepare_advice( depth, domain_size, num_queries, @@ -93,7 +93,7 @@ fn fri_fold4_ext2_remainder64() { for path_set in &advice_provider.0 { store.add_merkle_path_set(&path_set).unwrap(); } - let test = build_test!(source, &[domain_generator], &stack, store, advice_map.clone()); + let test = build_test!(source, &[domain_generator], &advice_stack, store, advice_map.clone()); test.expect_stack(&[]); } diff --git a/processor/src/advice/mem_provider.rs b/processor/src/advice/mem_provider.rs index 3f8b0f33bf..50cb03a591 100644 --- a/processor/src/advice/mem_provider.rs +++ b/processor/src/advice/mem_provider.rs @@ -93,9 +93,15 @@ impl AdviceProvider for MemAdviceProvider { depth: &Felt, index: &Felt, ) -> Result { - let index = NodeIndex::from_elements(depth, index) - .map_err(|_| ExecutionError::MerkleNodeIndex(*depth, *index))?; - self.store.get_node(root, index).map_err(ExecutionError::MerkleSetLookupFailed) + let index = NodeIndex::from_elements(depth, index).map_err(|_| { + ExecutionError::InvalidNodeIndex { + depth: *depth, + value: *index, + } + })?; + self.store + .get_node(root, index) + .map_err(ExecutionError::MerkleStoreLookupFailed) } fn get_merkle_path( @@ -104,12 +110,16 @@ impl AdviceProvider for MemAdviceProvider { depth: &Felt, index: &Felt, ) -> Result { - let index = NodeIndex::from_elements(depth, index) - .map_err(|_| ExecutionError::MerkleNodeIndex(*depth, *index))?; + let index = NodeIndex::from_elements(depth, index).map_err(|_| { + ExecutionError::InvalidNodeIndex { + depth: *depth, + value: *index, + } + })?; self.store .get_path(root, index) .map(|value| value.path) - .map_err(ExecutionError::MerkleSetLookupFailed) + .map_err(ExecutionError::MerkleStoreLookupFailed) } fn update_merkle_node( @@ -119,12 +129,16 @@ impl AdviceProvider for MemAdviceProvider { index: &Felt, value: Word, ) -> Result { - let node_index = NodeIndex::from_elements(depth, index) - .map_err(|_| ExecutionError::MerkleNodeIndex(*depth, *index))?; + let node_index = NodeIndex::from_elements(depth, index).map_err(|_| { + ExecutionError::InvalidNodeIndex { + depth: *depth, + value: *index, + } + })?; self.store .set_node(root, node_index, value) .map(|root| root.path) - .map_err(ExecutionError::MerkleSetLookupFailed) + .map_err(ExecutionError::MerkleStoreUpdateFailed) } // CONTEXT MANAGEMENT diff --git a/processor/src/errors.rs b/processor/src/errors.rs index 99568d9531..4d260e51c1 100644 --- a/processor/src/errors.rs +++ b/processor/src/errors.rs @@ -16,11 +16,10 @@ use std::error::Error; pub enum ExecutionError { AdviceKeyNotFound(Word), AdviceStackReadFailed(u32), - MerkleNodeIndex(Felt, Felt), + InvalidNodeIndex { depth: Felt, value: Felt }, MerkleUpdateInPlace, - MerkleSetLookupFailed(MerkleError), - MerkleSetNotFound([u8; 32]), - MerkleSetUpdateFailed(MerkleError), + MerkleStoreLookupFailed(MerkleError), + MerkleStoreUpdateFailed(MerkleError), CodeBlockNotFound(Digest), CallerNotInSyscall, DivideByZero(u32), @@ -50,11 +49,17 @@ impl Display for ExecutionError { let hex = to_hex(Felt::elements_as_bytes(key))?; write!(fmt, "Can't push values onto the advice stack: value for key {hex} not present in the advice map.") } - MerkleNodeIndex(depth, index) => write!(fmt, "The provided depth {depth} cannot be represented as Merkle index with the value {index}"), + InvalidNodeIndex { depth, value } => write!( + fmt, + "The provided index {value} is out of bounds for a node at depth {depth}" + ), MerkleUpdateInPlace => write!(fmt, "Update in place is not supported"), - MerkleSetLookupFailed(reason) => write!(fmt, "Advice set lookup failed: {reason}"), - MerkleSetNotFound(root) => write!(fmt, "Advice set with root {root:x?} not found"), - MerkleSetUpdateFailed(reason) => write!(fmt, "Advice set update failed: {reason}"), + MerkleStoreLookupFailed(reason) => { + write!(fmt, "Advice provider Merkle store backend lookup failed: {reason}") + } + MerkleStoreUpdateFailed(reason) => { + write!(fmt, "Advice provider Merkle store backend update failed: {reason}") + } AdviceStackReadFailed(step) => write!(fmt, "Advice stack read failed at step {step}"), CodeBlockNotFound(digest) => { let hex = to_hex(&digest.as_bytes())?; From d91847c6cb1079487a64e0f05801fc3fe110f4c3 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Fri, 24 Mar 2023 01:36:17 -0700 Subject: [PATCH 32/47] fix: failing FRI tests after advice provider refactoring --- miden/tests/integration/stdlib/crypto/fri/mod.rs | 5 ++--- .../tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/miden/tests/integration/stdlib/crypto/fri/mod.rs b/miden/tests/integration/stdlib/crypto/fri/mod.rs index 3734d20949..cdfc789863 100644 --- a/miden/tests/integration/stdlib/crypto/fri/mod.rs +++ b/miden/tests/integration/stdlib/crypto/fri/mod.rs @@ -15,7 +15,6 @@ pub use verifier_fri_e2f4::*; mod remainder; #[test] -#[ignore = "the unbound integrated test struct needs to be refactored to use merkle store"] fn fri_fold4_ext2_remainder32() { let source = " use.std::crypto::fri::frie2f4 @@ -57,7 +56,6 @@ fn fri_fold4_ext2_remainder32() { } #[test] -#[ignore = "the unbound integrated test struct needs to be refactored to use merkle store"] fn fri_fold4_ext2_remainder64() { let source = " use.std::crypto::fri::frie2f4 @@ -122,7 +120,8 @@ fn prepare_advice( stack.extend_from_slice(&com[(4 * i)..(4 * i + 4)]); stack.extend_from_slice(&alphas[(4 * i)..(4 * i + 2)]); - stack.extend_from_slice(&vec![current_depth - 1, current_domain_size]); + // TODO: explain why "-2" for depth; related to folding factor? + stack.extend_from_slice(&vec![current_depth - 2, current_domain_size]); current_depth -= 2; } diff --git a/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs b/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs index 1165ba7484..97d96dde68 100644 --- a/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs +++ b/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs @@ -333,7 +333,7 @@ fn iterate_query_fold_4_quad_ext( // Assumes the num_partitions == 1 let position_index = folded_pos; - let tree_depth = log2(target_domain_size) + 1; + let tree_depth = log2(target_domain_size); let query_nodes = m_path_sets[depth] .get_node(NodeIndex::new(tree_depth as u8, position_index as u64)) @@ -456,7 +456,7 @@ impl UnBatch for MidenFriVerifierChannel Date: Thu, 23 Mar 2023 13:49:55 +0100 Subject: [PATCH 33/47] feat: deprecate mtree_cwm in favor of new mtree_merge With the introduction of [MerkleStore], the copy in place for Merkle trees became obsolete. This commit removes the `mtree_cwm` from the instruction set. This commit introduces a new instruction: `mtree_merge`. It will merge the roots of two existing trees, increment their depth by 1 and creating a new tree. closes #775 --- CHANGELOG.md | 3 +- air/src/stack/op_flags/mod.rs | 2 +- air/src/stack/op_flags/tests.rs | 2 +- .../src/assembler/instruction/crypto_ops.rs | 57 +++--- assembly/src/assembler/instruction/mod.rs | 2 +- assembly/src/parsers/context.rs | 2 +- assembly/src/parsers/nodes.rs | 4 +- assembly/src/parsers/serde/deserialization.rs | 2 +- assembly/src/parsers/serde/mod.rs | 2 +- assembly/src/parsers/serde/serialization.rs | 2 +- core/Cargo.toml | 2 +- core/src/operations/decorators/advice.rs | 18 +- core/src/operations/mod.rs | 23 +-- .../assembly/cryptographic_operations.md | 2 +- docs/src/user_docs/assembly/io_operations.md | 4 +- miden/examples/fib/fib.inputs | 2 +- miden/examples/nprime/nprime.inputs | 2 +- miden/src/cli/data.rs | 8 +- .../tests/integration/air/chiplets/hasher.rs | 48 ++++- .../integration/operations/crypto_ops.rs | 19 -- .../integration/stdlib/crypto/fri/mod.rs | 8 +- processor/src/advice/inputs.rs | 4 +- processor/src/advice/mem_provider.rs | 4 + processor/src/advice/mod.rs | 19 +- processor/src/chiplets/hasher/lookups.rs | 4 +- processor/src/decorators/mod.rs | 25 +++ processor/src/errors.rs | 4 + processor/src/operations/crypto_ops.rs | 187 ++++++++++++------ processor/src/operations/mod.rs | 2 +- processor/src/trace/tests/hasher.rs | 2 +- 30 files changed, 309 insertions(+), 156 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1ff573e0e..98072c0df2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ - Renamed `ProgramInfo` to `ExecutionDetails` since there is another `ProgramInfo` struct in the source code. #### Assembly -- Added new instructions: `is_odd`, `assert_eqw`. +- Added new instructions: `is_odd`, `assert_eqw`, `mtree_merge`. +- [BREAKING] Removed `mtree_cwm` instruction. ## 0.4.0 (2023-02-27) diff --git a/air/src/stack/op_flags/mod.rs b/air/src/stack/op_flags/mod.rs index f0959bcef5..a7ba7b7214 100644 --- a/air/src/stack/op_flags/mod.rs +++ b/air/src/stack/op_flags/mod.rs @@ -839,7 +839,7 @@ impl OpFlags { /// Operation Flag of MRUPDATE operation. #[inline(always)] pub fn mrupdate(&self) -> E { - self.degree4_op_flags[get_op_index(Operation::MrUpdate(true).op_code())] + self.degree4_op_flags[get_op_index(Operation::MrUpdate.op_code())] } /// Operation Flag of PUSH operation. diff --git a/air/src/stack/op_flags/tests.rs b/air/src/stack/op_flags/tests.rs index 31ff6f97be..51474b81f9 100644 --- a/air/src/stack/op_flags/tests.rs +++ b/air/src/stack/op_flags/tests.rs @@ -219,7 +219,7 @@ fn composite_flags() { // ------ no change 4 --------------------------------------------------------------------- - let op_no_change_4 = [Operation::MrUpdate(false), Operation::ReadW, Operation::Ext2Mul]; + let op_no_change_4 = [Operation::MrUpdate, Operation::ReadW, Operation::Ext2Mul]; for op in op_no_change_4 { // frame initialised with an op operation. let frame = generate_evaluation_frame(op.op_code().into()); diff --git a/assembly/src/assembler/instruction/crypto_ops.rs b/assembly/src/assembler/instruction/crypto_ops.rs index 68cd635ec1..9a076f74c1 100644 --- a/assembly/src/assembler/instruction/crypto_ops.rs +++ b/assembly/src/assembler/instruction/crypto_ops.rs @@ -145,27 +145,33 @@ pub(super) fn mtree_set(span: &mut SpanBuilder) -> Result, Ass // stack: [d, i, R_old, V_new, ...] // stack: [V_old, R_new, ...] (29 cycles) - update_mtree(span, true) + update_mtree(span) } -/// Appends the MRUPDATE op with a parameter of "true" and stack manipulations to the span block as -/// required to copy a Merkle tree with root R and update the node in the copied tree at depth d -/// and index i to value V. The stack is expected to be arranged as follows (from the top): -/// - depth of the node, 1 element; this is expected to be the depth of the Merkle tree -/// - index of the node, 1 element -/// - current root of the tree, 4 elements -/// - new value of the node, 4 element +/// Creates a new Merkle tree in the advice provider by combining trees with the specified roots. +/// The stack is expected to be arranged as follows (from the top): +/// - root of the right tree, 4 elements +/// - root of the left tree, 4 elements /// -/// After the operations are executed, the stack will be arranged as follows: -/// - old value of the node, 4 elements -/// - new root of the tree after the update, 4 elements +/// The operation will merge the Merkle trees with the provided roots, producing a new merged root +/// with incremented depth. After the operations are executed, the stack will be arranged as +/// follows: +/// - merged root, 4 elements /// -/// This operation takes 29 VM cycles. -pub(super) fn mtree_cwm(span: &mut SpanBuilder) -> Result, AssemblyError> { - // stack: [d, i, R_old, V_new, ...] +/// This operation will fail if either of the input roots doesn't exist as Merkle tree in the +/// advice provider. +/// +/// This operation takes 16 VM cycles. +pub(super) fn mtree_merge(span: &mut SpanBuilder) -> Result, AssemblyError> { + // stack input: [R_rhs, R_lhs, ...] + // stack output: [R_merged, ...] - // stack: [V_old, R_new, ...] (29 cycles) - update_mtree(span, true) + // invoke the advice provider function to merge 2 Merkle trees defined by the roots on the top + // of the operand stack + span.push_decorator(Decorator::Advice(AdviceInjector::MerkleMerge)); + + // perform the `hmerge`, updating the operand stack + hmerge(span) } // MERKLE TREES - HELPERS @@ -178,21 +184,21 @@ pub(super) fn mtree_cwm(span: &mut SpanBuilder) -> Result, Ass /// - depth of the node, 1 element /// - index of the node, 1 element /// - root of the Merkle tree, 4 elements -/// - new value of the node, 4 elements (only in the case of mtree_set and mtree_cwm) +/// - new value of the node, 4 elements (only in the case of mtree_set) /// /// After the operations are executed, the stack will be arranged as follows: /// - old value of the node, 4 elements /// - depth of the node, 1 element /// - index of the node, 1 element /// - root of the Merkle tree, 4 elements -/// - new value of the node, 4 elements (only in the case of mtree_set and mtree_cwm) +/// - new value of the node, 4 elements (only in the case of mtree_set) /// /// This operation takes 4 VM cycles. fn read_mtree_node(span: &mut SpanBuilder) { // The stack should be arranged in the following way: [d, i, R, ...] so that the decorator // can fetch the node value from the root. In the `mtree.get` operation we have the stack in - // the following format: [d, i, R], whereas in the case of `mtree.set` and `mtree.cwm` we - // would also have the new node value post the tree root: [d, i, R, V_new]. + // the following format: [d, i, R], whereas in the case of `mtree.set` we would also have the + // new node value post the tree root: [d, i, R, V_new] // // pops the value of the node we are looking for from the advice stack span.push_decorator(Decorator::Advice(AdviceInjector::MerkleNode)); @@ -202,11 +208,11 @@ fn read_mtree_node(span: &mut SpanBuilder) { span.push_op_many(Read, 4); } -/// Update a node in the merkle tree. The `copy` flag will be passed as argument of the `MrUpdate` -/// operation. +/// Update a node in the merkle tree. This operation will always copy the tree into a new instance, +/// and perform the mutation on the copied tree. /// /// This operation takes 29 VM cycles. -fn update_mtree(span: &mut SpanBuilder, copy: bool) -> Result, AssemblyError> { +fn update_mtree(span: &mut SpanBuilder) -> Result, AssemblyError> { // stack: [d, i, R_old, V_new, ...] // output: [R_new, R_old, V_new, V_old, ...] @@ -249,10 +255,9 @@ fn update_mtree(span: &mut SpanBuilder, copy: bool) -> Result, // Update the Merkle tree // ======================================================================================== - // Update the node at depth `d` and position `i`. Copy of the Merkle tree depends on the - // value of the `copy` flag. + // Update the node at depth `d` and position `i`. It will always copy the Merkle tree. // stack: [R_new, d, i, R_old, V_new, V_old, ...] - MrUpdate(copy), + MrUpdate, // Drop unecessary values // ======================================================================================== diff --git a/assembly/src/assembler/instruction/mod.rs b/assembly/src/assembler/instruction/mod.rs index 6a874f79d2..9df041edf3 100644 --- a/assembly/src/assembler/instruction/mod.rs +++ b/assembly/src/assembler/instruction/mod.rs @@ -297,7 +297,7 @@ impl Assembler { Instruction::HMerge => crypto_ops::hmerge(span), Instruction::MTreeGet => crypto_ops::mtree_get(span), Instruction::MTreeSet => crypto_ops::mtree_set(span), - Instruction::MTreeCwm => crypto_ops::mtree_cwm(span), + Instruction::MTreeMerge => crypto_ops::mtree_merge(span), Instruction::FriExt2Fold4 => span.add_op(FriE2F4), // ----- exec/call instructions ------------------------------------------------------- diff --git a/assembly/src/parsers/context.rs b/assembly/src/parsers/context.rs index 3cb2e9e0ac..e7f2d143a3 100644 --- a/assembly/src/parsers/context.rs +++ b/assembly/src/parsers/context.rs @@ -499,7 +499,7 @@ impl ParserContext { "mtree_get" => simple_instruction(op, MTreeGet), "mtree_set" => simple_instruction(op, MTreeSet), - "mtree_cwm" => simple_instruction(op, MTreeCwm), + "mtree_merge" => simple_instruction(op, MTreeMerge), "fri_ext2fold4" => simple_instruction(op, FriExt2Fold4), diff --git a/assembly/src/parsers/nodes.rs b/assembly/src/parsers/nodes.rs index 28d2ecd42f..5e8deef161 100644 --- a/assembly/src/parsers/nodes.rs +++ b/assembly/src/parsers/nodes.rs @@ -268,7 +268,7 @@ pub enum Instruction { HPerm, MTreeGet, MTreeSet, - MTreeCwm, + MTreeMerge, FriExt2Fold4, // ----- exec / call -------------------------------------------------------------------------- @@ -543,7 +543,7 @@ impl fmt::Display for Instruction { Self::HPerm => write!(f, "hperm"), Self::MTreeGet => write!(f, "mtree_get"), Self::MTreeSet => write!(f, "mtree_set"), - Self::MTreeCwm => write!(f, "mtree_cwm"), + Self::MTreeMerge => write!(f, "mtree_merge"), Self::FriExt2Fold4 => write!(f, "fri_ext2fold4"), // ----- exec / call ------------------------------------------------------------------ diff --git a/assembly/src/parsers/serde/deserialization.rs b/assembly/src/parsers/serde/deserialization.rs index d75587974b..f160b55b07 100644 --- a/assembly/src/parsers/serde/deserialization.rs +++ b/assembly/src/parsers/serde/deserialization.rs @@ -326,7 +326,7 @@ impl Deserializable for Instruction { OpCode::HPerm => Ok(Instruction::HPerm), OpCode::MTreeGet => Ok(Instruction::MTreeGet), OpCode::MTreeSet => Ok(Instruction::MTreeSet), - OpCode::MTreeCwm => Ok(Instruction::MTreeCwm), + OpCode::MTreeMerge => Ok(Instruction::MTreeMerge), OpCode::FriExt2Fold4 => Ok(Instruction::FriExt2Fold4), // ----- exec / call ------------------------------------------------------------------ diff --git a/assembly/src/parsers/serde/mod.rs b/assembly/src/parsers/serde/mod.rs index ea4d1a7db7..71baf291e1 100644 --- a/assembly/src/parsers/serde/mod.rs +++ b/assembly/src/parsers/serde/mod.rs @@ -264,7 +264,7 @@ pub enum OpCode { HPerm = 232, MTreeGet = 233, MTreeSet = 234, - MTreeCwm = 235, + MTreeMerge = 235, FriExt2Fold4 = 236, // ----- exec / call -------------------------------------------------------------------------- diff --git a/assembly/src/parsers/serde/serialization.rs b/assembly/src/parsers/serde/serialization.rs index e0e9b413b3..10b39790b6 100644 --- a/assembly/src/parsers/serde/serialization.rs +++ b/assembly/src/parsers/serde/serialization.rs @@ -443,7 +443,7 @@ impl Serializable for Instruction { Self::HPerm => OpCode::HPerm.write_into(target)?, Self::MTreeGet => OpCode::MTreeGet.write_into(target)?, Self::MTreeSet => OpCode::MTreeSet.write_into(target)?, - Self::MTreeCwm => OpCode::MTreeCwm.write_into(target)?, + Self::MTreeMerge => OpCode::MTreeMerge.write_into(target)?, Self::FriExt2Fold4 => OpCode::FriExt2Fold4.write_into(target)?, // ----- exec / call ------------------------------------------------------------------ diff --git a/core/Cargo.toml b/core/Cargo.toml index 41e3301fbf..90a70dccbd 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -22,7 +22,7 @@ std = ["math/std", "winter-utils/std"] [dependencies] math = { package = "winter-math", version = "0.5", default-features = false } #crypto = { package = "miden-crypto", version = "0.1", default-features = false } -crypto = { package = "miden-crypto", git = "https://github.com/0xPolygonMiden/crypto", branch = "next", default-features = false } +crypto = { package = "miden-crypto", git = "https://github.com/0xPolygonMiden/crypto", rev = "75af3d474b0f5aa03ee65708ec3fd9397426f29c", default-features = false } winter-utils = { package = "winter-utils", version = "0.5", default-features = false } [dev-dependencies] diff --git a/core/src/operations/decorators/advice.rs b/core/src/operations/decorators/advice.rs index 3e583b272f..6a00ee5e66 100644 --- a/core/src/operations/decorators/advice.rs +++ b/core/src/operations/decorators/advice.rs @@ -11,6 +11,17 @@ pub enum AdviceInjector { /// - root of the tree, 4 elements MerkleNode, + /// Creates a new Merkle tree in the advice provider by combining Merkle trees with the + /// specified roots. The root of the new tree is defined as `Hash(left_root, right_root)`. + /// + /// The operand stack is expected to be arranged as follows: + /// - root of the right tree, 4 elements. + /// - root of the left tree, 4 elements. + /// + /// After the operation, both the original trees and the new tree remains in the advice + /// provider (i.e., the input trees are not removed). + MerkleMerge, + /// Pushes the result of [u64] division (both the quotient and the remainder) onto the advice /// stack. The operand stack is expected to be arranged as follows (from the top): /// - divisor split into two 32-bit elements @@ -25,7 +36,11 @@ pub enum AdviceInjector { /// stack as key. MapValue, - /// Pushes a list of words from the memory starting from the specified address. + /// Inserts a list of words that are read from VM memory into the advice map under the key + /// specified by the word at the top of the operand stack. + /// + /// The first internal u32 specifies the starting address of the memory region, and the second + /// specifies the number of words to be read. Memory(u32, u32), /// Given an element of quadratic extension field, it computes multiplicative inverse and @@ -42,6 +57,7 @@ impl fmt::Display for AdviceInjector { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::MerkleNode => write!(f, "merkle_node"), + Self::MerkleMerge => write!(f, "merkle_merge"), Self::DivResultU64 => write!(f, "div_result_u64"), Self::MapValue => write!(f, "map_value"), Self::Memory(start_addr, num_words) => write!(f, "mem({start_addr}, {num_words})"), diff --git a/core/src/operations/mod.rs b/core/src/operations/mod.rs index 2436f71496..8181886968 100644 --- a/core/src/operations/mod.rs +++ b/core/src/operations/mod.rs @@ -395,14 +395,13 @@ pub enum Operation { /// - new value of the node, 4 element /// /// The Merkle path for the node is expected to be provided by the prover non-deterministically - /// (via merkle sets). At the end of the operation, the old node value is replaced with the - /// new root value computed based on the provided path. Everything else on the stack remains the - /// same. + /// via the advice provider. At the end of the operation, the old node value is replaced with + /// the new root value, that is computed based on the provided path. Everything else on the + /// stack remains the same. /// - /// If the boolean parameter is set to false, at the end of the operation the merkle set with - /// the specified root will be removed from the advice provider. Otherwise, the advice - /// provider will keep track of both, the old and the new merkle sets. - MrUpdate(bool), + /// The tree will always be copied into a new instance, meaning the advice provider will keep + /// track of both the old and new Merkle trees. + MrUpdate, /// TODO: add docs FriE2F4, @@ -512,7 +511,7 @@ impl Operation { Self::Split => 0b0101_1100, Self::Loop => 0b0101_1110, - Self::MrUpdate(_) => 0b0110_0000, + Self::MrUpdate => 0b0110_0000, Self::Push(_) => 0b0110_0100, Self::SysCall => 0b0110_1000, Self::Call => 0b0110_1100, @@ -668,13 +667,7 @@ impl fmt::Display for Operation { // ----- cryptographic operations ----------------------------------------------------- Self::HPerm => write!(f, "hperm"), Self::MpVerify => write!(f, "mpverify"), - Self::MrUpdate(copy) => { - if *copy { - write!(f, "mrupdate(copy)") - } else { - write!(f, "mrupdate(move)") - } - } + Self::MrUpdate => write!(f, "mrupdate"), Self::FriE2F4 => write!(f, "frie2f4"), } } diff --git a/docs/src/user_docs/assembly/cryptographic_operations.md b/docs/src/user_docs/assembly/cryptographic_operations.md index 048fccc30e..1278dde00d 100644 --- a/docs/src/user_docs/assembly/cryptographic_operations.md +++ b/docs/src/user_docs/assembly/cryptographic_operations.md @@ -11,4 +11,4 @@ Miden assembly provides a set of instructions for performing common cryptographi | hmerge
- *(16 cycles)* | [B, A, ...] | [C, ...] | $C \leftarrow hash(A,B)$
where, $hash()$ computes a 2-to-1 Rescue Prime Optimized hash. | | mtree_get
- *(9 cycles)* | [d, i, R, ...] | [V, R, ...] | Verifies that a Merkle tree with root $R$ opens to node $V$ at depth $d$ and index $i$. Merkle tree with root $R$ must be present in the advice provider, otherwise execution fails. | | mtree_set
- *(29 cycles)* | [d, i, R, V', ...] | [V, R', ...] | Updates a node in the Merkle tree with root $R$ at depth $d$ and index $i$ to value $V'$. $R'$ is the Merkle root of the resulting tree and $V$ is old value of the node. Merkle tree with root $R$ must be present in the advice provider, otherwise execution fails. At the end of the operation the advice provider will contain both Merkle trees. | -| mtree_cwm
- *(29 cycles)* | [d, i, R, V', ...] | [V, R', ...] | Copies a Merkle tree with root $R$ and updates a node at depth $d$ and index $i$ in the copied tree to value $V'$. $R'$ is the Merkle root of the new tree and $V$ is old value of the node. Merkle tree with root $R$ must be present in the advice provider, otherwise execution fails. At the end of the operation the advice provider will contain both Merkle trees. | +| mtree_merge
- *(16 cycles)* | [R, L, ...] | [M, ...] | Merges two Merkle trees with the provided roots R (right), L (left) into a new Merkle tree with root M (merged). The input trees are retained in the advice provider. | diff --git a/docs/src/user_docs/assembly/io_operations.md b/docs/src/user_docs/assembly/io_operations.md index 54b590254b..6aa4916347 100644 --- a/docs/src/user_docs/assembly/io_operations.md +++ b/docs/src/user_docs/assembly/io_operations.md @@ -3,7 +3,7 @@ Miden assembly provides a set of instructions for moving data between the stack * **Program code**: values to be moved onto the stack can be hard-coded in a program's source code. * **Environment**: values can be moved onto the stack from environment variables. Currently, the available environment variables are *stack_depth*, which holds the current depth of the stack, and *local_address*, which stores absolute addresses of local variables. In the future, other environment variables may be added. -* **Advice stack**: values can be moved onto the stack from the advice provider by popping the from the advice stack. There is no limit on the number of values in the advice stack. +* **Advice stack**: values can be moved onto the stack from the advice provider by popping them from the advice stack. There is no limit on the number of values in the advice stack. * **Memory**: values can be moved between the stack and random-access memory. The memory is word-addressable, meaning, four elements are located at each address, and we can read and write elements to/from memory in batches of four. Memory can be accessed via absolute memory references (i.e., via memory addresses) as well as via local procedure references (i.e., local index). The latter approach ensures that a procedure does not access locals of another procedure. In the future several other sources such as *storage* and *logs* may be added. @@ -36,7 +36,7 @@ In both case the values must still encode valid field elements. | Instruction | Stack_input | Stack_output | Notes | | --------------- | ----------- | ------------ | ------------------------------------------ | | adv_push.*n*
- *(n cycles)* | [ ... ] | [a, ... ] | $a \leftarrow stack.pop()$
Pops $n$ values from the advice stack and pushes them onto the operand stack. Valid for $n \in \{1, ..., 16\}$.
Fails if the advice stack has fewer than $n$ values. | -| adv_loadw
- *(1 cycle)* | [0, 0, 0, 0, ... ] | [A, ... ] | $A \leftarrow stack.pop(4)$
Pop the next word (4 elements) from the advice stack and pushes them onto the operand stack.
Fails if the advice stack has fewer than $4$ values. | +| adv_loadw
- *(1 cycle)* | [0, 0, 0, 0, ... ] | [A, ... ] | $A \leftarrow stack.pop(4)$
Pop the next word (4 elements) from the advice stack and overwrites the first word of the operand stack (4 elements) with them.
Fails if the advice stack has fewer than $4$ values. | | adv_pipe
- *(2 cycles)* | [S2, S1, S0, a, ... ] | [T2, T1, T0, b, ... ] | $[T_0, T_1, T_2] \leftarrow permute(S_0, stack.pop(4), stack.pop(4))$
$b \leftarrow a + 2$
Pops the next two words (8 elements) from the advice stack, inserts them into memory at address $a$ sequentially, overwrites these top 8 elements onto the operand stack, and performs a Rescue Prime Optimized permutation to the top 12 elements of the operand stack. At the end of the operation, the address is incremented by $2$.
Fails if the advice stack has fewer than $8$ values. | > **Note**: The opcodes above always push data onto the operand stack so that the first element is placed deepest in the stack. For example, if the data on the stack is `a,b,c,d` and you use the opcode `adv_push.4`, the data will be `d,c,b,a` on your stack. This is also the behavior of the other opcodes. diff --git a/miden/examples/fib/fib.inputs b/miden/examples/fib/fib.inputs index ac0da580a2..4fe61aa7f2 100644 --- a/miden/examples/fib/fib.inputs +++ b/miden/examples/fib/fib.inputs @@ -1,3 +1,3 @@ { - "stack_init": ["1", "1"] + "operand_stack": ["1", "1"] } diff --git a/miden/examples/nprime/nprime.inputs b/miden/examples/nprime/nprime.inputs index 7d1b357a26..0a3eb238e1 100644 --- a/miden/examples/nprime/nprime.inputs +++ b/miden/examples/nprime/nprime.inputs @@ -1,3 +1,3 @@ { - "stack_init": ["50"] + "operand_stack": ["50"] } diff --git a/miden/src/cli/data.rs b/miden/src/cli/data.rs index 518a6bb542..bbe9be8d72 100644 --- a/miden/src/cli/data.rs +++ b/miden/src/cli/data.rs @@ -19,7 +19,7 @@ use stdlib::StdLibrary; /// Input file struct #[derive(Deserialize, Debug)] pub struct InputFile { - pub stack_init: Vec, + pub operand_stack: Vec, pub advice_stack: Option>, } @@ -27,10 +27,10 @@ pub struct InputFile { impl InputFile { pub fn read(inputs_path: &Option, program_path: &Path) -> Result { // if file not specified explicitly and corresponding file with same name as program_path - // with '.inputs' extension does't exist, set stack_init to empty vector + // with '.inputs' extension does't exist, set operand_stack to empty vector if !inputs_path.is_some() && !program_path.with_extension("inputs").exists() { return Ok(Self { - stack_init: Vec::new(), + operand_stack: Vec::new(), advice_stack: Some(Vec::new()), }); } @@ -72,7 +72,7 @@ impl InputFile { /// Parse and return the stack inputs for the program. pub fn parse_stack_inputs(&self) -> Result { let stack_inputs = self - .stack_init + .operand_stack .iter() .map(|v| v.parse::().map_err(|e| e.to_string())) .collect::, _>>()?; diff --git a/miden/tests/integration/air/chiplets/hasher.rs b/miden/tests/integration/air/chiplets/hasher.rs index 230099c572..89c3566bb7 100644 --- a/miden/tests/integration/air/chiplets/hasher.rs +++ b/miden/tests/integration/air/chiplets/hasher.rs @@ -2,7 +2,10 @@ use crate::build_op_test; use crate::helpers::crypto::{init_merkle_leaf, init_merkle_store}; use rand_utils::rand_vector; use vm_core::{ - crypto::merkle::{MerkleStore, MerkleTree}, + crypto::{ + hash::Rpo256, + merkle::{MerkleStore, MerkleTree}, + }, StarkField, Word, }; @@ -53,11 +56,46 @@ fn mtree_set() { } #[test] -fn mtree_cwm() { - let asm_op = "mtree_cwm"; - let (stack_inputs, store, _leaves) = build_mtree_update_test_inputs(); +fn mtree_merge() { + let asm_op = "mtree_merge"; + + let leaves_a = init_merkle_store(&[1, 2, 3, 4, 5, 6, 7, 8]).0; + let leaves_b = init_merkle_store(&[9, 10, 11, 12, 13, 14, 15, 16]).0; + let tree_a = MerkleTree::new(leaves_a.clone()).unwrap(); + let tree_b = MerkleTree::new(leaves_a.clone()).unwrap(); + let root_a = tree_a.root(); + let root_b = tree_b.root(); + let root_merged = Rpo256::merge(&[root_a.into(), root_b.into()]); + let store = MerkleStore::new() + .with_merkle_tree(leaves_a) + .unwrap() + .with_merkle_tree(leaves_b) + .unwrap(); + + let stack_inputs = vec![ + 0xbeef, + 0xdead, + root_a[0].as_int(), + root_a[1].as_int(), + root_a[2].as_int(), + root_a[3].as_int(), + root_b[0].as_int(), + root_b[1].as_int(), + root_b[2].as_int(), + root_b[3].as_int(), + ]; + + let stack_outputs = vec![ + 0xbeef, + 0xdead, + root_merged[0].as_int(), + root_merged[1].as_int(), + root_merged[2].as_int(), + root_merged[3].as_int(), + ]; - build_op_test!(asm_op, &stack_inputs, &[], store).prove_and_verify(stack_inputs, false); + build_op_test!(asm_op, &stack_inputs, &stack_outputs, store) + .prove_and_verify(stack_inputs, false); } /// Helper function that builds a test stack and Merkle tree for testing mtree updates. diff --git a/miden/tests/integration/operations/crypto_ops.rs b/miden/tests/integration/operations/crypto_ops.rs index 3e8d5e7827..5a43d228ce 100644 --- a/miden/tests/integration/operations/crypto_ops.rs +++ b/miden/tests/integration/operations/crypto_ops.rs @@ -177,25 +177,6 @@ fn mtree_update() { let test = build_op_test!(asm_op, &stack_inputs, &[], store.clone()); test.expect_stack(&final_stack); - - // --- mtree_cwm ---------------------------------------------------------------------- - // update a node value and replace the old root - let asm_op = "mtree_cwm"; - - // expected state has the old leaf and the new root of the tree - let final_stack = [ - old_node[3].as_int(), - old_node[2].as_int(), - old_node[1].as_int(), - old_node[0].as_int(), - new_tree.root()[3].as_int(), - new_tree.root()[2].as_int(), - new_tree.root()[1].as_int(), - new_tree.root()[0].as_int(), - ]; - - let test = build_op_test!(asm_op, &stack_inputs, &[], store); - test.expect_stack(&final_stack); } // HELPER FUNCTIONS diff --git a/miden/tests/integration/stdlib/crypto/fri/mod.rs b/miden/tests/integration/stdlib/crypto/fri/mod.rs index 3734d20949..d2fb9f472b 100644 --- a/miden/tests/integration/stdlib/crypto/fri/mod.rs +++ b/miden/tests/integration/stdlib/crypto/fri/mod.rs @@ -31,14 +31,14 @@ fn fri_fold4_ext2_remainder32() { let depth = trace_len_e + blowup_exp; let domain_size = 1 << depth; - let (advice_provider, stack, position_eval, alphas, commitments, remainder, num_queries) = + let (advice_provider, advice_stack, position_eval, alphas, commitments, remainder, num_queries) = fri_prove_verify_fold4_ext2(trace_len_e).expect("should not panic"); let advice_stack = prepare_advice( depth, domain_size, num_queries, - stack, + advice_stack, position_eval, alphas, commitments, @@ -73,14 +73,14 @@ fn fri_fold4_ext2_remainder64() { let depth = trace_len_e + blowup_exp; let domain_size = 1 << depth; - let (advice_provider, stack, position_eval, alphas, commitments, remainder, num_queries) = + let (advice_provider, advice_stack, position_eval, alphas, commitments, remainder, num_queries) = fri_prove_verify_fold4_ext2(trace_len_e).expect("should not panic"); let advice_stack = prepare_advice( depth, domain_size, num_queries, - stack, + advice_stack, position_eval, alphas, commitments, diff --git a/processor/src/advice/inputs.rs b/processor/src/advice/inputs.rs index dbec786418..32dab8166b 100644 --- a/processor/src/advice/inputs.rs +++ b/processor/src/advice/inputs.rs @@ -11,8 +11,8 @@ use super::{BTreeMap, Felt, InputError, MerkleStore, Vec}; /// There are three types of advice inputs: /// /// 1. Single advice stack which can contain any number of elements. -/// 2. Key-mapped stacks set that can be pushed onto the operand stack. -/// 3. Merkle sets list, which are used to provide nondeterministic inputs for instructions that +/// 2. Key-mapped element lists which can be pushed onto the advice stack. +/// 3. Merkle store, which is used to provide nondeterministic inputs for instructions that /// operates with Merkle trees. #[derive(Clone, Debug, Default)] pub struct AdviceInputs { diff --git a/processor/src/advice/mem_provider.rs b/processor/src/advice/mem_provider.rs index 50cb03a591..c11da7a508 100644 --- a/processor/src/advice/mem_provider.rs +++ b/processor/src/advice/mem_provider.rs @@ -141,6 +141,10 @@ impl AdviceProvider for MemAdviceProvider { .map_err(ExecutionError::MerkleStoreUpdateFailed) } + fn merge_roots(&mut self, lhs: Word, rhs: Word) -> Result { + self.store.merge_roots(lhs, rhs).map_err(ExecutionError::MerkleStoreMergeFailed) + } + // CONTEXT MANAGEMENT // -------------------------------------------------------------------------------------------- diff --git a/processor/src/advice/mod.rs b/processor/src/advice/mod.rs index e7911710a0..d1e058f8e3 100644 --- a/processor/src/advice/mod.rs +++ b/processor/src/advice/mod.rs @@ -132,9 +132,7 @@ pub trait AdviceProvider { /// Updates a leaf at the specified index on an existing Merkle tree with the specified root; /// returns the Merkle path from the updated leaf to the new root. /// - /// If `update_in_copy` is set to true, retains both the tree prior to the update (i.e. with - /// the original root), and the new updated tree. Otherwise, the old merkle set is removed from - /// this provider. + /// Retains both the tree prior to the update, and the new updated tree. /// /// # Errors /// Returns an error if: @@ -151,6 +149,17 @@ pub trait AdviceProvider { value: Word, ) -> Result; + /// Creates a new Merkle tree in the advice provider by combining Merkle trees with the + /// specified roots. The root of the new tree is defined as `hash(left_root, right_root)`. + /// + /// After the operation, both the original trees and the new tree remains in the advice + /// provider (i.e., the input trees are not removed). + /// + /// # Errors + /// Returns an error if a Merkle tree for either of the specified roots cannot be found in this + /// advice provider. + fn merge_roots(&mut self, lhs: Word, rhs: Word) -> Result; + // CONTEXT MANAGEMENT // -------------------------------------------------------------------------------------------- @@ -213,6 +222,10 @@ where T::update_merkle_node(self, root, depth, index, value) } + fn merge_roots(&mut self, lhs: Word, rhs: Word) -> Result { + T::merge_roots(self, lhs, rhs) + } + fn advance_clock(&mut self) { T::advance_clock(self) } diff --git a/processor/src/chiplets/hasher/lookups.rs b/processor/src/chiplets/hasher/lookups.rs index c0927cc17a..7a572354e7 100644 --- a/processor/src/chiplets/hasher/lookups.rs +++ b/processor/src/chiplets/hasher/lookups.rs @@ -109,8 +109,8 @@ impl LookupTableRow for HasherLookup { ); // build the leaf value by selecting from the left and right words of the state. // the same alphas must be used in both cases, since whichever word is selected - // by the index bit will be the leaf node, and the value must be computed in the - // same way in both cases. + // by the index bit will be the leaf node, and the value must be computed in + // the same way in both cases. let bit = (self.index.as_int() >> 1) & 1; let left_word = build_value(&alphas[DIGEST_RANGE], &state[..DIGEST_LEN]); let right_word = build_value(&alphas[DIGEST_RANGE], &state[DIGEST_LEN..]); diff --git a/processor/src/decorators/mod.rs b/processor/src/decorators/mod.rs index 6e673aa78b..b7f80ead8f 100644 --- a/processor/src/decorators/mod.rs +++ b/processor/src/decorators/mod.rs @@ -39,6 +39,7 @@ where pub fn dec_advice(&mut self, injector: &AdviceInjector) -> Result<(), ExecutionError> { match injector { AdviceInjector::MerkleNode => self.inject_merkle_node(), + AdviceInjector::MerkleMerge => self.inject_merkle_merge(), AdviceInjector::DivResultU64 => self.inject_div_result_u64(), AdviceInjector::MapValue => self.inject_map_value(), AdviceInjector::Memory(start_addr, num_words) => { @@ -83,6 +84,30 @@ where Ok(()) } + /// Creates a new Merkle tree in the advice provider by combining Merkle trees with the + /// specified roots. The root of the new tree is defined as `hash(left_root, right_root)`. + /// + /// The operand stack is expected to be arranged as follows: + /// - root of the right tree, 4 elements + /// - root of the left tree, 4 elements + /// + /// After the operation, both the original trees and the new tree remains in the advice + /// provider (i.e., the input trees are not removed). + /// + /// # Errors + /// Return an error if a Merkle tree for either of the specified roots cannot be found in this + /// advice provider. + fn inject_merkle_merge(&mut self) -> Result<(), ExecutionError> { + // fetch the arguments from the stack + let lhs = [self.stack.get(7), self.stack.get(6), self.stack.get(5), self.stack.get(4)]; + let rhs = [self.stack.get(3), self.stack.get(2), self.stack.get(1), self.stack.get(0)]; + + // perform the merge + self.advice_provider.merge_roots(lhs, rhs)?; + + Ok(()) + } + /// Pushes the result of [u64] division (both the quotient and the remainder) onto the advice /// stack. The operand stack is expected to be arranged as follows (from the top): /// - divisor split into two 32-bit elements diff --git a/processor/src/errors.rs b/processor/src/errors.rs index 4d260e51c1..1f68975ac4 100644 --- a/processor/src/errors.rs +++ b/processor/src/errors.rs @@ -20,6 +20,7 @@ pub enum ExecutionError { MerkleUpdateInPlace, MerkleStoreLookupFailed(MerkleError), MerkleStoreUpdateFailed(MerkleError), + MerkleStoreMergeFailed(MerkleError), CodeBlockNotFound(Digest), CallerNotInSyscall, DivideByZero(u32), @@ -60,6 +61,9 @@ impl Display for ExecutionError { MerkleStoreUpdateFailed(reason) => { write!(fmt, "Advice provider Merkle store backend update failed: {reason}") } + MerkleStoreMergeFailed(reason) => { + write!(fmt, "Advice provider Merkle store backend merge failed: {reason}") + } AdviceStackReadFailed(step) => write!(fmt, "Advice stack read failed at step {step}"), CodeBlockNotFound(digest) => { let hex = to_hex(&digest.as_bytes())?; diff --git a/processor/src/operations/crypto_ops.rs b/processor/src/operations/crypto_ops.rs index 31ab54df28..6d7a3bb0a1 100644 --- a/processor/src/operations/crypto_ops.rs +++ b/processor/src/operations/crypto_ops.rs @@ -84,7 +84,7 @@ where // helper registers. self.decoder.set_user_op_helpers(Operation::MpVerify, &[addr]); - // Asserting the computed root of the merkle path from the advice provider is consistent with + // Asserting the computed root of the Merkle path from the advice provider is consistent with // the input root. assert_eq!(provided_root, computed_root, "inconsistent Merkle tree root"); @@ -93,7 +93,7 @@ where Ok(()) } - /// Computes a new root of a Merkle tree where a leaf at the specified index is updated to + /// Computes a new root of a Merkle tree where a node at the specified index is updated to /// the specified value. The stack is expected to be arranged as follows (from the top): /// - old value of the node, 4 elements. /// - depth of the node, 1 element; this is expected to be the depth of the Merkle tree. @@ -102,23 +102,20 @@ where /// - new value of the node, 4 elements. /// /// To perform the operation we do the following: - /// 1. Update the leaf node at the specified index in the advice provider with the specified - /// root, and get the Merkle path to this leaf. If `copy` is set to true, we make a copy - /// of the merkle set before updating it. + /// 1. Update the node at the specified index in the Merkle tree with the specified root, and + /// get the Merkle path to it. /// 2. Use the hasher to update the root of the Merkle path for the specified node. For this /// we need to provide the old and the new node value. /// 3. Verify that the computed old root is equal to the input root provided via the stack. /// 4. Replace the old node value with the computed new root. /// /// The Merkle path for the node is expected to be provided by the prover non-deterministically - /// (via merkle sets). At the end of the operation, the old node value is replaced with the - /// new root value computed based on the provided path. Everything else on the stack remains the - /// same. - /// - /// If `copy` is set to true, at the end of the operation the advice provide will keep both, - /// the old and the new merkle sets. Otherwise, the old merkle set is removed from the - /// provider. + /// (via the advice provider). At the end of the operation, the old node value is replaced with + /// the new roots value computed based on the provided path. Everything else on the stack + /// remains the same. /// + /// The original Merkle tree is cloned before the update is performed, and thus, after the + /// operation, the advice provider will keep track of both the old and the new trees. /// /// # Errors /// Returns an error if: @@ -129,12 +126,7 @@ where /// /// # Panics /// Panics if the computed old root does not match the input root provided via the stack. - pub(super) fn op_mrupdate(&mut self, copy: bool) -> Result<(), ExecutionError> { - // TODO https://github.com/0xPolygonMiden/miden-vm/issues/775 - if !copy { - return Err(ExecutionError::MerkleUpdateInPlace); - } - + pub(super) fn op_mrupdate(&mut self) -> Result<(), ExecutionError> { // read old node value, depth, index, tree root and new node values from the stack let old_node = [self.stack.get(3), self.stack.get(2), self.stack.get(1), self.stack.get(0)]; let depth = self.stack.get(4); @@ -143,17 +135,16 @@ where let new_node = [self.stack.get(13), self.stack.get(12), self.stack.get(11), self.stack.get(10)]; - // update the leaf at the specified index in the merkle set specified by the old root, and - // get a Merkle path to the specified leaf. the length of the returned path is expected to - // match the specified depth. - // TODO: in the future, we should be able to replace sub-trees and not just the leaves, - // and, thus, the assert on depth would not be needed. + // update the node at the specified index in the Merkle tree specified by the old root, and + // get a Merkle path to it. the length of the returned path is expected to match the + // specified depth. if the new node is the root of a tree, this instruction will append the + // whole sub-tree to this node. let path = self.advice_provider.update_merkle_node(old_root, &depth, &index, new_node)?; assert_eq!(path.len(), depth.as_int() as usize); let merkle_tree_update = self.chiplets.update_merkle_root(old_node, new_node, &path, index); - // Asserts the computed old root of the merkle path from the advice provider is consistent + // Asserts the computed old root of the Merkle path from the advice provider is consistent // with the input root provided via the stack. This will panic only if the advice provider // returns a Merkle path inconsistent with the specified root. assert_eq!(old_root, merkle_tree_update.get_old_root(), "inconsistent Merkle tree root"); @@ -161,7 +152,7 @@ where // save address(r) of the hasher trace from when the computation starts in the decoder // helper registers. self.decoder - .set_user_op_helpers(Operation::MrUpdate(copy), &[merkle_tree_update.get_address()]); + .set_user_op_helpers(Operation::MrUpdate, &[merkle_tree_update.get_address()]); // Replace the old node value with computed new root; everything else remains the same. for (i, &value) in merkle_tree_update.get_new_root().iter().rev().enumerate() { @@ -186,7 +177,7 @@ mod tests { use rand_utils::rand_vector; use vm_core::{ chiplets::hasher::{apply_permutation, STATE_WIDTH}, - crypto::merkle::{MerkleStore, MerkleTree}, + crypto::merkle::{MerkleStore, MerkleTree, NodeIndex}, utils::collections::Vec, }; @@ -228,11 +219,11 @@ mod tests { #[test] fn op_mpverify() { let index = 5usize; - let leaves = init_leaves(&[1, 2, 3, 4, 5, 6, 7, 8]); - let tree = MerkleTree::new(leaves.to_vec()).unwrap(); - let store = MerkleStore::new().with_merkle_tree(leaves.clone()).unwrap(); + let nodes = init_leaves(&[1, 2, 3, 4, 5, 6, 7, 8]); + let tree = MerkleTree::new(nodes.to_vec()).unwrap(); + let store = MerkleStore::new().with_merkle_tree(nodes.clone()).unwrap(); let root = tree.root(); - let leaf = leaves[index]; + let node = nodes[index]; let index = index as u64; let depth = tree.depth() as u64; @@ -243,10 +234,10 @@ mod tests { root[3].as_int(), index, depth, - leaf[0].as_int(), - leaf[1].as_int(), - leaf[2].as_int(), - leaf[3].as_int(), + node[0].as_int(), + node[1].as_int(), + node[2].as_int(), + node[3].as_int(), ]; let depth = Felt::new(depth); @@ -259,38 +250,38 @@ mod tests { process.execute_op(Operation::MpVerify).unwrap(); let expected_stack = build_expected(&[ - leaf[3], leaf[2], leaf[1], leaf[0], depth, index, root[3], root[2], root[1], root[0], + node[3], node[2], node[1], node[0], depth, index, root[3], root[2], root[1], root[0], ]); assert_eq!(expected_stack, process.stack.trace_state()); } #[test] - fn op_mrupdate_copy() { + fn op_mrupdate() { let leaves = init_leaves(&[1, 2, 3, 4, 5, 6, 7, 8]); - let node_index = 5usize; - let new_node = init_leaf(9); + let leaf_index = 5usize; + let new_leaf = init_node(9); let mut new_leaves = leaves.clone(); - new_leaves[node_index] = new_node; + new_leaves[leaf_index] = new_leaf; let tree = MerkleTree::new(leaves.clone()).unwrap(); let new_tree = MerkleTree::new(new_leaves).unwrap(); let stack_inputs = [ - new_node[0].as_int(), - new_node[1].as_int(), - new_node[2].as_int(), - new_node[3].as_int(), + new_leaf[0].as_int(), + new_leaf[1].as_int(), + new_leaf[2].as_int(), + new_leaf[3].as_int(), tree.root()[0].as_int(), tree.root()[1].as_int(), tree.root()[2].as_int(), tree.root()[3].as_int(), - node_index as u64, + leaf_index as u64, tree.depth() as u64, - leaves[node_index][0].as_int(), - leaves[node_index][1].as_int(), - leaves[node_index][2].as_int(), - leaves[node_index][3].as_int(), + leaves[leaf_index][0].as_int(), + leaves[leaf_index][1].as_int(), + leaves[leaf_index][2].as_int(), + leaves[leaf_index][3].as_int(), ]; let store = MerkleStore::new().with_merkle_tree(leaves.to_vec()).unwrap(); @@ -300,37 +291,119 @@ mod tests { Process::new_dummy_with_inputs_and_decoder_helpers(stack_inputs, advice_inputs); // update the Merkle tree but keep the old copy - process.execute_op(Operation::MrUpdate(true)).unwrap(); + process.execute_op(Operation::MrUpdate).unwrap(); let expected_stack = build_expected(&[ new_tree.root()[3], new_tree.root()[2], new_tree.root()[1], new_tree.root()[0], Felt::new(tree.depth() as u64), - Felt::new(node_index as u64), + Felt::new(leaf_index as u64), tree.root()[3], tree.root()[2], tree.root()[1], tree.root()[0], - new_node[3], - new_node[2], - new_node[1], - new_node[0], + new_leaf[3], + new_leaf[2], + new_leaf[1], + new_leaf[0], ]); assert_eq!(expected_stack, process.stack.trace_state()); - // make sure both Merkle trees are still in the merkle set + // make sure both Merkle trees are still in the advice provider assert!(process.advice_provider.has_merkle_root(tree.root())); assert!(process.advice_provider.has_merkle_root(new_tree.root())); } + #[test] + fn op_mrupdate_merge_subtree() { + // init 3 trees, `a` and `b` to be the initial trees, and `c` to be the merged product of + // `a` and `b` + let leaves_a = init_leaves(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + let leaves_b = init_leaves(&[100, 101, 102, 103]); + let leaves_c = init_leaves(&[0, 1, 2, 3, 100, 101, 102, 103, 8, 9, 10, 11, 12, 13, 14, 15]); + + let tree_a = MerkleTree::new(leaves_a.clone()).unwrap(); + let tree_b = MerkleTree::new(leaves_b.clone()).unwrap(); + let tree_c = MerkleTree::new(leaves_c.clone()).unwrap(); + + // appends only the input trees to the Merkle store + let store = MerkleStore::new() + .with_merkle_tree(leaves_a.to_vec()) + .unwrap() + .with_merkle_tree(leaves_b.to_vec()) + .unwrap(); + + // set the target coordinates to update the indexes 4..8 + let target_depth = 2; + let target_index = 1; + let target_node = tree_b.root(); + + // fetch the final root after the sub-tree merge + let expected_root = tree_c.root(); + + // fetch the node to be replaced + let replaced_root = tree_a.root(); + let replaced_node = store + .get_node(replaced_root, NodeIndex::new(target_depth as u8, target_index)) + .unwrap(); + + // setup the process + let advice_inputs = AdviceInputs::default().with_merkle_store(store); + let stack_inputs = [ + target_node[0].as_int(), + target_node[1].as_int(), + target_node[2].as_int(), + target_node[3].as_int(), + replaced_root[0].as_int(), + replaced_root[1].as_int(), + replaced_root[2].as_int(), + replaced_root[3].as_int(), + target_index, + target_depth, + replaced_node[0].as_int(), + replaced_node[1].as_int(), + replaced_node[2].as_int(), + replaced_node[3].as_int(), + ]; + let stack_inputs = StackInputs::try_from_values(stack_inputs).unwrap(); + let mut process = + Process::new_dummy_with_inputs_and_decoder_helpers(stack_inputs, advice_inputs); + + // assert the expected root doesn't exist before the merge operation + assert!(!process.advice_provider.has_merkle_root(expected_root)); + + // update the previous root + process.execute_op(Operation::MrUpdate).unwrap(); + let expected_stack = build_expected(&[ + expected_root[3], + expected_root[2], + expected_root[1], + expected_root[0], + Felt::new(target_depth), + Felt::new(target_index), + replaced_root[3], + replaced_root[2], + replaced_root[1], + replaced_root[0], + target_node[3], + target_node[2], + target_node[1], + target_node[0], + ]); + assert_eq!(expected_stack, process.stack.trace_state()); + + // assert the expected root now exists in the advice provider + assert!(process.advice_provider.has_merkle_root(expected_root)); + } + // HELPER FUNCTIONS // -------------------------------------------------------------------------------------------- fn init_leaves(values: &[u64]) -> Vec { - values.iter().map(|&v| init_leaf(v)).collect() + values.iter().map(|&v| init_node(v)).collect() } - fn init_leaf(value: u64) -> Word { + fn init_node(value: u64) -> Word { [Felt::new(value), Felt::ZERO, Felt::ZERO, Felt::ZERO] } diff --git a/processor/src/operations/mod.rs b/processor/src/operations/mod.rs index 237f8a751f..f55607be8e 100644 --- a/processor/src/operations/mod.rs +++ b/processor/src/operations/mod.rs @@ -145,7 +145,7 @@ where // ----- cryptographic operations ----------------------------------------------------- Operation::HPerm => self.op_hperm()?, Operation::MpVerify => self.op_mpverify()?, - Operation::MrUpdate(copy) => self.op_mrupdate(copy)?, + Operation::MrUpdate => self.op_mrupdate()?, Operation::FriE2F4 => self.op_fri_ext2fold4()?, } diff --git a/processor/src/trace/tests/hasher.rs b/processor/src/trace/tests/hasher.rs index 95be3f1cd9..347f93d143 100644 --- a/processor/src/trace/tests/hasher.rs +++ b/processor/src/trace/tests/hasher.rs @@ -65,7 +65,7 @@ fn hasher_p1_mr_update() { let advice_inputs = AdviceInputs::default().with_merkle_store(store); // build execution trace and extract the sibling table column from it - let ops = vec![Operation::MrUpdate(true)]; + let ops = vec![Operation::MrUpdate]; let mut trace = build_trace_from_ops_with_inputs(ops, stack_inputs, advice_inputs); let alphas = rand_array::(); let aux_columns = trace.build_aux_segment(&[], &alphas).unwrap(); From 234c5054728523934f32aa70a6ca9ee996eaf86e Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sun, 26 Mar 2023 01:17:20 -0700 Subject: [PATCH 34/47] chore: update winterfell dependency to v0.6.0 --- air/Cargo.toml | 6 +- air/src/lib.rs | 9 +++ air/src/proof.rs | 4 +- core/Cargo.toml | 12 ++-- core/src/lib.rs | 9 ++- core/src/program/info.rs | 27 +++++++- core/src/random.rs | 65 +++++++++++++++++++ core/src/stack/inputs.rs | 8 ++- core/src/stack/mod.rs | 2 +- core/src/stack/outputs.rs | 20 +++++- core/src/utils/mod.rs | 2 - miden/Cargo.toml | 10 +-- miden/src/lib.rs | 10 +-- .../tests/integration/operations/ext2_ops.rs | 33 +++++----- miden/tests/integration/stdlib/crypto/mod.rs | 2 +- processor/Cargo.toml | 8 +-- processor/src/chiplets/bitwise/mod.rs | 7 +- processor/src/chiplets/bus/aux_trace.rs | 11 ++-- processor/src/chiplets/bus/mod.rs | 8 +-- processor/src/chiplets/hasher/aux_trace.rs | 11 ++-- processor/src/chiplets/hasher/lookups.rs | 29 ++++----- processor/src/chiplets/hasher/mod.rs | 19 +++--- processor/src/chiplets/memory/mod.rs | 9 +-- processor/src/chiplets/mod.rs | 5 +- processor/src/decoder/aux_hints.rs | 11 ++-- processor/src/decoder/mod.rs | 5 +- processor/src/decorators/mod.rs | 20 +++--- processor/src/errors.rs | 3 +- processor/src/lib.rs | 21 +++--- processor/src/operations/ext2_ops.rs | 9 ++- processor/src/operations/fri_ops.rs | 6 +- processor/src/range/aux_trace.rs | 16 ++--- processor/src/range/mod.rs | 8 ++- processor/src/range/request.rs | 11 ++-- processor/src/stack/aux_trace.rs | 6 +- processor/src/stack/mod.rs | 3 +- processor/src/stack/overflow.rs | 6 +- processor/src/trace/decoder/mod.rs | 12 ++-- processor/src/trace/mod.rs | 30 ++++----- processor/src/trace/utils.rs | 10 +-- prover/Cargo.toml | 2 +- prover/src/lib.rs | 56 ++++++++++------ verifier/Cargo.toml | 2 +- verifier/src/lib.rs | 17 +++-- 44 files changed, 354 insertions(+), 226 deletions(-) create mode 100644 core/src/random.rs diff --git a/air/Cargo.toml b/air/Cargo.toml index 5c4c1a0ba9..8347d9fdd0 100644 --- a/air/Cargo.toml +++ b/air/Cargo.toml @@ -29,9 +29,9 @@ std = ["vm-core/std", "winter-air/std"] [dependencies] vm-core = { package = "miden-core", path = "../core", version = "0.5", default-features = false } -winter-air = { package = "winter-air", version = "0.5", default-features = false } +winter-air = { package = "winter-air", version = "0.6", default-features = false } [dev-dependencies] criterion = "0.4" -proptest = "1.0" -rand-utils = { package = "winter-rand-utils", version = "0.5" } +proptest = "1.1" +rand-utils = { package = "winter-rand-utils", version = "0.6" } diff --git a/air/src/lib.rs b/air/src/lib.rs index eb241859f2..b5ccc66f0d 100644 --- a/air/src/lib.rs +++ b/air/src/lib.rs @@ -277,3 +277,12 @@ impl Serializable for PublicInputs { self.stack_outputs.write_into(target); } } + +impl vm_core::ToElements for PublicInputs { + fn to_elements(&self) -> Vec { + let mut result = self.program_info.to_elements(); + result.append(&mut self.stack_inputs.to_elements()); + result.append(&mut self.stack_outputs.to_elements()); + result + } +} diff --git a/air/src/proof.rs b/air/src/proof.rs index b220673387..ceb58de69b 100644 --- a/air/src/proof.rs +++ b/air/src/proof.rs @@ -118,7 +118,7 @@ impl ProofOptions { /// Creates a new preset instance of [ProofOptions] targeting 96-bit security level. pub fn with_96_bit_security() -> Self { - let options = WinterProofOptions::new(27, 8, 16, FieldExtension::Quadratic, 8, 256); + let options = WinterProofOptions::new(27, 8, 16, FieldExtension::Quadratic, 8, 255); Self { hash_fn: HashFunction::Blake3_192, options, @@ -127,7 +127,7 @@ impl ProofOptions { /// Creates a new preset instance of [ProofOptions] targeting 128-bit security level. pub fn with_128_bit_security() -> Self { - let options = WinterProofOptions::new(27, 16, 21, FieldExtension::Cubic, 8, 256); + let options = WinterProofOptions::new(27, 16, 21, FieldExtension::Cubic, 8, 255); Self { hash_fn: HashFunction::Blake3_256, options, diff --git a/core/Cargo.toml b/core/Cargo.toml index 90a70dccbd..9227f9748c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -20,11 +20,11 @@ default = ["std"] std = ["math/std", "winter-utils/std"] [dependencies] -math = { package = "winter-math", version = "0.5", default-features = false } -#crypto = { package = "miden-crypto", version = "0.1", default-features = false } -crypto = { package = "miden-crypto", git = "https://github.com/0xPolygonMiden/crypto", rev = "75af3d474b0f5aa03ee65708ec3fd9397426f29c", default-features = false } -winter-utils = { package = "winter-utils", version = "0.5", default-features = false } +math = { package = "winter-math", version = "0.6", default-features = false } +crypto = { package = "miden-crypto", version = "0.2", default-features = false } +winter-crypto = { package = "winter-crypto", version = "0.6", default-features = false } +winter-utils = { package = "winter-utils", version = "0.6", default-features = false } [dev-dependencies] -proptest = "1.1.0" -rand_utils = { version = "0.5", package = "winter-rand-utils" } +proptest = "1.1" +rand_utils = { version = "0.6", package = "winter-rand-utils" } diff --git a/core/src/lib.rs b/core/src/lib.rs index b7856f1571..36472f42b3 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -25,11 +25,15 @@ pub mod crypto { ElementHasher, Hasher, }; } + + pub mod random { + pub use crate::random::*; + } } pub use math::{ fields::{f64::BaseElement as Felt, QuadExtension}, - polynom, ExtensionOf, FieldElement, StarkField, + polynom, ExtensionOf, FieldElement, StarkField, ToElements, }; mod program; @@ -43,6 +47,9 @@ pub use operations::{ pub mod stack; pub use stack::{StackInputs, StackOutputs}; +// TODO: this should move to miden-crypto crate +mod random; + pub mod utils; use utils::range; diff --git a/core/src/program/info.rs b/core/src/program/info.rs index 53d069d5a7..5899ba17fa 100644 --- a/core/src/program/info.rs +++ b/core/src/program/info.rs @@ -1,6 +1,7 @@ use super::{ - ByteReader, ByteWriter, Deserializable, DeserializationError, Digest, Kernel, Program, - Serializable, + super::{ToElements, WORD_SIZE}, + ByteReader, ByteWriter, Deserializable, DeserializationError, Digest, Felt, Kernel, Program, + Serializable, Vec, }; // PROGRAM INFO @@ -63,6 +64,9 @@ impl From for ProgramInfo { } } +// SERIALIZATION +// ------------------------------------------------------------------------------------------------ + impl Serializable for ProgramInfo { fn write_into(&self, target: &mut W) { self.program_hash.write_into(target); @@ -80,3 +84,22 @@ impl Deserializable for ProgramInfo { }) } } + +// TO ELEMENTS +// ------------------------------------------------------------------------------------------------ + +impl ToElements for ProgramInfo { + fn to_elements(&self) -> Vec { + let num_kernel_proc_elements = self.kernel.proc_hashes().len() * WORD_SIZE; + let mut result = Vec::with_capacity(WORD_SIZE + num_kernel_proc_elements); + + // append program hash elements + result.extend_from_slice(self.program_hash.as_elements()); + + // append kernel procedure hash elements + for proc_hash in self.kernel.proc_hashes() { + result.extend_from_slice(proc_hash.as_elements()); + } + result + } +} diff --git a/core/src/random.rs b/core/src/random.rs new file mode 100644 index 0000000000..93abb1e314 --- /dev/null +++ b/core/src/random.rs @@ -0,0 +1,65 @@ +use super::{ + crypto::hash::{Rpo256, RpoDigest}, + Felt, FieldElement, +}; + +// RE-EXPORTS +// ================================================================================================ + +pub use winter_crypto::{DefaultRandomCoin as WinterRandomCoin, RandomCoin, RandomCoinError}; + +// RPO RANDOM COIN +// ================================================================================================ + +/// PRNG based on RPO hash function. +/// +/// Right now, this is just a wrapper around [winter_crypto::DefaultRandomCoin], but in the future +/// this can be implemented more efficiently using sponge properties of RPO. +pub struct RpoRandomCoin(WinterRandomCoin); + +impl RpoRandomCoin { + pub fn new(seed: &[Felt]) -> Self { + Self(WinterRandomCoin::new(seed)) + } + + pub fn draw>(&mut self) -> Result { + RandomCoin::draw(self) + } +} + +impl RandomCoin for RpoRandomCoin { + type BaseField = Felt; + type Hasher = Rpo256; + + fn new(seed: &[Felt]) -> Self { + Self(WinterRandomCoin::new(seed)) + } + + fn reseed(&mut self, data: RpoDigest) { + self.0.reseed(data) + } + + fn reseed_with_int(&mut self, value: u64) { + self.0.reseed_with_int(value) + } + + fn leading_zeros(&self) -> u32 { + self.0.leading_zeros() + } + + fn check_leading_zeros(&self, value: u64) -> u32 { + self.0.check_leading_zeros(value) + } + + fn draw>(&mut self) -> Result { + self.0.draw() + } + + fn draw_integers( + &mut self, + num_values: usize, + domain_size: usize, + ) -> Result, RandomCoinError> { + self.0.draw_integers(num_values, domain_size) + } +} diff --git a/core/src/stack/inputs.rs b/core/src/stack/inputs.rs index 20ac1e9ffe..41245bcd95 100644 --- a/core/src/stack/inputs.rs +++ b/core/src/stack/inputs.rs @@ -1,4 +1,4 @@ -use super::{vec, ByteWriter, Felt, InputError, Serializable, Vec}; +use super::{vec, ByteWriter, Felt, InputError, Serializable, ToElements, Vec}; use core::slice; // STACK INPUTS @@ -77,3 +77,9 @@ impl Serializable for StackInputs { self.values.iter().copied().for_each(|v| target.write(v)); } } + +impl ToElements for StackInputs { + fn to_elements(&self) -> Vec { + self.values.to_vec() + } +} diff --git a/core/src/stack/mod.rs b/core/src/stack/mod.rs index 957b8b40c1..c6d13b461f 100644 --- a/core/src/stack/mod.rs +++ b/core/src/stack/mod.rs @@ -1,4 +1,4 @@ -use super::{errors::InputError, range, Felt, Range, StackTopState, StarkField}; +use super::{errors::InputError, range, Felt, Range, StackTopState, StarkField, ToElements}; use winter_utils::{ collections::{vec, Vec}, ByteWriter, Serializable, diff --git a/core/src/stack/outputs.rs b/core/src/stack/outputs.rs index 592aa6c1e9..4e5b73eb9e 100644 --- a/core/src/stack/outputs.rs +++ b/core/src/stack/outputs.rs @@ -1,4 +1,6 @@ -use super::{ByteWriter, Felt, Serializable, StackTopState, StarkField, Vec, STACK_TOP_SIZE}; +use super::{ + ByteWriter, Felt, Serializable, StackTopState, StarkField, ToElements, Vec, STACK_TOP_SIZE, +}; // STACK OUTPUTS // ================================================================================================ @@ -21,7 +23,7 @@ use super::{ByteWriter, Felt, Serializable, StackTopState, StarkField, Vec, STAC pub struct StackOutputs { /// The elements on the stack at the end of execution. stack: Vec, - /// The overflow table row addresse required to reconstruct the final state of the table. + /// The overflow table row addresses required to reconstruct the final state of the table. overflow_addrs: Vec, } @@ -154,3 +156,17 @@ impl Serializable for StackOutputs { self.overflow_addrs.iter().copied().for_each(|v| target.write_u64(v)); } } + +impl ToElements for StackOutputs { + fn to_elements(&self) -> Vec { + // infallible conversion from u64 to Felt is OK here because we check validity of u64 + // values in the constructor + // TODO: change internal data types of self.stack and self.overflow_addrs to Felt? + self.stack + .iter() + .chain(self.overflow_addrs.iter()) + .cloned() + .map(Felt::new) + .collect() + } +} diff --git a/core/src/utils/mod.rs b/core/src/utils/mod.rs index d4b1984caf..75ff870837 100644 --- a/core/src/utils/mod.rs +++ b/core/src/utils/mod.rs @@ -23,8 +23,6 @@ pub use winter_utils::{ ByteWriter, Deserializable, DeserializationError, Serializable, SliceReader, }; -pub use crypto::{RandomCoin, RandomCoinError}; - pub mod math { pub use math::{batch_inversion, log2}; } diff --git a/miden/Cargo.toml b/miden/Cargo.toml index 3c5b7eccba..1f3d19fc12 100644 --- a/miden/Cargo.toml +++ b/miden/Cargo.toml @@ -65,12 +65,12 @@ escargot = "0.5.7" num-bigint = "0.4" predicates = "2.1.5" processor = { package = "miden-processor", path = "../processor", version = "0.5", features = ["internals"], default-features = false } -proptest = "1.0.0" -rand-utils = { package = "winter-rand-utils", version = "0.5" } +proptest = "1.1" +rand-utils = { package = "winter-rand-utils", version = "0.6" } sha2 = "0.10" sha3 = "0.10" test-case = "3.0.0" vm-core = { package = "miden-core", path = "../core", version = "0.5", default-features = false } -winterfell = { package = "winter-prover", version = "0.5", default-features = false } -winter-fri = { package = "winter-fri", version = "0.5" } -winter-utils = { package = "winter-utils", version = "0.5" } +winterfell = { package = "winter-prover", version = "0.6", default-features = false } +winter-fri = { package = "winter-fri", version = "0.6" } +winter-utils = { package = "winter-utils", version = "0.6" } diff --git a/miden/src/lib.rs b/miden/src/lib.rs index 2e65cb8355..c4f052544d 100644 --- a/miden/src/lib.rs +++ b/miden/src/lib.rs @@ -6,12 +6,12 @@ pub use assembly::{Assembler, AssemblyError, ParsingError}; pub use processor::{ - execute, execute_iter, utils, AdviceInputs, AdviceProvider, AsmOpInfo, Blake3_192, - ExecutionError, ExecutionTrace, Kernel, MemAdviceProvider, Operation, ProgramInfo, Rpo256, - StackInputs, VmState, VmStateIterator, + crypto, execute, execute_iter, utils, AdviceInputs, AdviceProvider, AsmOpInfo, ExecutionError, + ExecutionTrace, Kernel, MemAdviceProvider, Operation, ProgramInfo, StackInputs, VmState, + VmStateIterator, }; pub use prover::{ - math, prove, Digest, ExecutionProof, FieldExtension, HashFunction, InputError, MerkleError, - Program, ProofOptions, StackOutputs, StarkProof, Word, + math, prove, Digest, ExecutionProof, FieldExtension, HashFunction, InputError, Program, + ProofOptions, StackOutputs, StarkProof, Word, }; pub use verifier::{verify, VerificationError}; diff --git a/miden/tests/integration/operations/ext2_ops.rs b/miden/tests/integration/operations/ext2_ops.rs index a2e9c7a203..0a36a19638 100644 --- a/miden/tests/integration/operations/ext2_ops.rs +++ b/miden/tests/integration/operations/ext2_ops.rs @@ -2,7 +2,7 @@ use crate::build_op_test; use rand_utils::rand_value; use vm_core::{Felt, FieldElement, QuadExtension, StarkField}; -type Ext2Element = QuadExtension; +type QuadFelt = QuadExtension; // EXT2 OPS ASSERTIONS - MANUAL TESTS // ================================================================================================ @@ -11,8 +11,8 @@ type Ext2Element = QuadExtension; fn ext2add() { let asm_op = "ext2add"; - let a = rand_value::(); - let b = rand_value::(); + let a = rand_value::(); + let b = rand_value::(); let c = a + b; let (a0, a1) = ext_element_to_ints(a); @@ -30,8 +30,8 @@ fn ext2add() { fn ext2sub() { let asm_op = "ext2sub"; - let a = rand_value::(); - let b = rand_value::(); + let a = rand_value::(); + let b = rand_value::(); let c = a - b; let (a0, a1) = ext_element_to_ints(a); @@ -49,8 +49,8 @@ fn ext2sub() { fn ext2mul() { let asm_op = "ext2mul"; - let a = rand_value::(); - let b = rand_value::(); + let a = rand_value::(); + let b = rand_value::(); let c = b * a; let (a0, a1) = ext_element_to_ints(a); @@ -68,8 +68,8 @@ fn ext2mul() { fn ext2div() { let asm_op = "ext2div"; - let a = rand_value::(); - let b = rand_value::(); + let a = rand_value::(); + let b = rand_value::(); let c = a * b.inv(); let (a0, a1) = ext_element_to_ints(a); let (b0, b1) = ext_element_to_ints(b); @@ -86,7 +86,7 @@ fn ext2div() { fn ext2neg() { let asm_op = "ext2neg"; - let a = rand_value::(); + let a = rand_value::(); let b = -a; let (a0, a1) = ext_element_to_ints(a); let (b0, b1) = ext_element_to_ints(b); @@ -102,7 +102,7 @@ fn ext2neg() { fn ext2inv() { let asm_op = "ext2inv"; - let a = rand_value::(); + let a = rand_value::(); let b = a.inv(); let (a0, a1) = ext_element_to_ints(a); @@ -117,10 +117,9 @@ fn ext2inv() { // HELPER FUNCTIONS // ================================================================================================ -/// Helper function to convert a list of field elements into a list of elements in the underlying -/// base field and convert them into integers. Returns a tuple of integers. -fn ext_element_to_ints(ext_elem: Ext2Element) -> (u64, u64) { - let ext_elem_arr = [ext_elem]; - let ext_elem_to_base_field = Ext2Element::as_base_elements(&ext_elem_arr); - (ext_elem_to_base_field[0].as_int(), ext_elem_to_base_field[1].as_int()) +/// Helper function to convert a quadratic extension field element into a tuple of elements in the +/// underlying base field and convert them into integers. +fn ext_element_to_ints(ext_elem: QuadFelt) -> (u64, u64) { + let base_elements = ext_elem.to_base_elements(); + (base_elements[0].as_int(), base_elements[1].as_int()) } diff --git a/miden/tests/integration/stdlib/crypto/mod.rs b/miden/tests/integration/stdlib/crypto/mod.rs index 7949ad8fef..206510e5e5 100644 --- a/miden/tests/integration/stdlib/crypto/mod.rs +++ b/miden/tests/integration/stdlib/crypto/mod.rs @@ -4,6 +4,6 @@ use crate::helpers::{Felt, STACK_TOP_SIZE}; mod blake3; mod ecdsa_secp256k1; mod falcon; -mod fri; +//mod fri; TODO: re-enable mod keccak256; mod sha256; diff --git a/processor/Cargo.toml b/processor/Cargo.toml index 490d340516..2975b1905d 100644 --- a/processor/Cargo.toml +++ b/processor/Cargo.toml @@ -24,11 +24,11 @@ std = ["vm-core/std", "winter-prover/std", "log/std"] [dependencies] log = "0.4.14" vm-core = { package = "miden-core", path = "../core", version = "0.5", default-features = false } -winter-prover = { package = "winter-prover", version = "0.5", default-features = false } +winter-prover = { package = "winter-prover", version = "0.6", default-features = false } [dev-dependencies] logtest = { version = "2.0", default-features = false } miden-assembly = { package = "miden-assembly", path = "../assembly", version = "0.5", default-features = false } -rand-utils = { package = "winter-rand-utils", version = "0.5" } -winter-fri = { package = "winter-fri", version = "0.5" } -winter-utils = { package = "winter-utils", version = "0.5" } +rand-utils = { package = "winter-rand-utils", version = "0.6" } +winter-fri = { package = "winter-fri", version = "0.6" } +winter-utils = { package = "winter-utils", version = "0.6" } diff --git a/processor/src/chiplets/bitwise/mod.rs b/processor/src/chiplets/bitwise/mod.rs index d276a13a0f..5f1f8c4681 100644 --- a/processor/src/chiplets/bitwise/mod.rs +++ b/processor/src/chiplets/bitwise/mod.rs @@ -1,8 +1,7 @@ use super::{ - ChipletsBus, ExecutionError, Felt, FieldElement, LookupTableRow, StarkField, TraceFragment, - Vec, BITWISE_AND_LABEL, BITWISE_XOR_LABEL, + trace::LookupTableRow, utils::get_trace_len, ChipletsBus, ColMatrix, ExecutionError, Felt, + FieldElement, StarkField, TraceFragment, Vec, BITWISE_AND_LABEL, BITWISE_XOR_LABEL, }; -use crate::{utils::get_trace_len, Matrix}; use vm_core::chiplets::bitwise::{ A_COL_IDX, A_COL_RANGE, BITWISE_AND, BITWISE_XOR, B_COL_IDX, B_COL_RANGE, OP_CYCLE_LEN, OUTPUT_COL_IDX, PREV_OUTPUT_COL_IDX, TRACE_WIDTH, @@ -262,7 +261,7 @@ impl LookupTableRow for BitwiseLookup { /// at least 5 alpha values. fn to_value>( &self, - _main_trace: &Matrix, + _main_trace: &ColMatrix, alphas: &[E], ) -> E { alphas[0] diff --git a/processor/src/chiplets/bus/aux_trace.rs b/processor/src/chiplets/bus/aux_trace.rs index 72669e7530..e873d5b576 100644 --- a/processor/src/chiplets/bus/aux_trace.rs +++ b/processor/src/chiplets/bus/aux_trace.rs @@ -1,7 +1,6 @@ -use super::{ChipletsLookup, ChipletsLookupRow, Felt, FieldElement}; -use crate::{ - trace::{build_lookup_table_row_values, AuxColumnBuilder, LookupTableRow}, - Matrix, Vec, +use super::{ + build_lookup_table_row_values, AuxColumnBuilder, ChipletsLookup, ChipletsLookupRow, ColMatrix, + Felt, FieldElement, LookupTableRow, Vec, }; // AUXILIARY TRACE BUILDER @@ -24,7 +23,7 @@ impl AuxTraceBuilder { /// provided by chiplets in the Chiplets module. pub fn build_aux_columns>( &self, - main_trace: &Matrix, + main_trace: &ColMatrix, rand_elements: &[E], ) -> Vec> { let b_chip = self.build_aux_column(main_trace, rand_elements); @@ -75,7 +74,7 @@ impl AuxColumnBuilder for AuxTraceBuilde /// requests. Since responses are grouped by chiplet, the operation order for the requests and /// responses will be permutations of each other rather than sharing the same order. Therefore, /// the `row_values` and `inv_row_values` must be built separately. - fn build_row_values(&self, main_trace: &Matrix, alphas: &[E]) -> (Vec, Vec) + fn build_row_values(&self, main_trace: &ColMatrix, alphas: &[E]) -> (Vec, Vec) where E: FieldElement, { diff --git a/processor/src/chiplets/bus/mod.rs b/processor/src/chiplets/bus/mod.rs index c3d976ae80..9fedf8aa6e 100644 --- a/processor/src/chiplets/bus/mod.rs +++ b/processor/src/chiplets/bus/mod.rs @@ -1,8 +1,8 @@ use super::{ - hasher::HasherLookup, BTreeMap, BitwiseLookup, Felt, FieldElement, LookupTableRow, - MemoryLookup, Vec, + hasher::HasherLookup, + trace::{build_lookup_table_row_values, AuxColumnBuilder, LookupTableRow}, + BTreeMap, BitwiseLookup, ColMatrix, Felt, FieldElement, MemoryLookup, Vec, }; -use crate::Matrix; mod aux_trace; pub use aux_trace::AuxTraceBuilder; @@ -235,7 +235,7 @@ pub(super) enum ChipletsLookupRow { impl LookupTableRow for ChipletsLookupRow { fn to_value>( &self, - main_trace: &Matrix, + main_trace: &ColMatrix, alphas: &[E], ) -> E { match self { diff --git a/processor/src/chiplets/hasher/aux_trace.rs b/processor/src/chiplets/hasher/aux_trace.rs index 79e8d36454..e0f5525e75 100644 --- a/processor/src/chiplets/hasher/aux_trace.rs +++ b/processor/src/chiplets/hasher/aux_trace.rs @@ -1,8 +1,5 @@ -use super::{Felt, FieldElement, StarkField, Vec, Word}; -use crate::{ - trace::{AuxColumnBuilder, LookupTableRow}, - Matrix, -}; +use super::{ColMatrix, Felt, FieldElement, StarkField, Vec, Word}; +use crate::trace::{AuxColumnBuilder, LookupTableRow}; // AUXILIARY TRACE BUILDER // ================================================================================================ @@ -27,7 +24,7 @@ impl AuxTraceBuilder { /// computation). pub fn build_aux_columns>( &self, - main_trace: &Matrix, + main_trace: &ColMatrix, rand_elements: &[E], ) -> Vec> { let p1 = self.build_aux_column(main_trace, rand_elements); @@ -125,7 +122,7 @@ impl LookupTableRow for SiblingTableRow { /// at least 6 alpha values. fn to_value>( &self, - _main_trace: &Matrix, + _main_trace: &ColMatrix, alphas: &[E], ) -> E { // when the least significant bit of the index is 0, the sibling will be in the 3rd word diff --git a/processor/src/chiplets/hasher/lookups.rs b/processor/src/chiplets/hasher/lookups.rs index 7a572354e7..de9ba4f1dc 100644 --- a/processor/src/chiplets/hasher/lookups.rs +++ b/processor/src/chiplets/hasher/lookups.rs @@ -1,17 +1,12 @@ +use super::{ColMatrix, Felt, FieldElement, LookupTableRow, StarkField, Vec}; use core::ops::Range; - -use super::{Felt, FieldElement, LookupTableRow, StarkField}; -use crate::Matrix; -use vm_core::{ - chiplets::{ - hasher::{ - CAPACITY_LEN, DIGEST_LEN, DIGEST_RANGE, LINEAR_HASH_LABEL, MP_VERIFY_LABEL, - MR_UPDATE_NEW_LABEL, MR_UPDATE_OLD_LABEL, RATE_LEN, RETURN_HASH_LABEL, - RETURN_STATE_LABEL, STATE_WIDTH, - }, - HASHER_RATE_COL_RANGE, HASHER_STATE_COL_RANGE, +use vm_core::chiplets::{ + hasher::{ + CAPACITY_LEN, DIGEST_LEN, DIGEST_RANGE, LINEAR_HASH_LABEL, MP_VERIFY_LABEL, + MR_UPDATE_NEW_LABEL, MR_UPDATE_OLD_LABEL, RATE_LEN, RETURN_HASH_LABEL, RETURN_STATE_LABEL, + STATE_WIDTH, }, - utils::collections::Vec, + HASHER_RATE_COL_RANGE, HASHER_STATE_COL_RANGE, }; // CONSTANTS @@ -82,7 +77,7 @@ impl LookupTableRow for HasherLookup { /// at least 16 alpha values. fn to_value>( &self, - main_trace: &Matrix, + main_trace: &ColMatrix, alphas: &[E], ) -> E { let header = self.get_header_value(&alphas[..NUM_HEADER_ALPHAS]); @@ -167,7 +162,11 @@ fn build_value>(alphas: &[E], elements: &[Felt /// Returns the portion of the hasher state at the provided address that is within the provided /// column range. -fn get_hasher_state_at(addr: u32, main_trace: &Matrix, col_range: Range) -> Vec { +fn get_hasher_state_at( + addr: u32, + main_trace: &ColMatrix, + col_range: Range, +) -> Vec { let row = get_row_from_addr(addr); col_range .map(|col| main_trace.get(HASHER_STATE_COL_RANGE.start + col, row)) @@ -177,7 +176,7 @@ fn get_hasher_state_at(addr: u32, main_trace: &Matrix, col_range: Range, + main_trace: &ColMatrix, ) -> ([Felt; RATE_LEN], [Felt; RATE_LEN]) { let row = get_row_from_addr(addr); diff --git a/processor/src/chiplets/hasher/mod.rs b/processor/src/chiplets/hasher/mod.rs index 8e2f1b2cbc..a12c50f652 100644 --- a/processor/src/chiplets/hasher/mod.rs +++ b/processor/src/chiplets/hasher/mod.rs @@ -1,16 +1,13 @@ use super::{ - Felt, FieldElement, HasherState, LookupTableRow, MerkleRootUpdate, OpBatch, StarkField, - TraceFragment, Vec, Word, ZERO, + trace::LookupTableRow, BTreeMap, ColMatrix, Felt, FieldElement, HasherState, MerkleRootUpdate, + OpBatch, StarkField, TraceFragment, Vec, Word, ZERO, }; -use vm_core::{ - chiplets::hasher::{ - absorb_into_state, get_digest, init_state, init_state_from_words, - init_state_from_words_with_domain, Digest, Selectors, HASH_CYCLE_LEN, LINEAR_HASH, - LINEAR_HASH_LABEL, MP_VERIFY, MP_VERIFY_LABEL, MR_UPDATE_NEW, MR_UPDATE_NEW_LABEL, - MR_UPDATE_OLD, MR_UPDATE_OLD_LABEL, RETURN_HASH, RETURN_HASH_LABEL, RETURN_STATE, - RETURN_STATE_LABEL, STATE_WIDTH, TRACE_WIDTH, - }, - utils::collections::BTreeMap, +use vm_core::chiplets::hasher::{ + absorb_into_state, get_digest, init_state, init_state_from_words, + init_state_from_words_with_domain, Digest, Selectors, HASH_CYCLE_LEN, LINEAR_HASH, + LINEAR_HASH_LABEL, MP_VERIFY, MP_VERIFY_LABEL, MR_UPDATE_NEW, MR_UPDATE_NEW_LABEL, + MR_UPDATE_OLD, MR_UPDATE_OLD_LABEL, RETURN_HASH, RETURN_HASH_LABEL, RETURN_STATE, + RETURN_STATE_LABEL, STATE_WIDTH, TRACE_WIDTH, }; mod lookups; diff --git a/processor/src/chiplets/memory/mod.rs b/processor/src/chiplets/memory/mod.rs index 76020c6bdd..c35613d2eb 100644 --- a/processor/src/chiplets/memory/mod.rs +++ b/processor/src/chiplets/memory/mod.rs @@ -1,11 +1,8 @@ use super::{ - BTreeMap, ChipletsBus, Felt, FieldElement, StarkField, TraceFragment, Vec, Word, ONE, ZERO, -}; -use crate::{ - range::RangeChecker, trace::LookupTableRow, utils::{split_element_u32_into_u16, split_u32_into_u16}, - Matrix, + BTreeMap, ChipletsBus, ColMatrix, Felt, FieldElement, RangeChecker, StarkField, TraceFragment, + Vec, Word, ONE, ZERO, }; use vm_core::chiplets::memory::{ ADDR_COL_IDX, CLK_COL_IDX, CTX_COL_IDX, D0_COL_IDX, D1_COL_IDX, D_INV_COL_IDX, V_COL_RANGE, @@ -337,7 +334,7 @@ impl LookupTableRow for MemoryLookup { /// at least 9 alpha values. fn to_value>( &self, - _main_trace: &Matrix, + _main_trace: &ColMatrix, alphas: &[E], ) -> E { let word_value = self diff --git a/processor/src/chiplets/mod.rs b/processor/src/chiplets/mod.rs index f2ac2154b2..6a024809e4 100644 --- a/processor/src/chiplets/mod.rs +++ b/processor/src/chiplets/mod.rs @@ -1,8 +1,7 @@ use super::{ - BTreeMap, ChipletsTrace, Felt, FieldElement, RangeChecker, StarkField, TraceFragment, Vec, - Word, CHIPLETS_WIDTH, ONE, ZERO, + trace, utils, BTreeMap, ChipletsTrace, ColMatrix, ExecutionError, Felt, FieldElement, + RangeChecker, StarkField, TraceFragment, Vec, Word, CHIPLETS_WIDTH, ONE, ZERO, }; -use crate::{trace::LookupTableRow, ExecutionError}; use vm_core::{ chiplets::bitwise::{BITWISE_AND_LABEL, BITWISE_XOR_LABEL}, chiplets::{ diff --git a/processor/src/decoder/aux_hints.rs b/processor/src/decoder/aux_hints.rs index 5df15ffbe1..773f6caeb4 100644 --- a/processor/src/decoder/aux_hints.rs +++ b/processor/src/decoder/aux_hints.rs @@ -1,8 +1,7 @@ use super::{ - super::trace::LookupTableRow, get_num_groups_in_next_batch, BlockInfo, Felt, FieldElement, - StarkField, Vec, Word, ONE, ZERO, + super::trace::LookupTableRow, get_num_groups_in_next_batch, BlockInfo, ColMatrix, Felt, + FieldElement, StarkField, Vec, Word, ONE, ZERO, }; -use crate::Matrix; // AUXILIARY TRACE HINTS // ================================================================================================ @@ -343,7 +342,7 @@ impl LookupTableRow for BlockStackTableRow { /// at least 12 alpha values. fn to_value>( &self, - _main_trace: &Matrix, + _main_trace: &ColMatrix, alphas: &[E], ) -> E { let is_loop = if self.is_loop { ONE } else { ZERO }; @@ -429,7 +428,7 @@ impl LookupTableRow for BlockHashTableRow { /// at least 8 alpha values. fn to_value>( &self, - _main_trace: &Matrix, + _main_trace: &ColMatrix, alphas: &[E], ) -> E { let is_first_child = if self.is_first_child { ONE } else { ZERO }; @@ -473,7 +472,7 @@ impl LookupTableRow for OpGroupTableRow { /// at least 4 alpha values. fn to_value>( &self, - _main_trace: &Matrix, + _main_trace: &ColMatrix, alphas: &[E], ) -> E { alphas[0] diff --git a/processor/src/decoder/mod.rs b/processor/src/decoder/mod.rs index 1ee0293324..6d66c73437 100644 --- a/processor/src/decoder/mod.rs +++ b/processor/src/decoder/mod.rs @@ -1,6 +1,7 @@ use super::{ - AdviceProvider, Call, ExecutionError, Felt, FieldElement, Join, Loop, OpBatch, Operation, - Process, Span, Split, StarkField, Vec, Word, MIN_TRACE_LEN, ONE, OP_BATCH_SIZE, ZERO, + AdviceProvider, Call, ColMatrix, ExecutionError, Felt, FieldElement, Join, Loop, OpBatch, + Operation, Process, Span, Split, StarkField, Vec, Word, MIN_TRACE_LEN, ONE, OP_BATCH_SIZE, + ZERO, }; use vm_core::{ chiplets::hasher::DIGEST_LEN, diff --git a/processor/src/decorators/mod.rs b/processor/src/decorators/mod.rs index b7f80ead8f..46468bc9d8 100644 --- a/processor/src/decorators/mod.rs +++ b/processor/src/decorators/mod.rs @@ -7,7 +7,7 @@ use winter_prover::math::fft; // TYPE ALIASES // ================================================================================================ -type Ext2Element = QuadExtension; +type QuadFelt = QuadExtension; // DECORATORS // ================================================================================================ @@ -203,15 +203,11 @@ where let coef0 = self.stack.get(1); let coef1 = self.stack.get(0); - let elm = Ext2Element::new(coef0, coef1); - if elm == Ext2Element::ZERO { + let elm = QuadFelt::new(coef0, coef1); + if elm == QuadFelt::ZERO { return Err(ExecutionError::DivideByZero(self.system.clk())); } - - let inv_elm = elm.inv(); - - let elm_arr = [inv_elm]; - let coeffs = Ext2Element::as_base_elements(&elm_arr); + let coeffs = elm.inv().to_base_elements(); self.advice_provider.push_stack(AdviceSource::Value(coeffs[1]))?; self.advice_provider.push_stack(AdviceSource::Value(coeffs[0]))?; @@ -269,14 +265,14 @@ where ExecutionError::UninitializedMemoryAddress(in_evaluations_addr + i as u64) })?; - poly.push(Ext2Element::new(word[0], word[1])); - poly.push(Ext2Element::new(word[2], word[3])); + poly.push(QuadFelt::new(word[0], word[1])); + poly.push(QuadFelt::new(word[2], word[3])); } let twiddles = fft::get_inv_twiddles::(in_evaluations_len); - fft::interpolate_poly::(&mut poly, &twiddles); + fft::interpolate_poly::(&mut poly, &twiddles); - for i in Ext2Element::as_base_elements(&poly[..out_poly_len]).iter().rev().copied() { + for i in QuadFelt::slice_as_base_elements(&poly[..out_poly_len]).iter().rev().copied() { self.advice_provider.push_stack(AdviceSource::Value(i))?; } diff --git a/processor/src/errors.rs b/processor/src/errors.rs index 1f68975ac4..e64ad09f7a 100644 --- a/processor/src/errors.rs +++ b/processor/src/errors.rs @@ -1,6 +1,7 @@ use super::{ + crypto::MerkleError, system::{FMP_MAX, FMP_MIN}, - CodeBlock, Digest, Felt, MerkleError, QuadFelt, Word, + CodeBlock, Digest, Felt, QuadFelt, Word, }; use core::fmt::{Display, Formatter}; use vm_core::{stack::STACK_TOP_SIZE, utils::to_hex}; diff --git a/processor/src/lib.rs b/processor/src/lib.rs index d4b6336d92..e387360739 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -5,15 +5,8 @@ extern crate alloc; pub use vm_core::{ - chiplets::hasher::Digest, - crypto::{ - hash::{Blake3_192, Blake3_256, ElementHasher, Hasher, Rpo256}, - merkle::MerkleError, - }, - errors::InputError, - utils::DeserializationError, - AssemblyOp, Kernel, Operation, Program, ProgramInfo, QuadExtension, StackInputs, StackOutputs, - Word, + chiplets::hasher::Digest, errors::InputError, utils::DeserializationError, AssemblyOp, Kernel, + Operation, Program, ProgramInfo, QuadExtension, StackInputs, StackOutputs, Word, }; use vm_core::{ code_blocks::{ @@ -24,7 +17,7 @@ use vm_core::{ StackTopState, StarkField, CHIPLETS_WIDTH, DECODER_TRACE_WIDTH, MIN_TRACE_LEN, ONE, RANGE_CHECK_TRACE_WIDTH, STACK_TRACE_WIDTH, SYS_TRACE_WIDTH, ZERO, }; -use winter_prover::Matrix; +use winter_prover::ColMatrix; mod decorators; mod operations; @@ -68,6 +61,14 @@ pub mod math { pub use winter_prover::math::fft; } +pub mod crypto { + pub use vm_core::crypto::{ + hash::{Blake3_192, Blake3_256, ElementHasher, Hasher, Rpo256}, + merkle::MerkleError, + random::{RandomCoin, RpoRandomCoin, WinterRandomCoin}, + }; +} + // TYPE ALIASES // ================================================================================================ diff --git a/processor/src/operations/ext2_ops.rs b/processor/src/operations/ext2_ops.rs index e73ed448cc..60dbf85fc6 100644 --- a/processor/src/operations/ext2_ops.rs +++ b/processor/src/operations/ext2_ops.rs @@ -32,7 +32,7 @@ where #[cfg(test)] mod tests { - type Ext2Element = QuadExtension; + type QuadFelt = QuadExtension; use super::{ super::{Felt, FieldElement, Operation, STACK_TOP_SIZE}, Process, @@ -54,10 +54,9 @@ mod tests { // multiply the top two values process.execute_op(Operation::Ext2Mul).unwrap(); - let a = Ext2Element::new(a0, a1); - let b = Ext2Element::new(b0, b1); - let c = [b * a]; - let c = Ext2Element::as_base_elements(&c); + let a = QuadFelt::new(a0, a1); + let b = QuadFelt::new(b0, b1); + let c = (b * a).to_base_elements(); let expected = build_expected(&[b1, b0, c[1], c[0]]); assert_eq!(STACK_TOP_SIZE, process.stack.depth()); diff --git a/processor/src/operations/fri_ops.rs b/processor/src/operations/fri_ops.rs index b99488dea1..a5e824e3de 100644 --- a/processor/src/operations/fri_ops.rs +++ b/processor/src/operations/fri_ops.rs @@ -176,10 +176,10 @@ where /// Populates helper registers with intermediate values used in the folding procedure. fn set_helper_registers(&mut self, ev: QuadFelt, es: QuadFelt, x: Felt, x_inv: Felt) { let ev_arr = [ev]; - let ev_felts = QuadFelt::as_base_elements(&ev_arr); + let ev_felts = QuadFelt::slice_as_base_elements(&ev_arr); let es_arr = [es]; - let es_felts = QuadFelt::as_base_elements(&es_arr); + let es_felts = QuadFelt::slice_as_base_elements(&es_arr); let values = [ev_felts[0], ev_felts[1], es_felts[0], es_felts[1], x, x_inv]; self.decoder.set_user_op_helpers(Operation::FriE2F4, &values); @@ -367,7 +367,7 @@ mod tests { assert_eq!(stack_state[15], end_ptr); // --- check helper registers ----------------------------------------- - let mut expected_helpers = QuadFelt::as_base_elements(&[ev, es]).to_vec(); + let mut expected_helpers = QuadFelt::slice_as_base_elements(&[ev, es]).to_vec(); expected_helpers.push(x); expected_helpers.push(x_inv); assert_eq!(expected_helpers, process.decoder.get_user_op_helpers().to_vec()); diff --git a/processor/src/range/aux_trace.rs b/processor/src/range/aux_trace.rs index 81fa17c3fa..618962d76c 100644 --- a/processor/src/range/aux_trace.rs +++ b/processor/src/range/aux_trace.rs @@ -1,10 +1,8 @@ -use vm_core::{range::V_COL_IDX, utils::uninit_vector}; - -use super::{BTreeMap, CycleRangeChecks, Felt, FieldElement, RangeCheckFlag, Vec}; -use crate::{ - trace::{build_lookup_table_row_values, NUM_RAND_ROWS}, - Matrix, +use super::{ + build_lookup_table_row_values, uninit_vector, BTreeMap, ColMatrix, CycleRangeChecks, Felt, + FieldElement, RangeCheckFlag, Vec, NUM_RAND_ROWS, }; +use vm_core::range::V_COL_IDX; // AUXILIARY TRACE BUILDER // ================================================================================================ @@ -60,7 +58,7 @@ impl AuxTraceBuilder { /// `p1`. It contains the product of the lookups performed by the Stack at each row. pub fn build_aux_columns>( &self, - main_trace: &Matrix, + main_trace: &ColMatrix, rand_elements: &[E], ) -> Vec> { let p0 = self.build_aux_col_p0(main_trace, rand_elements); @@ -73,7 +71,7 @@ impl AuxTraceBuilder { /// 16-bit section of the table so that the starting and ending value are both one. fn build_aux_col_p0>( &self, - main_trace: &Matrix, + main_trace: &ColMatrix, rand_elements: &[E], ) -> Vec { let mut aux_column = E::zeroed_vector(main_trace.num_rows()); @@ -136,7 +134,7 @@ impl AuxTraceBuilder { /// range check lookups performed by user operations match those executed by the Range Checker. fn build_aux_col_p1>( &self, - main_trace: &Matrix, + main_trace: &ColMatrix, alphas: &[E], ) -> (Vec, Vec) { // compute the inverses for range checks performed by operations. diff --git a/processor/src/range/mod.rs b/processor/src/range/mod.rs index 9a07b3b2a2..20cbc8c983 100644 --- a/processor/src/range/mod.rs +++ b/processor/src/range/mod.rs @@ -1,6 +1,8 @@ -use super::{BTreeMap, Felt, FieldElement, Vec, ONE, ZERO}; -use crate::RangeCheckTrace; -use vm_core::utils::uninit_vector; +use super::{ + trace::{build_lookup_table_row_values, LookupTableRow, NUM_RAND_ROWS}, + utils::uninit_vector, + BTreeMap, ColMatrix, Felt, FieldElement, RangeCheckTrace, Vec, ONE, ZERO, +}; mod aux_trace; pub use aux_trace::AuxTraceBuilder; diff --git a/processor/src/range/request.rs b/processor/src/range/request.rs index b4a99f75b3..28cf9c0704 100644 --- a/processor/src/range/request.rs +++ b/processor/src/range/request.rs @@ -1,5 +1,4 @@ -use super::{Felt, FieldElement}; -use crate::{trace::LookupTableRow, Matrix}; +use super::{ColMatrix, Felt, FieldElement, LookupTableRow}; // PROCESSOR RANGE CHECKS // ================================================================================================ @@ -55,7 +54,7 @@ impl CycleRangeChecks { /// element in the field specified by E. pub fn to_stack_value>( &self, - main_trace: &Matrix, + main_trace: &ColMatrix, alphas: &[E], ) -> E { let mut value = E::ONE; @@ -71,7 +70,7 @@ impl CycleRangeChecks { /// element in the field specified by E. fn to_mem_value>( &self, - main_trace: &Matrix, + main_trace: &ColMatrix, alphas: &[E], ) -> E { let mut value = E::ONE; @@ -89,7 +88,7 @@ impl LookupTableRow for CycleRangeChecks { /// at least 1 alpha value. Includes all values included at this cycle from all processors. fn to_value>( &self, - main_trace: &Matrix, + main_trace: &ColMatrix, alphas: &[E], ) -> E { let stack_value = self.to_stack_value(main_trace, alphas); @@ -116,7 +115,7 @@ impl LookupTableRow for RangeCheckRequest { /// at least 1 alpha value. fn to_value>( &self, - _main_trace: &Matrix, + _main_trace: &ColMatrix, alphas: &[E], ) -> E { let alpha: E = alphas[0]; diff --git a/processor/src/stack/aux_trace.rs b/processor/src/stack/aux_trace.rs index 00415055b1..d040a63b83 100644 --- a/processor/src/stack/aux_trace.rs +++ b/processor/src/stack/aux_trace.rs @@ -1,7 +1,7 @@ use super::{ - super::trace::AuxColumnBuilder, Felt, FieldElement, OverflowTableRow, OverflowTableUpdate, Vec, + super::trace::AuxColumnBuilder, ColMatrix, Felt, FieldElement, OverflowTableRow, + OverflowTableUpdate, Vec, }; -use crate::Matrix; // AUXILIARY TRACE BUILDER // ================================================================================================ @@ -26,7 +26,7 @@ impl AuxTraceBuilder { /// column p1 describing states of the stack overflow table. pub fn build_aux_columns>( &self, - main_trace: &Matrix, + main_trace: &ColMatrix, rand_elements: &[E], ) -> Vec> { let p1 = self.build_aux_column(main_trace, rand_elements); diff --git a/processor/src/stack/mod.rs b/processor/src/stack/mod.rs index f9e995309b..e0c17c96c7 100644 --- a/processor/src/stack/mod.rs +++ b/processor/src/stack/mod.rs @@ -1,5 +1,6 @@ use super::{ - BTreeMap, Felt, FieldElement, StackInputs, StackOutputs, Vec, ONE, STACK_TRACE_WIDTH, ZERO, + BTreeMap, ColMatrix, Felt, FieldElement, StackInputs, StackOutputs, Vec, ONE, + STACK_TRACE_WIDTH, ZERO, }; use core::cmp; use vm_core::stack::STACK_TOP_SIZE; diff --git a/processor/src/stack/overflow.rs b/processor/src/stack/overflow.rs index ece6f4dd29..e4d1cc4872 100644 --- a/processor/src/stack/overflow.rs +++ b/processor/src/stack/overflow.rs @@ -1,7 +1,7 @@ use super::{ - super::trace::LookupTableRow, AuxTraceBuilder, BTreeMap, Felt, FieldElement, Vec, ZERO, + super::trace::LookupTableRow, AuxTraceBuilder, BTreeMap, ColMatrix, Felt, FieldElement, Vec, + ZERO, }; -use crate::Matrix; use vm_core::{utils::uninit_vector, StarkField}; // OVERFLOW TABLE @@ -271,7 +271,7 @@ impl LookupTableRow for OverflowTableRow { /// at least 4 alpha values. fn to_value>( &self, - _main_trace: &Matrix, + _main_trace: &ColMatrix, alphas: &[E], ) -> E { alphas[0] diff --git a/processor/src/trace/decoder/mod.rs b/processor/src/trace/decoder/mod.rs index fbabf7e006..8b026ba14a 100644 --- a/processor/src/trace/decoder/mod.rs +++ b/processor/src/trace/decoder/mod.rs @@ -1,4 +1,4 @@ -use super::{utils::build_lookup_table_row_values, Felt, FieldElement, Matrix, Vec}; +use super::{utils::build_lookup_table_row_values, ColMatrix, Felt, FieldElement, Vec}; use crate::decoder::{AuxTraceHints, BlockTableUpdate, OpGroupTableUpdate}; use vm_core::{utils::uninit_vector, DECODER_TRACE_OFFSET}; @@ -16,7 +16,7 @@ const ADDR_COL_IDX: usize = DECODER_TRACE_OFFSET + vm_core::decoder::ADDR_COL_ID /// Builds and returns decoder auxiliary trace columns p1, p2, and p3 describing states of block /// stack, block hash, and op group tables respectively. pub fn build_aux_columns>( - main_trace: &Matrix, + main_trace: &ColMatrix, aux_trace_hints: &AuxTraceHints, rand_elements: &[E], ) -> Vec> { @@ -32,7 +32,7 @@ pub fn build_aux_columns>( /// Builds the execution trace of the decoder's `p1` column which describes the state of the block /// stack table via multiset checks. fn build_aux_col_p1>( - main_trace: &Matrix, + main_trace: &ColMatrix, aux_trace_hints: &AuxTraceHints, alphas: &[E], ) -> Vec { @@ -116,7 +116,7 @@ fn build_aux_col_p1>( /// Builds the execution trace of the decoder's `p2` column which describes the state of the block /// hash table via multiset checks. fn build_aux_col_p2>( - main_trace: &Matrix, + main_trace: &ColMatrix, aux_trace_hints: &AuxTraceHints, alphas: &[E], ) -> Vec { @@ -226,7 +226,7 @@ fn build_aux_col_p2>( /// Builds the execution trace of the decoder's `p3` column which describes the state of the op /// group table via multiset checks. fn build_aux_col_p3>( - main_trace: &Matrix, + main_trace: &ColMatrix, trace_len: usize, aux_trace_hints: &AuxTraceHints, alphas: &[E], @@ -298,6 +298,6 @@ fn build_aux_col_p3>( // ================================================================================================ /// Returns the value in the block address column at the specified row. -fn get_block_addr(main_trace: &Matrix, row_idx: u32) -> Felt { +fn get_block_addr(main_trace: &ColMatrix, row_idx: u32) -> Felt { main_trace.get(ADDR_COL_IDX, row_idx as usize) } diff --git a/processor/src/trace/mod.rs b/processor/src/trace/mod.rs index 7660e1a0b8..d07da86e32 100644 --- a/processor/src/trace/mod.rs +++ b/processor/src/trace/mod.rs @@ -1,9 +1,10 @@ use super::{ chiplets::{AuxTraceBuilder as ChipletsAuxTraceBuilder, HasherAuxTraceBuilder}, + crypto::RpoRandomCoin, decoder::AuxTraceHints as DecoderAuxTraceHints, range::AuxTraceBuilder as RangeCheckerAuxTraceBuilder, stack::AuxTraceBuilder as StackAuxTraceBuilder, - AdviceProvider, Digest, Felt, FieldElement, Process, StackTopState, Vec, + AdviceProvider, ColMatrix, Digest, Felt, FieldElement, Process, StackTopState, Vec, }; use vm_core::{ decoder::{NUM_USER_OP_HELPERS, USER_OP_HELPERS_OFFSET}, @@ -11,7 +12,7 @@ use vm_core::{ ProgramInfo, StackOutputs, AUX_TRACE_RAND_ELEMENTS, AUX_TRACE_WIDTH, DECODER_TRACE_OFFSET, MIN_TRACE_LEN, STACK_TRACE_OFFSET, TRACE_WIDTH, ZERO, }; -use winter_prover::{EvaluationFrame, Matrix, Serializable, Trace, TraceLayout}; +use winter_prover::{EvaluationFrame, Trace, TraceLayout}; #[cfg(feature = "std")] use vm_core::StarkField; @@ -30,11 +31,6 @@ mod tests; /// Number of rows at the end of an execution trace which are injected with random values. pub const NUM_RAND_ROWS: usize = 1; -// TYPE ALIASES -// ================================================================================================ - -type RandomCoin = vm_core::utils::RandomCoin; - // VM EXECUTION TRACE // ================================================================================================ @@ -56,7 +52,7 @@ pub struct AuxTraceHints { pub struct ExecutionTrace { meta: Vec, layout: TraceLayout, - main_trace: Matrix, + main_trace: ColMatrix, aux_trace_hints: AuxTraceHints, program_info: ProgramInfo, stack_outputs: StackOutputs, @@ -81,7 +77,7 @@ impl ExecutionTrace { // we are using random values only to stabilize constraint degrees, and not to achieve // perfect zero knowledge. let program_hash: Digest = process.decoder.program_hash().into(); - let rng = RandomCoin::new(&program_hash.to_bytes()); + let rng = RpoRandomCoin::new(program_hash.as_elements()); // create a new program info instance with the underlying kernel let kernel = process.kernel().clone(); @@ -91,7 +87,7 @@ impl ExecutionTrace { Self { meta: Vec::new(), layout: TraceLayout::new(TRACE_WIDTH, [AUX_TRACE_WIDTH], [AUX_TRACE_RAND_ELEMENTS]), - main_trace: Matrix::new(main_trace), + main_trace: ColMatrix::new(main_trace), aux_trace_hints, program_info, stack_outputs, @@ -174,7 +170,7 @@ impl ExecutionTrace { where A: AdviceProvider, { - let rng = RandomCoin::new(&[0; 32]); + let rng = RpoRandomCoin::new(&[ZERO; 4]); finalize_trace(process, rng) } } @@ -197,15 +193,15 @@ impl Trace for ExecutionTrace { &self.meta } - fn main_segment(&self) -> &Matrix { + fn main_segment(&self) -> &ColMatrix { &self.main_trace } fn build_aux_segment>( &mut self, - aux_segments: &[Matrix], + aux_segments: &[ColMatrix], rand_elements: &[E], - ) -> Option> { + ) -> Option> { // we only have one auxiliary segment if !aux_segments.is_empty() { return None; @@ -246,14 +242,14 @@ impl Trace for ExecutionTrace { .collect::>(); // inject random values into the last rows of the trace - let mut rng = RandomCoin::new(&self.program_hash().to_bytes()); + let mut rng = RpoRandomCoin::new(self.program_hash().as_elements()); for i in self.length() - NUM_RAND_ROWS..self.length() { for column in aux_columns.iter_mut() { column[i] = rng.draw().expect("failed to draw a random value"); } } - Some(Matrix::new(aux_columns)) + Some(ColMatrix::new(aux_columns)) } fn read_main_frame(&self, row_idx: usize, frame: &mut EvaluationFrame) { @@ -274,7 +270,7 @@ impl Trace for ExecutionTrace { /// - Inserting random values in the last row of all columns. This helps ensure that there /// are no repeating patterns in each column and each column contains a least two distinct /// values. This, in turn, ensures that polynomial degrees of all columns are stable. -fn finalize_trace
(process: Process, mut rng: RandomCoin) -> (Vec>, AuxTraceHints) +fn finalize_trace(process: Process, mut rng: RpoRandomCoin) -> (Vec>, AuxTraceHints) where A: AdviceProvider, { diff --git a/processor/src/trace/utils.rs b/processor/src/trace/utils.rs index cc8081e60a..cc095276aa 100644 --- a/processor/src/trace/utils.rs +++ b/processor/src/trace/utils.rs @@ -1,4 +1,4 @@ -use super::{Felt, FieldElement, Matrix, Vec}; +use super::{ColMatrix, Felt, FieldElement, Vec}; use core::slice; use vm_core::utils::uninit_vector; @@ -76,7 +76,7 @@ pub trait LookupTableRow { /// computed using the provided random values. fn to_value>( &self, - main_trace: &Matrix, + main_trace: &ColMatrix, rand_values: &[E], ) -> E; } @@ -89,7 +89,7 @@ pub trait LookupTableRow { /// computationally infeasible. pub fn build_lookup_table_row_values, R: LookupTableRow>( rows: &[R], - main_trace: &Matrix, + main_trace: &ColMatrix, rand_values: &[E], ) -> (Vec, Vec) { let mut row_values = unsafe { uninit_vector(rows.len()) }; @@ -148,7 +148,7 @@ pub trait AuxColumnBuilder { // -------------------------------------------------------------------------------------------- /// Builds and returns the auxiliary trace column managed by this builder. - fn build_aux_column(&self, main_trace: &Matrix, alphas: &[E]) -> Vec + fn build_aux_column(&self, main_trace: &ColMatrix, alphas: &[E]) -> Vec where E: FieldElement, { @@ -198,7 +198,7 @@ pub trait AuxColumnBuilder { /// Builds and returns row values and their inverses for all rows which were added to the /// lookup table managed by this column builder. - fn build_row_values(&self, main_trace: &Matrix, alphas: &[E]) -> (Vec, Vec) + fn build_row_values(&self, main_trace: &ColMatrix, alphas: &[E]) -> (Vec, Vec) where E: FieldElement, { diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 174478b162..da2c9a34ed 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -20,4 +20,4 @@ std = ["air/std", "processor/std", "log/std", "winter-prover/std"] air = { package = "miden-air", path = "../air", version = "0.5", default-features = false } log = { version = "0.4", default-features = false } processor = { package = "miden-processor", path = "../processor", version = "0.5", default-features = false } -winter-prover = { package = "winter-prover", version = "0.5", default-features = false } +winter-prover = { package = "winter-prover", version = "0.6", default-features = false } diff --git a/prover/src/lib.rs b/prover/src/lib.rs index b04271569c..8190bd73fd 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -2,7 +2,13 @@ use air::{ProcessorAir, PublicInputs}; use core::marker::PhantomData; -use processor::{math::Felt, Blake3_192, Blake3_256, ElementHasher, ExecutionTrace, Rpo256}; +use processor::{ + crypto::{ + Blake3_192, Blake3_256, ElementHasher, RandomCoin, Rpo256, RpoRandomCoin, WinterRandomCoin, + }, + math::Felt, + ExecutionTrace, +}; use winter_prover::{ProofOptions as WinterProofOptions, Prover}; #[cfg(feature = "std")] @@ -17,8 +23,8 @@ use winter_prover::Trace; pub use air::{DeserializationError, ExecutionProof, FieldExtension, HashFunction, ProofOptions}; pub use processor::{ - math, utils, AdviceInputs, AdviceProvider, Digest, ExecutionError, Hasher, InputError, - MemAdviceProvider, MerkleError, Program, StackInputs, StackOutputs, Word, + crypto, math, utils, AdviceInputs, AdviceProvider, Digest, ExecutionError, InputError, + MemAdviceProvider, Program, StackInputs, StackOutputs, Word, }; pub use winter_prover::StarkProof; @@ -60,18 +66,24 @@ where // generate STARK proof let proof = match hash_fn { - HashFunction::Blake3_192 => { - ExecutionProver::::new(options, stack_inputs, stack_outputs.clone()) - .prove(trace) - } - HashFunction::Blake3_256 => { - ExecutionProver::::new(options, stack_inputs, stack_outputs.clone()) - .prove(trace) - } - HashFunction::Rpo256 => { - ExecutionProver::::new(options, stack_inputs, stack_outputs.clone()) - .prove(trace) - } + HashFunction::Blake3_192 => ExecutionProver::>::new( + options, + stack_inputs, + stack_outputs.clone(), + ) + .prove(trace), + HashFunction::Blake3_256 => ExecutionProver::>::new( + options, + stack_inputs, + stack_outputs.clone(), + ) + .prove(trace), + HashFunction::Rpo256 => ExecutionProver::::new( + options, + stack_inputs, + stack_outputs.clone(), + ) + .prove(trace), } .map_err(ExecutionError::ProverError)?; let proof = ExecutionProof::new(proof, hash_fn); @@ -82,19 +94,21 @@ where // PROVER // ================================================================================================ -struct ExecutionProver +struct ExecutionProver where H: ElementHasher, + R: RandomCoin, { - hasher: PhantomData, + random_coin: PhantomData, options: WinterProofOptions, stack_inputs: StackInputs, stack_outputs: StackOutputs, } -impl ExecutionProver +impl ExecutionProver where H: ElementHasher, + R: RandomCoin, { pub fn new( options: ProofOptions, @@ -102,7 +116,7 @@ where stack_outputs: StackOutputs, ) -> Self { Self { - hasher: PhantomData, + random_coin: PhantomData, options: options.into(), stack_inputs, stack_outputs, @@ -131,14 +145,16 @@ where } } -impl Prover for ExecutionProver +impl Prover for ExecutionProver where H: ElementHasher, + R: RandomCoin + Sync, { type Air = ProcessorAir; type BaseField = Felt; type Trace = ExecutionTrace; type HashFn = H; + type RandomCoin = R; fn options(&self) -> &WinterProofOptions { &self.options diff --git a/verifier/Cargo.toml b/verifier/Cargo.toml index bb8e978b9b..334bea822e 100644 --- a/verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -22,4 +22,4 @@ std = ["air/std", "vm-core/std", "winter-verifier/std"] [dependencies] air = { package = "miden-air", path = "../air", version = "0.5", default-features = false } vm-core = { package = "miden-core", path = "../core", version = "0.5", default-features = false } -winter-verifier = { package = "winter-verifier", version = "0.5", default-features = false } +winter-verifier = { package = "winter-verifier", version = "0.6", default-features = false } diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index e237a5c052..b321e84f07 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -2,7 +2,10 @@ use air::{HashFunction, ProcessorAir, PublicInputs}; use core::fmt; -use vm_core::crypto::hash::{Blake3_192, Blake3_256, Rpo256}; +use vm_core::crypto::{ + hash::{Blake3_192, Blake3_256, Rpo256}, + random::{RpoRandomCoin, WinterRandomCoin}, +}; use winter_verifier::verify as verify_proof; // EXPORTS @@ -48,9 +51,15 @@ pub fn verify( let pub_inputs = PublicInputs::new(program_info, stack_inputs, stack_outputs); let (hash_fn, proof) = proof.into_parts(); match hash_fn { - HashFunction::Blake3_192 => verify_proof::(proof, pub_inputs), - HashFunction::Blake3_256 => verify_proof::(proof, pub_inputs), - HashFunction::Rpo256 => verify_proof::(proof, pub_inputs), + HashFunction::Blake3_192 => { + verify_proof::>(proof, pub_inputs) + } + HashFunction::Blake3_256 => { + verify_proof::>(proof, pub_inputs) + } + HashFunction::Rpo256 => { + verify_proof::(proof, pub_inputs) + } } .map_err(VerificationError::VerifierError)?; From 247b63b6a8375f8cddc2e19ba1ba17f24d145e94 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sun, 26 Mar 2023 23:14:39 -0700 Subject: [PATCH 35/47] fix: FRI remainder testes --- .../integration/stdlib/crypto/fri/mod.rs | 2 + .../stdlib/crypto/fri/remainder.rs | 18 +- .../stdlib/crypto/fri/verifier_fri_e2f4.rs | 24 ++- miden/tests/integration/stdlib/crypto/mod.rs | 2 +- processor/src/lib.rs | 2 +- stdlib/asm/crypto/fri/ext2fri.masm | 176 +++++++++--------- 6 files changed, 112 insertions(+), 112 deletions(-) diff --git a/miden/tests/integration/stdlib/crypto/fri/mod.rs b/miden/tests/integration/stdlib/crypto/fri/mod.rs index b605509189..523ae64795 100644 --- a/miden/tests/integration/stdlib/crypto/fri/mod.rs +++ b/miden/tests/integration/stdlib/crypto/fri/mod.rs @@ -15,6 +15,7 @@ pub use verifier_fri_e2f4::*; mod remainder; #[test] +#[ignore = "enable after new remainder verification is implemented"] fn fri_fold4_ext2_remainder32() { let source = " use.std::crypto::fri::frie2f4 @@ -56,6 +57,7 @@ fn fri_fold4_ext2_remainder32() { } #[test] +#[ignore = "enable after new remainder verification is implemented"] fn fri_fold4_ext2_remainder64() { let source = " use.std::crypto::fri::frie2f4 diff --git a/miden/tests/integration/stdlib/crypto/fri/remainder.rs b/miden/tests/integration/stdlib/crypto/fri/remainder.rs index 8fd7355a3c..0de7704fdc 100644 --- a/miden/tests/integration/stdlib/crypto/fri/remainder.rs +++ b/miden/tests/integration/stdlib/crypto/fri/remainder.rs @@ -1,9 +1,9 @@ -use super::{build_test, Felt}; +use super::build_test; use processor::math::fft; use test_case::test_case; -use vm_core::{FieldElement, QuadExtension, StarkField}; +use vm_core::{Felt, FieldElement, QuadExtension, StarkField}; -type Ext2Element = QuadExtension; +type QuadFelt = QuadExtension; #[test_case(8, 1; "poly_8 |> evaluated_8 |> interpolated_8")] #[test_case(8, 2; "poly_8 |> evaluated_16 |> interpolated_8")] @@ -66,9 +66,9 @@ fn test_decorator_ext2intt(in_poly_len: usize, blowup: usize) { let twiddles = fft::get_twiddles(poly.len()); let evals = fft::evaluate_poly_with_offset(&poly, &twiddles, Felt::ONE, blowup); - let ifelts = Ext2Element::as_base_elements(&evals); + let ifelts = QuadFelt::slice_as_base_elements(&evals); let iu64s = ifelts.iter().map(|v| v.as_int()).collect::>(); - let ou64s = Ext2Element::as_base_elements(&poly) + let ou64s = QuadFelt::slice_as_base_elements(&poly) .iter() .rev() .map(|v| v.as_int()) @@ -103,11 +103,11 @@ fn test_verify_remainder_64() { end "; - let poly = rand_utils::rand_vector::(8); + let poly = rand_utils::rand_vector::(8); let twiddles = fft::get_twiddles(poly.len()); let evals = fft::evaluate_poly_with_offset(&poly, &twiddles, Felt::ONE, 8); - let ifelts = Ext2Element::as_base_elements(&evals); + let ifelts = QuadFelt::slice_as_base_elements(&evals); let iu64s = ifelts.iter().map(|v| v.as_int()).collect::>(); let test = build_test!(source, &iu64s); @@ -140,11 +140,11 @@ fn test_verify_remainder_32() { end "; - let poly = rand_utils::rand_vector::(4); + let poly = rand_utils::rand_vector::(4); let twiddles = fft::get_twiddles(poly.len()); let evals = fft::evaluate_poly_with_offset(&poly, &twiddles, Felt::ONE, 8); - let ifelts = Ext2Element::as_base_elements(&evals); + let ifelts = QuadFelt::slice_as_base_elements(&evals); let iu64s = ifelts.iter().map(|v| v.as_int()).collect::>(); let test = build_test!(source, &iu64s); diff --git a/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs b/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs index 97d96dde68..aeeda90cc2 100644 --- a/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs +++ b/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs @@ -1,12 +1,10 @@ -use std::marker::PhantomData; -use std::mem; - +use core::{marker::PhantomData, mem}; use miden::{math::fft, utils::math::log2, Digest as MidenDigest}; -use processor::Hasher; +use processor::crypto::{Hasher, RandomCoin, WinterRandomCoin}; use vm_core::{ chiplets::hasher::Hasher as MidenHasher, crypto::merkle::{MerklePath, MerklePathSet, NodeIndex}, - utils::{IntoBytes, RandomCoin}, + utils::IntoBytes, Felt, FieldElement, QuadExtension, StarkField, ZERO, }; use winter_fri::{ @@ -94,7 +92,7 @@ pub fn fri_prove_verify_fold4_ext2( let remainder: Vec = proof.parse_remainder().expect("should return remainder"); - let remainder: Vec = QuadExt::as_base_elements(&remainder[..]) + let remainder: Vec = QuadExt::slice_as_base_elements(&remainder[..]) .to_owned() .iter() .map(|a| a.as_int()) @@ -122,7 +120,7 @@ pub fn fri_prove_verify_fold4_ext2( pub fn build_prover_channel( trace_length: usize, options: &FriOptions, -) -> DefaultProverChannel { +) -> DefaultProverChannel> { DefaultProverChannel::new(trace_length * options.blowup_factor(), 32) } @@ -159,7 +157,7 @@ fn verify_proof( options.folding_factor(), ) .unwrap(); - let mut coin = RandomCoin::::new(&[]); + let mut coin = WinterRandomCoin::new(&[]); let miden_verifier = FriVerifierFold4Ext2::new(&mut channel, &mut coin, options.clone(), max_degree)?; @@ -186,7 +184,7 @@ pub struct FriVerifierFold4Ext2 { impl FriVerifierFold4Ext2 { pub fn new( channel: &mut MidenFriVerifierChannel, - public_coin: &mut RandomCoin, + public_coin: &mut WinterRandomCoin, options: FriOptions, max_poly_degree: usize, ) -> Result { @@ -203,7 +201,7 @@ impl FriVerifierFold4Ext2 { let mut max_degree_plus_1 = max_poly_degree + 1; for (depth, commitment) in layer_commitments.iter().enumerate() { public_coin.reseed(*commitment); - let alpha = public_coin.draw().map_err(VerifierError::PublicCoinError)?; + let alpha = public_coin.draw().map_err(VerifierError::RandomCoinError)?; layer_alphas.push(alpha); // make sure the degree can be reduced by the folding factor at all layers @@ -319,7 +317,7 @@ fn iterate_query_fold_4_quad_ext( let mut init_exp = initial_domain_generator.exp((position as u64).into()); let arr = vec![evaluation]; - let a = QuadExt::as_base_elements(&arr); + let a = QuadExt::slice_as_base_elements(&arr); let mut partial_tap = vec![]; let position_evaluation = vec![a[0].as_int(), a[1].as_int(), (position as u64).into(), 0]; @@ -396,7 +394,7 @@ fn iterate_query_fold_4_quad_ext( }; let arr = vec![layer_alphas[depth]]; - let a = QuadExt::as_base_elements(&arr); + let a = QuadExt::slice_as_base_elements(&arr); alphas.push(a[0].as_int()); alphas.push(a[1].as_int()); alphas.push(0); @@ -474,7 +472,7 @@ impl UnBatch for MidenFriVerifierChannel Date: Fri, 3 Mar 2023 01:02:22 +0100 Subject: [PATCH 36/47] docs: added information about domain to the hash description --- core/src/program/blocks/call_block.rs | 15 ++++++---- core/src/program/blocks/join_block.rs | 10 +++++-- core/src/program/blocks/loop_block.rs | 16 +++++------ core/src/program/blocks/proxy_block.rs | 6 ++-- core/src/program/blocks/span_block.rs | 40 ++++++++++++++++---------- core/src/program/blocks/split_block.rs | 15 +++++----- 6 files changed, 60 insertions(+), 42 deletions(-) diff --git a/core/src/program/blocks/call_block.rs b/core/src/program/blocks/call_block.rs index d0cc2a72e2..b6e84c36f9 100644 --- a/core/src/program/blocks/call_block.rs +++ b/core/src/program/blocks/call_block.rs @@ -3,14 +3,17 @@ use crate::utils::to_hex; // CALL BLOCK // ================================================================================================ -/// A code block describing a function call. +/// Block for a function call. /// -/// When the VM executes a Call block, it simply executes the code of the underlying function. -/// Thus, to execute a function call, the VM must have access to the function's body, otherwise, -/// the execution fails. +/// Executes the function referenced by `fn_hash`. Fails if the body is unavailable to the VM, or +/// if the execution of the call fails. /// -/// Hash of a Call block is computed by hashing a concatenation of the function's body hash with -/// zero. +/// The hash of a call block is computed as: +/// +/// > hash(fn_hash || padding, domain=CALL_DOMAIN) +/// > hash(fn_hash || padding, domain=SYSCALL_DOMAIN) # when a syscall is used +/// +/// Where `fn_hash` is 4 field elements (256 bits), and `padding` is 4 ZERO elements (256 bits). #[derive(Clone, Debug)] pub struct Call { hash: Digest, diff --git a/core/src/program/blocks/join_block.rs b/core/src/program/blocks/join_block.rs index 45314638c0..d5ae4ad88b 100644 --- a/core/src/program/blocks/join_block.rs +++ b/core/src/program/blocks/join_block.rs @@ -2,11 +2,15 @@ use super::{fmt, hasher, Box, CodeBlock, Digest, Felt, Operation}; // JOIN BLOCKS // ================================================================================================ -/// A code block used to combine two other code blocks. +/// Block for sequential execution of two sub-blocks. /// -/// When the VM executes a Join block, it executes joined blocks in sequence one after the other. +/// Executes left sub-block then the right sub-block. Fails if either of the sub-block execution fails. /// -/// Hash of a Join block is computed by hashing a concatenation of the hashes of joined blocks. +/// The hash of a join block is computed as: +/// +/// > hash(left_block_hash || right_block_hash, domain=JOIN_DOMAIN) +/// +/// Where `left_block_hash` and `right_block_hash` are 4 field elements (256 bits) each. #[derive(Clone, Debug)] pub struct Join { body: Box<[CodeBlock; 2]>, diff --git a/core/src/program/blocks/loop_block.rs b/core/src/program/blocks/loop_block.rs index 5778901275..74e1cbb032 100644 --- a/core/src/program/blocks/loop_block.rs +++ b/core/src/program/blocks/loop_block.rs @@ -2,16 +2,16 @@ use super::{fmt, hasher, Box, CodeBlock, Digest, Felt, Operation}; // LOOP BLOCK // ================================================================================================ -/// A code block used to describe condition-based iterative execution. +/// Block for a conditional loop. /// -/// When the VM encounters a Loop block, executes the loop's body if the top of the stack is `1`, -/// and skips the block if the top of the stack is `0`. If the top of the stack is neither `0` nor -/// `1`, the program fails. Once the loop body is fully executed, the VM checks the top of the -/// stack again. If the top of the stack is `1`, the loop is executed again, if it is `0`, the VM -/// stops executing the loop and moves to the next block. Thus, the body of the loop is executed -/// while the top of the stack remains `1` at the end of each loop iteration. +/// Executes the loop body while the value on the top of the stack is `1`, stops when `0`. Fails if +/// the top of the stack is neither `1` nor `0`, or if the execution of the body fails. /// -/// Hash of a Loop block is computed by hashing a concatenation of the loop's body hash with zero. +/// The hash of a loop block is: +/// +/// > hash(body_hash || padding, domain=LOOP_DOMAIN) +/// +/// Where `body_hash` is 4 field elements (256 bits), and `padding` is 4 ZERO elements (256 bits). #[derive(Clone, Debug)] pub struct Loop { body: Box, diff --git a/core/src/program/blocks/proxy_block.rs b/core/src/program/blocks/proxy_block.rs index fd6c6e1e98..6d9e04bfa1 100644 --- a/core/src/program/blocks/proxy_block.rs +++ b/core/src/program/blocks/proxy_block.rs @@ -2,10 +2,10 @@ use super::{fmt, Digest}; // PROXY BLOCK // ================================================================================================ -/// A code block used to conceal a part of a program. +/// Block for a unknown function call. /// -/// Proxy blocks cannot be executed by the VM. They are used primarily to verify the integrity of -/// a program's hash while keeping parts of the program secret. +/// Proxy blocks are used to verify the integrity of a program's hash while keeping parts +/// of the program secret. Fails if executed. /// /// Hash of a proxy block is not computed but is rather defined at instantiation time. #[derive(Clone, Debug)] diff --git a/core/src/program/blocks/span_block.rs b/core/src/program/blocks/span_block.rs index 9036063695..0888a5839c 100644 --- a/core/src/program/blocks/span_block.rs +++ b/core/src/program/blocks/span_block.rs @@ -16,25 +16,35 @@ const MAX_OPS_PER_BATCH: usize = GROUP_SIZE * BATCH_SIZE; // SPAN BLOCK // ================================================================================================ -/// A code block used to describe a linear sequence of operations (i.e., no branching or loops). +/// Block for a linear sequence of operations (i.e., no branching or loops). /// -/// When the VM executes a Span block, it breaks the sequence of operations into batches and -/// groups according to the following rules: -/// - A group may contain up to 9 operations or a single immediate value. -/// - An operation carrying an immediate value cannot be in the 9th position in a group. -/// - A batch may contain up to 8 groups. -/// - There is no limit on the number of batches contained within a single span. +/// Executes its operations in order. Fails if any of the operations fails. /// -/// Thus, for example, executing 8 pushes in a row will result in two operation batches: -/// - The first batch will contain 8 groups, with the first group containing 7 push opcodes, -/// and the remaining 7 groups containing immediate values for each of the push operations. -/// - The second batch will contain 2 groups, with the first group containing a single push opcode, -/// and the second group containing the immediate value for the last push operation. +/// A span is composed of operation batches, operation batches are composed of operation groups, +/// operation groups encode the VM's operations and immediate values. These values are created +/// according to these rules: /// -/// If a sequence of operations does not have any operations which carry immediate values, then -/// up to 72 operations can fit into a single batch. +/// - A span contains one or more batches. +/// - A batch contains exactly 8 groups. +/// - A group contains exactly 9 operations or 1 immediate value. +/// - NOOPs are used to fill a group or batch when necessary. +/// - An immediate value follows the operation that requires it, using the next available group in +/// the batch. If there are no batches available in the group, then both the operation and its +/// immediate are moved to the next batch. /// -/// TODO: describe how Span hash is computed. +/// Example: 8 pushes result in two operation batches: +/// +/// - First batch: First group with 7 push opcodes and 2 zero-paddings packed together, followed by +/// 7 groups with their respective immediate values. +/// - Second batch: First group with the last push opcode and 8 zero-paddings packed together, +/// followed by one immediate and 6 padding groups. +/// +/// The hash of a span block is: +/// +/// > hash(batches, domain=SPAN_DOMAIN) +/// +/// Where `batches` is the concatenation of each `batch` in the span, and each batch is 8 field +/// elements (512 bits). #[derive(Clone, Debug)] pub struct Span { op_batches: Vec, diff --git a/core/src/program/blocks/split_block.rs b/core/src/program/blocks/split_block.rs index 158676919a..873f0d7917 100644 --- a/core/src/program/blocks/split_block.rs +++ b/core/src/program/blocks/split_block.rs @@ -2,15 +2,16 @@ use super::{fmt, hasher, Box, CodeBlock, Digest, Felt, Operation}; // SPLIT BLOCK // ================================================================================================ -/// A code block used to describe conditional execution. +/// Block for conditional execution. /// -/// When the VM executes a Split bock, either the true branch or the false branch of the block is -/// executed. Specifically, if the top of the stack is `1`, the true branch is executed, and if -/// the top of the stack is `0`, the false branch is executed. If the top of the stack is neither -/// `0` nor `1`, the program fails. +/// Executes the first branch if the top of the stack is `1` or the second branch if `0`. Fails if +/// the top of the stack is neither `1` or `0` or if the branch execution fails. /// -/// Hash of a Split block is computed by hashing a concatenation of the true and the false branch -/// hashes. +/// The hash of a split block is: +/// +/// > hash(true_branch_hash || false_branch_hash, domain=SPLIT_DOMAIN) +/// +/// Where `true_branch_hash` and `false_branch_hash` are 4 field elements (256 bits) each. #[derive(Clone, Debug)] pub struct Split { branches: Box<[CodeBlock; 2]>, From 2a8a6aaaead3c25ef52b6ed41cb21a5e7b5ce2a0 Mon Sep 17 00:00:00 2001 From: frisitano Date: Fri, 17 Mar 2023 15:00:39 +0400 Subject: [PATCH 37/47] feat: add suppport for libraries in the debugger --- assembly/src/errors.rs | 16 ++++++++++ assembly/src/library.rs | 17 +++++++++++ miden/src/cli/compile.rs | 12 ++++++-- miden/src/cli/data.rs | 62 +++++++++++++++++++++++++++++++++++--- miden/src/cli/debug/mod.rs | 10 ++++-- miden/src/cli/prove.rs | 10 ++++-- miden/src/cli/run.rs | 10 ++++-- 7 files changed, 124 insertions(+), 13 deletions(-) diff --git a/assembly/src/errors.rs b/assembly/src/errors.rs index b40173a600..ce4e3346b7 100644 --- a/assembly/src/errors.rs +++ b/assembly/src/errors.rs @@ -633,9 +633,11 @@ impl From for SerializationError { #[derive(Clone, Debug)] pub enum LibraryError { ModuleNotFound(String), + DeserializationFailed(String, String), DuplicateModulePath(String), DuplicateNamespace(String), EmptyProcedureName, + FileIO(String, String), ProcedureNameWithDelimiter(String), ModulePathStartsWithDelimiter(String), ModulePathEndsWithDelimiter(String), @@ -655,6 +657,14 @@ impl LibraryError { Self::DuplicateNamespace(namespace.to_string()) } + pub fn file_error(path: &str, message: &str) -> Self { + Self::FileIO(path.to_string(), message.to_string()) + } + + pub fn deserialization_error(path: &str, message: &str) -> Self { + Self::DeserializationFailed(path.to_string(), message.to_string()) + } + pub fn procedure_name_with_delimiter(name: &str) -> Self { Self::ProcedureNameWithDelimiter(name.to_string()) } @@ -684,9 +694,15 @@ impl fmt::Display for LibraryError { use LibraryError::*; match self { ModuleNotFound(path) => write!(f, "module '{path}' not found"), + DeserializationFailed(path, message) => { + write!(f, "library deserialization failed - '{path}': {message}") + } DuplicateModulePath(path) => write!(f, "duplciate module path '{path}'"), DuplicateNamespace(namespace) => write!(f, "duplicate namespace '{namespace}'"), EmptyProcedureName => write!(f, "the procedure name cannot be empty"), + FileIO(path, message) => { + write!(f, "file error - '{path}': {message}") + } ProcedureNameWithDelimiter(name) => { write!(f, "'{name}' cannot contain a module delimiter") } diff --git a/assembly/src/library.rs b/assembly/src/library.rs index 71233efd14..ac961fbe27 100644 --- a/assembly/src/library.rs +++ b/assembly/src/library.rs @@ -118,6 +118,23 @@ mod use_std { }) } + /// Read a library from a file. + pub fn read_from_file

(path: P) -> Result + where + P: AsRef, + { + // convert path to str + let path_str = path.as_ref().to_str().unwrap_or("path contains invalid unicode"); + + // read bytes from file + let contents = + fs::read(&path).map_err(|e| LibraryError::file_error(path_str, &e.to_string()))?; + + // read library from bytes + Self::read_from_bytes(&contents) + .map_err(|e| LibraryError::deserialization_error(path_str, &e.to_string())) + } + /// Write the library to a target dir, using its namespace as file name and the appropriate /// extension. pub fn write_to_dir

(&self, dir_path: P) -> io::Result<()> diff --git a/miden/src/cli/compile.rs b/miden/src/cli/compile.rs index 9844f3dc59..8510132b87 100644 --- a/miden/src/cli/compile.rs +++ b/miden/src/cli/compile.rs @@ -1,4 +1,4 @@ -use super::data::ProgramFile; +use super::data::{Debug, Libraries, ProgramFile}; use std::path::PathBuf; use structopt::StructOpt; @@ -8,6 +8,9 @@ pub struct CompileCmd { /// Path to .masm assembly file #[structopt(short = "a", long = "assembly", parse(from_os_str))] assembly_file: PathBuf, + /// Paths to .masl library files + #[structopt(short = "l", long = "libraries", parse(from_os_str))] + library_paths: Vec, } impl CompileCmd { @@ -16,8 +19,11 @@ impl CompileCmd { println!("Compile program"); println!("============================================================"); - // load and compile program file - let program = ProgramFile::read(&self.assembly_file, false)?; + // load libraries from files + let libraries = Libraries::new(&self.library_paths)?; + + // load program from file and compile + let program = ProgramFile::read(&self.assembly_file, &Debug::Off, libraries.libraries)?; // report program hash to user let program_hash: [u8; 32] = program.hash().into(); diff --git a/miden/src/cli/data.rs b/miden/src/cli/data.rs index 7044ecd946..4cd6610c4d 100644 --- a/miden/src/cli/data.rs +++ b/miden/src/cli/data.rs @@ -1,3 +1,4 @@ +use assembly::{Library, MaslLibrary}; use miden::{ utils::{Deserializable, SliceReader}, AdviceInputs, Assembler, Digest, ExecutionProof, MemAdviceProvider, Program, StackInputs, @@ -12,6 +13,22 @@ use std::{ }; use stdlib::StdLibrary; +// HELPERS +// ================================================================================================ + +/// Indicates whether debug mode is on or off. +pub enum Debug { + On, + Off, +} + +impl Debug { + /// Returns true if debug mode is on. + fn is_on(&self) -> bool { + matches!(self, Self::On) + } +} + // INPUT FILE // ================================================================================================ @@ -164,7 +181,11 @@ pub struct ProgramFile; /// Helper methods to interact with masm program file impl ProgramFile { - pub fn read(path: &PathBuf, debug: bool) -> Result { + pub fn read(path: &PathBuf, debug: &Debug, libraries: I) -> Result + where + I: IntoIterator, + L: Library, + { println!("Reading program file `{}`", path.display()); // read program file to string @@ -175,10 +196,16 @@ impl ProgramFile { let now = Instant::now(); // compile program - let program = Assembler::default() + let mut assembler = Assembler::default() + .with_debug_mode(debug.is_on()) .with_library(&StdLibrary::default()) - .map_err(|err| format!("Failed to load stdlib - {}", err))? - .with_debug_mode(debug) + .map_err(|err| format!("Failed to load stdlib - {}", err))?; + + assembler = assembler + .with_libraries(libraries.into_iter()) + .map_err(|err| format!("Failed to load libraries `{}`", err))?; + + let program = assembler .compile(&program_file) .map_err(|err| format!("Failed to compile program - {}", err))?; @@ -270,3 +297,30 @@ impl ProgramHash { Ok(program_hash) } } + +// LIBRARY FILE +// ================================================================================================ +pub struct Libraries { + pub libraries: Vec, +} + +impl Libraries { + /// Creates a new instance of [Libraries] from a list of library paths. + pub fn new(paths: I) -> Result + where + P: AsRef, + I: IntoIterator, + { + let mut libraries = Vec::new(); + + for path in paths { + println!("Reading library file `{}`", path.as_ref().display()); + + let library = MaslLibrary::read_from_file(path) + .map_err(|e| format!("Failed to read library: {e}"))?; + libraries.push(library); + } + + Ok(Self { libraries }) + } +} diff --git a/miden/src/cli/debug/mod.rs b/miden/src/cli/debug/mod.rs index f6edbb09b2..03f2536b4f 100644 --- a/miden/src/cli/debug/mod.rs +++ b/miden/src/cli/debug/mod.rs @@ -1,4 +1,4 @@ -use super::data::{InputFile, ProgramFile}; +use super::data::{Debug, InputFile, Libraries, ProgramFile}; use rustyline::{error::ReadlineError, Config, EditMode, Editor}; use std::path::PathBuf; use structopt::StructOpt; @@ -21,6 +21,9 @@ pub struct DebugCmd { /// Enable vi edit mode #[structopt(short = "vi", long = "vim_edit_mode")] vim_edit_mode: Option, + /// Paths to .masl library files + #[structopt(short = "l", long = "libraries", parse(from_os_str))] + library_paths: Vec, } impl DebugCmd { @@ -29,8 +32,11 @@ impl DebugCmd { println!("Debug program"); println!("============================================================"); + // load libraries from files + let libraries = Libraries::new(&self.library_paths)?; + // load program from file and compile - let program = ProgramFile::read(&self.assembly_file, true)?; + let program = ProgramFile::read(&self.assembly_file, &Debug::On, libraries.libraries)?; let program_hash: [u8; 32] = program.hash().into(); println!("Debugging program with hash {}... ", hex::encode(program_hash)); diff --git a/miden/src/cli/prove.rs b/miden/src/cli/prove.rs index 404ade2e29..faa4f6b40b 100644 --- a/miden/src/cli/prove.rs +++ b/miden/src/cli/prove.rs @@ -1,4 +1,4 @@ -use super::data::{InputFile, OutputFile, ProgramFile, ProofFile}; +use super::data::{Debug, InputFile, Libraries, OutputFile, ProgramFile, ProofFile}; use miden::ProofOptions; use std::{io::Write, path::PathBuf, time::Instant}; use structopt::StructOpt; @@ -25,6 +25,9 @@ pub struct ProveCmd { /// Security level for execution proofs generated by the VM #[structopt(short = "s", long = "security", default_value = "96bits")] security: String, + /// Paths to .masl library files + #[structopt(short = "l", long = "libraries", parse(from_os_str))] + library_paths: Vec, } impl ProveCmd { @@ -47,8 +50,11 @@ impl ProveCmd { .filter_level(log::LevelFilter::Debug) .init(); + // load libraries from files + let libraries = Libraries::new(&self.library_paths)?; + // load program from file and compile - let program = ProgramFile::read(&self.assembly_file, false)?; + let program = ProgramFile::read(&self.assembly_file, &Debug::Off, libraries.libraries)?; // load input data from file let input_data = InputFile::read(&self.input_file, &self.assembly_file)?; diff --git a/miden/src/cli/run.rs b/miden/src/cli/run.rs index 6a92af17e6..e8423c0567 100644 --- a/miden/src/cli/run.rs +++ b/miden/src/cli/run.rs @@ -1,4 +1,4 @@ -use super::data::{InputFile, OutputFile, ProgramFile}; +use super::data::{Debug, InputFile, Libraries, OutputFile, ProgramFile}; use std::{path::PathBuf, time::Instant}; use structopt::StructOpt; @@ -17,6 +17,9 @@ pub struct RunCmd { /// Path to output file #[structopt(short = "o", long = "output", parse(from_os_str))] output_file: Option, + /// Paths to .masl library files + #[structopt(short = "l", long = "libraries", parse(from_os_str))] + library_paths: Vec, } impl RunCmd { @@ -25,8 +28,11 @@ impl RunCmd { println!("Run program"); println!("============================================================"); + // load libraries from files + let libraries = Libraries::new(&self.library_paths)?; + // load program from file and compile - let program = ProgramFile::read(&self.assembly_file, false)?; + let program = ProgramFile::read(&self.assembly_file, &Debug::Off, libraries.libraries)?; // load input data from file let input_data = InputFile::read(&self.input_file, &self.assembly_file)?; From d4554a088416a7478a3a64c5d49adc0a939390da Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Mon, 27 Mar 2023 10:01:45 +0200 Subject: [PATCH 38/47] feat: rename `Read/ReadW` to `AdvPop/AdvPopW` This commit renames the operations `Read` and `ReadW` to `AdvPop` and `AdvPopW`, respectively. This renaming aims to improve the terminology consistency as this operation is referred as pop elsewhere in the documentation. related issue: #774 --- CHANGELOG.md | 3 +++ air/src/stack/op_flags/mod.rs | 16 +++++++------- air/src/stack/op_flags/tests.rs | 2 +- assembly/src/assembler/instruction/adv_ops.rs | 4 ++-- .../src/assembler/instruction/crypto_ops.rs | 2 +- .../src/assembler/instruction/ext2_ops.rs | 8 +++---- assembly/src/assembler/instruction/mod.rs | 2 +- core/src/operations/mod.rs | 18 ++++++++-------- .../stack/io_ops/{READ.png => ADVPOP.png} | Bin .../stack/io_ops/{READW.png => ADVPOPW.png} | Bin docs/src/design/stack/io_ops.md | 20 +++++++++--------- docs/src/design/stack/op_constraints.md | 4 ++-- processor/src/decorators/mod.rs | 8 +++---- processor/src/operations/io_ops.rs | 16 +++++++------- processor/src/operations/mod.rs | 4 ++-- 15 files changed, 55 insertions(+), 52 deletions(-) rename docs/src/assets/design/stack/io_ops/{READ.png => ADVPOP.png} (100%) rename docs/src/assets/design/stack/io_ops/{READW.png => ADVPOPW.png} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98072c0df2..feb3c7cd56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ - Added new instructions: `is_odd`, `assert_eqw`, `mtree_merge`. - [BREAKING] Removed `mtree_cwm` instruction. +#### VM Internals +- [BREAKING] Renamed `Read/ReadW` operations into `AdvPop/AdvPopW`. + ## 0.4.0 (2023-02-27) #### Advice provider diff --git a/air/src/stack/op_flags/mod.rs b/air/src/stack/op_flags/mod.rs index a7ba7b7214..af269169d6 100644 --- a/air/src/stack/op_flags/mod.rs +++ b/air/src/stack/op_flags/mod.rs @@ -179,7 +179,7 @@ impl OpFlags { let mov7_flag = degree7_op_flags[22]; let mov8_flag = degree7_op_flags[26]; let swapwx_flag = degree7_op_flags[28]; - let readw_expacc = degree7_op_flags[14]; + let adv_popw_expacc = degree7_op_flags[14]; // adding the least significant bit. for i in (0..64).step_by(2) { @@ -247,7 +247,7 @@ impl OpFlags { no_shift_flags[3] = no_shift_flags[2] + mov2_flag; no_shift_flags[4] = no_shift_flags[3] + mov3_flag - + readw_expacc + + adv_popw_expacc + swapwx_flag + ext2mul_flag + degree4_op_flags[0]; @@ -460,10 +460,10 @@ impl OpFlags { self.degree7_op_flags[get_op_index(Operation::MovDn3.op_code())] } - /// Operation Flag of READW operation. + /// Operation Flag of ADVPOPW operation. #[inline(always)] - pub fn readw(&self) -> E { - self.degree7_op_flags[get_op_index(Operation::ReadW.op_code())] + pub fn advpopw(&self) -> E { + self.degree7_op_flags[get_op_index(Operation::AdvPopW.op_code())] } /// Operation Flag of EXPACC operation. @@ -734,10 +734,10 @@ impl OpFlags { self.degree7_op_flags[get_op_index(Operation::Dup15.op_code())] } - /// Operation Flag of READ operation. + /// Operation Flag of ADVPOP operation. #[inline(always)] - pub fn read(&self) -> E { - self.degree7_op_flags[get_op_index(Operation::Read.op_code())] + pub fn advpop(&self) -> E { + self.degree7_op_flags[get_op_index(Operation::AdvPop.op_code())] } /// Operation Flag of SDEPTH operation. diff --git a/air/src/stack/op_flags/tests.rs b/air/src/stack/op_flags/tests.rs index 51474b81f9..d3093a474d 100644 --- a/air/src/stack/op_flags/tests.rs +++ b/air/src/stack/op_flags/tests.rs @@ -219,7 +219,7 @@ fn composite_flags() { // ------ no change 4 --------------------------------------------------------------------- - let op_no_change_4 = [Operation::MrUpdate, Operation::ReadW, Operation::Ext2Mul]; + let op_no_change_4 = [Operation::MrUpdate, Operation::AdvPopW, Operation::Ext2Mul]; for op in op_no_change_4 { // frame initialised with an op operation. let frame = generate_evaluation_frame(op.op_code().into()); diff --git a/assembly/src/assembler/instruction/adv_ops.rs b/assembly/src/assembler/instruction/adv_ops.rs index 05eecc6084..03fb768c1b 100644 --- a/assembly/src/assembler/instruction/adv_ops.rs +++ b/assembly/src/assembler/instruction/adv_ops.rs @@ -5,7 +5,7 @@ use vm_core::{code_blocks::CodeBlock, Operation::*}; // NON-DETERMINISTIC (ADVICE) INPUTS // ================================================================================================ -/// Appends the number of READ operations specified by the instruction's immediate value to the +/// Appends the number of ADVPOP operations specified by the instruction's immediate value to the /// span. This pops the specified number of elements from the advice stack and pushes them onto the /// operand stack. /// @@ -14,7 +14,7 @@ use vm_core::{code_blocks::CodeBlock, Operation::*}; /// than 16. pub fn adv_push(span: &mut SpanBuilder, n: u8) -> Result, AssemblyError> { validate_param(n, 1..=ADVICE_READ_LIMIT)?; - span.push_op_many(Read, n as usize); + span.push_op_many(AdvPop, n as usize); Ok(None) } diff --git a/assembly/src/assembler/instruction/crypto_ops.rs b/assembly/src/assembler/instruction/crypto_ops.rs index 9a076f74c1..bd6d920f25 100644 --- a/assembly/src/assembler/instruction/crypto_ops.rs +++ b/assembly/src/assembler/instruction/crypto_ops.rs @@ -205,7 +205,7 @@ fn read_mtree_node(span: &mut SpanBuilder) { // pops the old node value from advice the stack => MPVERIFY: [V_old, d, i, R, ...] // MRUPDATE: [V_old, d, i, R, V_new, ...] - span.push_op_many(Read, 4); + span.push_op_many(AdvPop, 4); } /// Update a node in the merkle tree. This operation will always copy the tree into a new instance, diff --git a/assembly/src/assembler/instruction/ext2_ops.rs b/assembly/src/assembler/instruction/ext2_ops.rs index 1349467629..d61d15ff72 100644 --- a/assembly/src/assembler/instruction/ext2_ops.rs +++ b/assembly/src/assembler/instruction/ext2_ops.rs @@ -55,8 +55,8 @@ pub fn ext2_div(span: &mut SpanBuilder) -> Result, AssemblyErr span.add_decorator(Decorator::Advice(Ext2Inv))?; #[rustfmt::skip] let ops = [ - Read, // [b0', b1, b0, a1, a0, ...] - Read, // [b1', b0', b1, b0, a1, a0, ...] + AdvPop, // [b0', b1, b0, a1, a0, ...] + AdvPop, // [b1', b0', b1, b0, a1, a0, ...] Ext2Mul, // [b1', b0', 0, 1, a1, a0, ...] MovUp2, // [0, b1', b0', 1, a1, a0, ...] Eqz, // [1, b1', b0', 1, a1, a0, ...] @@ -115,8 +115,8 @@ pub fn ext2_inv(span: &mut SpanBuilder) -> Result, AssemblyErr span.add_decorator(Decorator::Advice(Ext2Inv))?; #[rustfmt::skip] let ops = [ - Read, // [a0', a1, a0, ...] - Read, // [a1', a0', a1, a0, ...] + AdvPop, // [a0', a1, a0, ...] + AdvPop, // [a1', a0', a1, a0, ...] Ext2Mul, // [a1', a0', 0, 1, ...] MovUp2, // [0, a1', a0', 1, ...] Eqz, // [1, a1', a0', 1, ...] diff --git a/assembly/src/assembler/instruction/mod.rs b/assembly/src/assembler/instruction/mod.rs index 9df041edf3..b908897bb9 100644 --- a/assembly/src/assembler/instruction/mod.rs +++ b/assembly/src/assembler/instruction/mod.rs @@ -267,7 +267,7 @@ impl Assembler { Instruction::Clk => span.add_op(Clk), Instruction::AdvPipe => span.add_ops([Pipe, HPerm]), Instruction::AdvPush(n) => adv_ops::adv_push(span, *n), - Instruction::AdvLoadW => span.add_op(ReadW), + Instruction::AdvLoadW => span.add_op(AdvPopW), Instruction::MemStream => span.add_ops([MStream, HPerm]), diff --git a/core/src/operations/mod.rs b/core/src/operations/mod.rs index 8181886968..ebbcef6f9f 100644 --- a/core/src/operations/mod.rs +++ b/core/src/operations/mod.rs @@ -312,12 +312,12 @@ pub enum Operation { /// Pushes the immediate value onto the stack. Push(Felt), - /// Removes the next element from the advice stack and pushes it onto the stack. - Read, + /// Removes the next element from the advice stack and pushes it onto the operand stack. + AdvPop, - /// Removes a word (4 elements) from the advice stack and overwrites the top four stack - /// elements with it. - ReadW, + /// Removes a word (4 elements) from the advice stack and overwrites the top four operand + /// stack elements with it. + AdvPopW, /// Pops an element off the stack, interprets it as a memory address, and replaces the /// remaining 4 elements at the top of the stack with values located at the specified address. @@ -439,7 +439,7 @@ impl Operation { Self::MovDn2 => 0b0000_1011, Self::MovUp3 => 0b0000_1100, Self::MovDn3 => 0b0000_1101, - Self::ReadW => 0b0000_1110, + Self::AdvPopW => 0b0000_1110, Self::Expacc => 0b0000_1111, Self::MovUp4 => 0b0001_0000, @@ -489,7 +489,7 @@ impl Operation { Self::Dup11 => 0b0011_1010, Self::Dup13 => 0b0011_1011, Self::Dup15 => 0b0011_1100, - Self::Read => 0b0011_1101, + Self::AdvPop => 0b0011_1101, Self::SDepth => 0b0011_1110, Self::Clk => 0b0011_1111, @@ -652,8 +652,8 @@ impl fmt::Display for Operation { // ----- input / output --------------------------------------------------------------- Self::Push(value) => write!(f, "push({value})"), - Self::Read => write!(f, "read"), - Self::ReadW => write!(f, "readw"), + Self::AdvPop => write!(f, "advpop"), + Self::AdvPopW => write!(f, "advpopw"), Self::MLoadW => write!(f, "mloadw"), Self::MStoreW => write!(f, "mstorew"), diff --git a/docs/src/assets/design/stack/io_ops/READ.png b/docs/src/assets/design/stack/io_ops/ADVPOP.png similarity index 100% rename from docs/src/assets/design/stack/io_ops/READ.png rename to docs/src/assets/design/stack/io_ops/ADVPOP.png diff --git a/docs/src/assets/design/stack/io_ops/READW.png b/docs/src/assets/design/stack/io_ops/ADVPOPW.png similarity index 100% rename from docs/src/assets/design/stack/io_ops/READW.png rename to docs/src/assets/design/stack/io_ops/ADVPOPW.png diff --git a/docs/src/design/stack/io_ops.md b/docs/src/design/stack/io_ops.md index a27314509c..4839bf184b 100644 --- a/docs/src/design/stack/io_ops.md +++ b/docs/src/design/stack/io_ops.md @@ -21,24 +21,24 @@ $$ The effect of this operation on the rest of the stack is: * **Right shift** starting from position $0$. -### READ -Assume $a$ is an element at the head of the advice stack. The `READ` operation removes $a$ from the advice stack and pushes it onto the stack. The diagram below illustrates this graphically. +### ADVPOP +Assume $a$ is an element at the top of the advice stack. The `ADVPOP` operation removes $a$ from the advice stack and pushes it onto the operand stack. The diagram below illustrates this graphically. -![read](../../assets/design/stack/io_ops/READ.png) +![advpop](../../assets/design/stack/io_ops/ADVPOP.png) -The `READ` operation does not impose any constraints against the first element of the stack. +The `ADVPOP` operation does not impose any constraints against the first element of the operand stack. -The effect of this operation on the rest of the stack is: +The effect of this operation on the rest of the operand stack is: * **Right shift** starting from position $0$. -### READW -Assume $a$, $b$, $c$, and $d$, are the elements at the head of the advice stack (with $a$ being on top). The `READW` operation removes these elements from the advice stack and puts them onto the stack by overwriting the top $4$ stack elements. The diagram below illustrates this graphically. +### ADVPOPW +Assume $a$, $b$, $c$, and $d$, are the elements at the top of the advice stack (with $a$ being on top). The `ADVPOPW` operation removes these elements from the advice stack and puts them onto the operand stack by overwriting the top $4$ stack elements. The diagram below illustrates this graphically. -![readw](../../assets/design/stack/io_ops/READW.png) +![advpopw](../../assets/design/stack/io_ops/ADVPOPW.png) -The `READW` operation does not impose any constraints against the top $4$ elements of the stack. +The `ADVPOPW` operation does not impose any constraints against the top $4$ elements of the operand stack. -The effect of this operation on the rest of the stack is: +The effect of this operation on the rest of the operand stack is: * **No change** starting from position $4$. ## Memory access operations diff --git a/docs/src/design/stack/op_constraints.md b/docs/src/design/stack/op_constraints.md index e3c2a0d80b..7ffa97d1f4 100644 --- a/docs/src/design/stack/op_constraints.md +++ b/docs/src/design/stack/op_constraints.md @@ -79,7 +79,7 @@ This group contains $32$ operations which do not shift the stack (this is almost | `MOVDN2` | $11$ | `000_1011` | [Stack ops](./stack_ops.md) | $7$ | | `MOVUP3` | $12$ | `000_1100` | [Stack ops](./stack_ops.md) | $7$ | | `MOVDN3` | $13$ | `000_1101` | [Stack ops](./stack_ops.md) | $7$ | -| `READW` | $14$ | `000_1110` | [I/O ops](./io_ops.md) | $7$ | +| `ADVPOPW` | $14$ | `000_1110` | [I/O ops](./io_ops.md) | $7$ | | `EXPACC` | $15$ | `000_1111` | [Field ops](./field_ops.md) | $7$ | | `MOVUP4` | $16$ | `001_0000` | [Stack ops](./stack_ops.md) | $7$ | | `MOVDN4` | $17$ | `001_0001` | [Stack ops](./stack_ops.md) | $7$ | @@ -138,7 +138,7 @@ This group contains $16$ operations which shift the stack to the right (i.e., pu | `DUP11` | $58$ | `011_1010` | [Stack ops](./stack_ops.md) | $7$ | | `DUP13` | $59$ | `011_1011` | [Stack ops](./stack_ops.md) | $7$ | | `DUP15` | $60$ | `011_1100` | [Stack ops](./stack_ops.md) | $7$ | -| `READ` | $61$ | `011_1101` | [Stack ops](./stack_ops.md) | $7$ | +| `ADVPOP` | $61$ | `011_1101` | [Stack ops](./io_ops.md) | $7$ | | `SDEPTH` | $62$ | `011_1110` | [I/O ops](./io_ops.md) | $7$ | | `CLK` | $63$ | `011_1111` | [System ops](./system_ops.md) | $7$ | diff --git a/processor/src/decorators/mod.rs b/processor/src/decorators/mod.rs index 46468bc9d8..18bde219f1 100644 --- a/processor/src/decorators/mod.rs +++ b/processor/src/decorators/mod.rs @@ -330,10 +330,10 @@ mod tests { .unwrap(); // pop the node from the advice stack and push it onto the operand stack - process.execute_op(Operation::Read).unwrap(); - process.execute_op(Operation::Read).unwrap(); - process.execute_op(Operation::Read).unwrap(); - process.execute_op(Operation::Read).unwrap(); + process.execute_op(Operation::AdvPop).unwrap(); + process.execute_op(Operation::AdvPop).unwrap(); + process.execute_op(Operation::AdvPop).unwrap(); + process.execute_op(Operation::AdvPop).unwrap(); let expected_stack = build_expected(&[ leaves[1][3], diff --git a/processor/src/operations/io_ops.rs b/processor/src/operations/io_ops.rs index 92ac6e9ad0..b6db128569 100644 --- a/processor/src/operations/io_ops.rs +++ b/processor/src/operations/io_ops.rs @@ -229,7 +229,7 @@ where /// /// # Errors /// Returns an error if the advice stack is empty. - pub(super) fn op_read(&mut self) -> Result<(), ExecutionError> { + pub(super) fn op_advpop(&mut self) -> Result<(), ExecutionError> { let value = self.advice_provider.pop_stack()?; self.stack.set(0, value); self.stack.shift_right(0); @@ -241,7 +241,7 @@ where /// /// # Errors /// Returns an error if the advice stack contains fewer than four elements. - pub(super) fn op_readw(&mut self) -> Result<(), ExecutionError> { + pub(super) fn op_advpopw(&mut self) -> Result<(), ExecutionError> { let word = self.advice_provider.pop_stack_word()?; self.stack.set(0, word[3]); @@ -522,20 +522,20 @@ mod tests { // -------------------------------------------------------------------------------------------- #[test] - fn op_read() { + fn op_advpop() { // popping from the advice stack should push the value onto the operand stack let mut process = Process::new_dummy_with_advice_stack(&[3]); process.execute_op(Operation::Push(ONE)).unwrap(); - process.execute_op(Operation::Read).unwrap(); + process.execute_op(Operation::AdvPop).unwrap(); let expected = build_expected_stack(&[3, 1]); assert_eq!(expected, process.stack.trace_state()); - // reading again should result in an error because advice stack is empty - assert!(process.execute_op(Operation::Read).is_err()); + // popping again should result in an error because advice stack is empty + assert!(process.execute_op(Operation::AdvPop).is_err()); } #[test] - fn op_readw() { + fn op_advpopw() { // popping a word from the advice stack should overwrite top 4 elements of the operand // stack let mut process = Process::new_dummy_with_advice_stack(&[3, 4, 5, 6]); @@ -544,7 +544,7 @@ mod tests { process.execute_op(Operation::Pad).unwrap(); process.execute_op(Operation::Pad).unwrap(); process.execute_op(Operation::Pad).unwrap(); - process.execute_op(Operation::ReadW).unwrap(); + process.execute_op(Operation::AdvPopW).unwrap(); let expected = build_expected_stack(&[6, 5, 4, 3, 1]); assert_eq!(expected, process.stack.trace_state()); } diff --git a/processor/src/operations/mod.rs b/processor/src/operations/mod.rs index f55607be8e..f4fe6e95a7 100644 --- a/processor/src/operations/mod.rs +++ b/processor/src/operations/mod.rs @@ -130,8 +130,8 @@ where // ----- input / output --------------------------------------------------------------- Operation::Push(value) => self.op_push(value)?, - Operation::Read => self.op_read()?, - Operation::ReadW => self.op_readw()?, + Operation::AdvPop => self.op_advpop()?, + Operation::AdvPopW => self.op_advpopw()?, Operation::MLoadW => self.op_mloadw()?, Operation::MStoreW => self.op_mstorew()?, From b815bd2497bf0cfc684afe03a36d7a8d33f796aa Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Tue, 28 Mar 2023 12:51:18 +0200 Subject: [PATCH 39/47] docs: explicitly state u32 are two-complement --- docs/src/user_docs/assembly/u32_operations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/user_docs/assembly/u32_operations.md b/docs/src/user_docs/assembly/u32_operations.md index a2fecd88e6..2e0a886e56 100644 --- a/docs/src/user_docs/assembly/u32_operations.md +++ b/docs/src/user_docs/assembly/u32_operations.md @@ -1,5 +1,5 @@ ## u32 operations -Miden assembly provides a set of instructions which can perform operations on regular 32-bit integers. These instructions are described in the tables below. +Miden assembly provides a set of instructions which can perform operations on regular two-complement 32-bit integers. These instructions are described in the tables below. Most instructions have _checked_ variants. These variants ensure that input values are 32-bit integers, and fail if that's not the case. All other variants do not perform these checks, and thus, should be used only if the inputs are known to be 32-bit integers. Supplying inputs which are greater than or equal to $2^{32}$ to unchecked operations results in undefined behavior. From 530c36011e1267559dc5315ed9a236f5bb9c3c08 Mon Sep 17 00:00:00 2001 From: Al-Kindi-0 <82364884+Al-Kindi-0@users.noreply.github.com> Date: Fri, 24 Mar 2023 12:50:44 +0100 Subject: [PATCH 40/47] feat: update FRI to work with STARK verifier fix remainder errors --- .../integration/stdlib/crypto/fri/channel.rs | 19 +- .../integration/stdlib/crypto/fri/mod.rs | 22 +- .../stdlib/crypto/fri/verifier_fri_e2f4.rs | 80 +++---- stdlib/asm/crypto/fri/frie2f4.masm | 212 +++++++----------- stdlib/docs/frie2f4_fri.md | 4 +- 5 files changed, 138 insertions(+), 199 deletions(-) diff --git a/miden/tests/integration/stdlib/crypto/fri/channel.rs b/miden/tests/integration/stdlib/crypto/fri/channel.rs index 5642bbb424..1859202297 100644 --- a/miden/tests/integration/stdlib/crypto/fri/channel.rs +++ b/miden/tests/integration/stdlib/crypto/fri/channel.rs @@ -1,4 +1,5 @@ -use vm_core::{crypto::merkle::MerklePathSet, Felt, FieldElement}; +use miden::math::fft; +use vm_core::{crypto::merkle::MerklePathSet, Felt, FieldElement, StarkField}; use winter_fri::{FriProof, VerifierError}; use winterfell::{ @@ -67,11 +68,21 @@ where pub fn read_remainder( &mut self, - _commitment: &::Digest, + expected_commitment: &::Digest, ) -> Result, VerifierError> { - let remainder = self.take_fri_remainder(); + let poly = self.take_fri_remainder(); + let commitment = H::hash_elements(&poly); + assert_eq!(&commitment, expected_commitment); + + // Compute remainder codeword corresponding to remainder polynomial + let twiddles = fft::get_twiddles(poly.len()); + let remainder = fft::evaluate_poly_with_offset( + &poly, + &twiddles, + ::GENERATOR, + 8, + ); - // TODO: Verifiy commitment in the case where the remainder codeword is sent Ok(remainder) } } diff --git a/miden/tests/integration/stdlib/crypto/fri/mod.rs b/miden/tests/integration/stdlib/crypto/fri/mod.rs index 523ae64795..68d7ec5b3e 100644 --- a/miden/tests/integration/stdlib/crypto/fri/mod.rs +++ b/miden/tests/integration/stdlib/crypto/fri/mod.rs @@ -15,7 +15,6 @@ pub use verifier_fri_e2f4::*; mod remainder; #[test] -#[ignore = "enable after new remainder verification is implemented"] fn fri_fold4_ext2_remainder32() { let source = " use.std::crypto::fri::frie2f4 @@ -31,14 +30,13 @@ fn fri_fold4_ext2_remainder32() { let depth = trace_len_e + blowup_exp; let domain_size = 1 << depth; - let (advice_provider, advice_stack, position_eval, alphas, commitments, remainder, num_queries) = + let (advice_provider, position_eval, alphas, commitments, remainder, num_queries) = fri_prove_verify_fold4_ext2(trace_len_e).expect("should not panic"); let advice_stack = prepare_advice( depth, domain_size, num_queries, - advice_stack, position_eval, alphas, commitments, @@ -57,7 +55,6 @@ fn fri_fold4_ext2_remainder32() { } #[test] -#[ignore = "enable after new remainder verification is implemented"] fn fri_fold4_ext2_remainder64() { let source = " use.std::crypto::fri::frie2f4 @@ -73,14 +70,13 @@ fn fri_fold4_ext2_remainder64() { let depth = trace_len_e + blowup_exp; let domain_size = 1 << depth; - let (advice_provider, advice_stack, position_eval, alphas, commitments, remainder, num_queries) = + let (advice_provider, position_eval, alphas, commitments, remainder, num_queries) = fri_prove_verify_fold4_ext2(trace_len_e).expect("should not panic"); let advice_stack = prepare_advice( depth, domain_size, num_queries, - advice_stack, position_eval, alphas, commitments, @@ -102,7 +98,6 @@ fn prepare_advice( depth: usize, domain_size: u32, num_queries: usize, - stack_pre: Vec, position_eval: Vec, alphas: Vec, com: Vec, @@ -112,6 +107,10 @@ fn prepare_advice( let remainder_length = remainder.len() / 2; let num_layers = (com.len() / 4) - 1; + stack.push(num_queries as u64); + + stack.extend_from_slice(&position_eval[..]); + stack.push(num_layers as u64); let mut current_domain_size = domain_size as u64; @@ -122,13 +121,12 @@ fn prepare_advice( stack.extend_from_slice(&com[(4 * i)..(4 * i + 4)]); stack.extend_from_slice(&alphas[(4 * i)..(4 * i + 2)]); - // TODO: explain why "-2" for depth; related to folding factor? + // - 2 is due to the fact that we are folding by 4 stack.extend_from_slice(&vec![current_depth - 2, current_domain_size]); current_depth -= 2; } stack.push(remainder_length as u64 / 2); - for i in 0..remainder_length / 2 { let mut remainder_4 = vec![0; 4]; remainder_4[0] = remainder[4 * i + 0]; @@ -139,11 +137,5 @@ fn prepare_advice( stack.extend_from_slice(&remainder_4); } - stack.push(num_queries as u64); - - stack.extend_from_slice(&position_eval[..]); - - stack.extend_from_slice(&stack_pre[..]); - stack } diff --git a/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs b/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs index aeeda90cc2..991e352529 100644 --- a/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs +++ b/miden/tests/integration/stdlib/crypto/fri/verifier_fri_e2f4.rs @@ -28,16 +28,14 @@ type QuadExt = QuadExtension; // merkle_sets contains the Merkle authentication paths used to authenticate the queries. // advice_maps is used to unhash Merkle nodes to a sequence of field elements representing // the query-values. TODO: Make use of the advice_maps. -// 2) `advice_stack: Vec` is how the query values are provided in order to unhash them. -// This should be replaced with the use of advice_maps. -// 3) `positions: Vec` a vector of consecutive quadruples of the form (0, p, e1, e0) +// 2) `positions: Vec` a vector of consecutive quadruples of the form (poe, p, e1, e0) // where p is index of the query at the first layer and (e1, e0) is its corresponding -// evaluation. -// 4) `alphas: Vec` is a vector of tuples representing the folding challenges. -// 5) `commitments: Vec` is a vector of consecutive quadruples (c3, c2, c1, c0) +// evaluation and poe is g^p with g being the initial domain generator. +// 3) `alphas: Vec` is a vector of tuples representing the folding challenges. +// 4) `commitments: Vec` is a vector of consecutive quadruples (c3, c2, c1, c0) // representing the Merkle tree layer commitments. -// 6) `remainder: Vec` is the remainder codeword as consecutive (r0, r1). -// 7) `num_queries: usize` is the number of queries contained in the current FRI proof. +// 5) `remainder: Vec` is the remainder codeword as consecutive (r0, r1). +// 6) `num_queries: usize` is the number of queries contained in the current FRI proof. pub fn fri_prove_verify_fold4_ext2( trace_length_e: usize, ) -> Result< @@ -47,12 +45,11 @@ pub fn fri_prove_verify_fold4_ext2( Vec, Vec, Vec, - Vec, usize, ), VerifierError, > { - let max_remainder_size_e = 6; + let max_remainder_size_e = 3; let folding_factor_e = 2; let trace_length = 1 << trace_length_e; let lde_blowup = 1 << 3; @@ -90,7 +87,10 @@ pub fn fri_prove_verify_fold4_ext2( .flatten() .collect(); - let remainder: Vec = proof.parse_remainder().expect("should return remainder"); + let remainder_poly: Vec = + proof.parse_remainder().expect("should return remainder polynomial"); + let twiddles = fft::get_twiddles(remainder_poly.len()); + let remainder = fft::evaluate_poly_with_offset(&remainder_poly, &twiddles, Felt::GENERATOR, 8); let remainder: Vec = QuadExt::slice_as_base_elements(&remainder[..]) .to_owned() @@ -99,10 +99,9 @@ pub fn fri_prove_verify_fold4_ext2( .collect(); match result { - Ok(((merkle_path_set, advice_values), full_stack, all_position_evaluation, all_alphas)) => { + Ok(((merkle_path_set, advice_values), all_position_evaluation, all_alphas)) => { return Ok(( (merkle_path_set, advice_values), - full_stack, all_position_evaluation, all_alphas, commitments, @@ -146,10 +145,7 @@ fn verify_proof( domain_size: usize, positions: &[usize], options: &FriOptions, -) -> Result< - ((Vec, Vec<([u8; 32], Vec)>), Vec, Vec, Vec), - VerifierError, -> { +) -> Result<((Vec, Vec<([u8; 32], Vec)>), Vec, Vec), VerifierError> { let mut channel = MidenFriVerifierChannel::::new( proof, commitments.clone(), @@ -246,10 +242,8 @@ impl FriVerifierFold4Ext2 { channel: &mut MidenFriVerifierChannel, evaluations: &[QuadExt], positions: &[usize], - ) -> Result< - ((Vec, Vec<([u8; 32], Vec)>), Vec, Vec, Vec), - VerifierError, - > { + ) -> Result<((Vec, Vec<([u8; 32], Vec)>), Vec, Vec), VerifierError> + { // 1 ----- verify the recursive components of the FRI proof ----------------------------------- let positions = positions.to_vec(); let evaluations = evaluations.to_vec(); @@ -258,23 +252,20 @@ impl FriVerifierFold4Ext2 { channel.unbatch::<4, 3>(&positions, self.domain_size(), self.layer_commitments.clone()); let mut d_generator; - let mut full_stack = vec![]; let mut all_alphas = vec![]; let mut all_position_evaluation = vec![]; for (index, &position) in positions.iter().enumerate() { d_generator = self.domain_generator; - let (cur_pos, evaluation, partial_stack, position_evaluation, alphas) = - iterate_query_fold_4_quad_ext( - &self.layer_alphas, - &advice_provider.0, - &advice_provider.1, - position, - self.options.num_fri_layers(self.domain_size()), - self.domain_size(), - &evaluations[index], - &mut d_generator, - )?; - full_stack.extend_from_slice(&partial_stack[..]); + let (cur_pos, evaluation, position_evaluation, alphas) = iterate_query_fold_4_quad_ext( + &self.layer_alphas, + &advice_provider.0, + &advice_provider.1, + position, + self.options.num_fri_layers(self.domain_size()), + self.domain_size(), + &evaluations[index], + &mut d_generator, + )?; all_position_evaluation.extend_from_slice(&position_evaluation[..]); all_alphas = alphas; @@ -293,7 +284,7 @@ impl FriVerifierFold4Ext2 { } } - Ok((advice_provider, full_stack, all_position_evaluation, all_alphas)) + Ok((advice_provider, all_position_evaluation, all_alphas)) } } @@ -306,7 +297,7 @@ fn iterate_query_fold_4_quad_ext( initial_domain_size: usize, evaluation: &QuadExt, domain_generator: &mut Felt, -) -> Result<(usize, QuadExtension, Vec, Vec, Vec), VerifierError> { +) -> Result<(usize, QuadExtension, Vec, Vec), VerifierError> { let mut cur_pos = position; let mut evaluation = *evaluation; let mut domain_size = initial_domain_size; @@ -319,8 +310,8 @@ fn iterate_query_fold_4_quad_ext( let arr = vec![evaluation]; let a = QuadExt::slice_as_base_elements(&arr); - let mut partial_tap = vec![]; - let position_evaluation = vec![a[0].as_int(), a[1].as_int(), (position as u64).into(), 0]; + let position_evaluation = + vec![a[0].as_int(), a[1].as_int(), (position as u64).into(), init_exp.as_int()]; let mut alphas = vec![]; for depth in 0..number_of_layers { @@ -342,17 +333,6 @@ fn iterate_query_fold_4_quad_ext( .next() .expect("must contain the leaf values") .1; - let tmp_query = vec![ - query_values[0].as_int(), - query_values[1].as_int(), - query_values[2].as_int(), - query_values[3].as_int(), - query_values[4].as_int(), - query_values[5].as_int(), - query_values[6].as_int(), - query_values[7].as_int(), - ]; - partial_tap.extend(&tmp_query); let query_values = [ QuadExt::new(query_values[0], query_values[1]), @@ -405,7 +385,7 @@ fn iterate_query_fold_4_quad_ext( domain_size /= 4; } - Ok((cur_pos, evaluation, partial_tap, position_evaluation, alphas)) + Ok((cur_pos, evaluation, position_evaluation, alphas)) } impl UnBatch for MidenFriVerifierChannel { diff --git a/stdlib/asm/crypto/fri/frie2f4.masm b/stdlib/asm/crypto/fri/frie2f4.masm index 996b4ed930..95700b3972 100644 --- a/stdlib/asm/crypto/fri/frie2f4.masm +++ b/stdlib/asm/crypto/fri/frie2f4.masm @@ -1,102 +1,89 @@ use.std::crypto::fri::ext2fri -#! Stores the layer commitments C followed by [d_size, t_depth, a1, a0] and [0, p, e1, e0] where: +#! Stores the layer commitments C followed by [d_size, t_depth, a1, a0] and [poe, p, e1, e0] where: #! 1) d_size is the domain size divided by 4 of the domain corresponding to C. #! 2) t_depth is the tree depth of the Merkle tree with commitment C. #! 3) (a0, a1) is the folding challenge to create the next layer. -#! 4) p is the query index and (e0, e1) is the evaluation at the first layer. -#! TODO: This processing function should in fact compute d_size and t_depth for each C +#! 4) p is the query index and (e0, e1) is the evaluation at the first layer and poe is g^p with +#! g being the initial domain generator. +#! TODO: This pre-processing function should in fact compute d_size and t_depth for each C #! starting from the original domain size. export.preprocess.4 - #adv_push.1 locaddr.3 - adv_push.1 #[num_layers, layer_ptr, g, ..] - dup - neq.0 + adv_push.1 #[num_queries, query_ptr, g, ..] + sub.1 + push.0.0.0.0 + push.1 while.true - push.0.0.0.0 - adv_loadw #[C, num_layers, ptr, ..] - movup.5 #[ptr, C, num_layers, ..] - u32wrapping_add.1 #[ptr+1, C, num_layers, ..] - dup movdn.5 #[ptr+1, C, ptr+1, num_layers, ..] - mem_storew #[C, ptr+1, num_layers, ..] - adv_loadw #[d_size, t_depth, a1, a0, ptr+1, num_layers, ..] - movup.4 #[ptr+1, d_size, t_depth, a1, a0, num_layers, ..] - u32wrapping_add.1 #[ptr+2, d_size, t_depth, a1, a0, num_layers, ..] - dup movdn.6 #[ptr+2, d_size, t_depth, a1, a0, num_layers, ptr+2, ..] - mem_storew dropw #[num_layers, ptr+2, ..] - u32wrapping_sub.1 dup #[num_layers-1, num_layers-1, ptr+2, ..] - neq.0 #[?, num_layers-1, ptr+2, ..] + adv_loadw #[Q, num_queries, ptr, ..] + dup.5 #[ptr, Q, num_queries, ptr,..] + u32wrapping_add.1 #[ptr+1, Q, num_queries, ptr, ..] + swap.6 #[ptr, Q, num_queries, ptr+1, ..] + mem_storew #[Q, num_queries, ptr+1, ..] + dup.4 + sub.1 #[num_queries-1, Q, num_queries, ptr+1, ..] + swap.5 #[num_queries, Q, num_queries-1, ptr+1, ..] + neq.0 #[?, Q, num_queries-1, ptr+1, ..] end + #=> [X, x, layer_ptr, g] - #[ptr2n', rem_len, rem_ptr, g, ..] drop - adv_push.1 - dup.1 - add.1 - swap.2 + #=> [X, layer_ptr, g] + + dup.4 + movdn.5 + #=> [X, layer_ptr, layer_ptr, g] + adv_push.1 + mul.2 + sub.1 + movdn.4 + #=> [X, layer_ptr, num_layers, layer_ptr, g] - # value of the remainder codeword - push.0.0.0.0 push.1 while.true - adv_loadw - movup.4 - add.1 - dup - movdn.5 - mem_storew - movup.5 - u32wrapping_sub.1 - movdn.5 - dup.5 - neq.0 + adv_loadw + dup.5 + u32wrapping_add.1 + swap.6 + mem_storew + dup.4 + sub.1 + swap.5 + neq.0 end + #=> [X, x, remainder_ptr, layer_ptr, g] - # [ptr_(2n+rem_len/2)', rem_ptr, g] - dropw - swap drop + #=> [X, remainder_ptr, layer_ptr, g] + + dup.4 + movdn.5 + #=> [X, remainder_ptr, remainder_ptr, layer_ptr, g] - # number of queries adv_push.1 + sub.1 + movdn.4 + #=> [X, remainder_ptr, len_remainder/2, remainder_ptr, layer_ptr, g] - # [ptr_(2n+rem_len/2)', num_q, query_start_ptr, rem_ptr, g] - dup.1 - add.1 - swap.2 - - # value of the queries and evaluations - push.0.0.0.0 push.1 while.true - adv_loadw - movup.4 - add.1 - dup - movdn.5 - mem_storew - movup.5 - u32wrapping_sub.1 - movdn.5 - dup.5 - neq.0 + adv_loadw + dup.5 + u32wrapping_add.1 + swap.6 + mem_storew + dup.4 + sub.1 + swap.5 + neq.0 end + #=> [X, x, x, remainder_ptr, layer_ptr, g] + dropw drop drop - - # [ptr_queries_end, query_start_ptr, rem_ptr, g] - dropw swap - drop - - # [ptr_queries_end + 1, query_start_ptr, rem_ptr, g] - add.1 - - # [query_start_ptr, ptr_queries_end + 1, ptr_c_start, rem_ptr, g] locaddr.3 - add.1 - swap.2 + #=> [query_ptr, layer_ptr, remainder_ptr, g] end #! Checks that, for a query with index p at layer i, the folding procedure to create layer (i + 1) @@ -128,6 +115,7 @@ export.verify_query_layer.3 dup.5 movup.5 # [t_depth, f_pos, C, f_pos, d_seg, poe, e1, e0, a1, a0, layer_ptr, rem_ptr, ...] mtree_get # [V, C, f_pos, d_seg, poe, e1, e0, a1, a0, layer_ptr, rem_ptr, ...] + adv.keyval swapw # => [V, C, f_pos, d_seg, poe, e1, e0, a1, a0, layer_ptr, rem_ptr, ...] # where f_pos = p % d_size and d_seg = p / 4 @@ -243,14 +231,12 @@ end #! Verifies a FRI proof where the proof was generated over the quadratic extension of the base #! field and layer folding was performed using folding factor 4. #! -#! Input: [query_start_ptr, query_end_ptr, layer_ptr, rem_ptr, g, ...] +#! Input: [query_ptr, layer_ptr, rem_ptr, g, ...] #! Output: [...] #! -#! - query_start_ptr is a pointer to a list of tuples of the form (e0, e1, p, 0) where p is a -#! query index at the first layer and (e0, e1) is an extension field element corresponding to -#! the value of the first layer at index p. -#! - query_end_ptr is a pointer to the first empty memory address after the last (e0, e1, p, 0) -#! tuple. +#! - query_ptr is a pointer to a list of tuples of the form (e0, e1, p, poe) where poe is equal +#! to g^p with g being the initial FRI domain generator. p is the query index at the first layer +#! and (e0, e1) is an extension field element corresponding to the value of the first layer at index p. #! - layer_ptr is a pointer to the first layer commitment denoted throughout the code by C. #! layer_ptr + 1 points to the first [alpha0, alpha1, t_depth, d_size] where d_size is the size #! of initial domain divided by 4, t_depth is the depth of the Merkle tree commitment to the @@ -262,82 +248,52 @@ end #! #! The memory referenced above is used contiguously, as follows: #! -#! [layer_ptr ... rem_ptr ... query_start_ptr ... query_end_ptr] +#! [query_ptr ... layer_ptr ... rem_ptr ...] #! #! This means for example that: #! 1. rem_ptr - 1 points to the last (alpha0, alpha1, t_depth, d_size) tuple. -#! 2. The length of the remainder codeword is 2 * (rem_ptr - query_start_ptr). +#! 2. layer_ptr - 1 points to the last (e0, e1, p, poe) tuple. #! #! Cycles: for domains of size 2^n where: -#! - n is even: 12 + 6 + num_queries * (40 + num_layers * 76 + 69) + 2626 -#! - n is odd: 12 + 6 + num_queries * (40 + num_layers * 76 + 69) + 1356 +#! - n is even: 6 + 7 + num_queries * (40 + num_layers * 76 + 26) +#! - n is odd: 6 + 7 + num_queries * (40 + num_layers * 76 + 26) export.verify.1 - # store [query_start_ptr, query_end_ptr, layer_ptr, rem_ptr] to keep track of all queries + # store [query_ptr, layer_ptr, rem_ptr, g] to keep track of all queries # (3 cycles) locaddr.0 mem_storew - # compute size of remainder divided by 2 to be used in determining the appropriate remainder - # verification procedure. (5 cycles) + # [(query_ptr == layer_ptr), query_ptr, layer_ptr, rem_ptr, g] + # (3 cycles) dup - dup.4 - sub - movdn.5 - # => [query_start_ptr, query_end_ptr, layer_ptr, rem_ptr, g, rem_len] - - # compute number of remaining queries to be verified and assert it is not 0 - # (4 cycles) - swap - dup.1 + dup.2 neq - # => [?, query_start_ptr, layer_ptr, rem_ptr, g, rem_len, ...] while.true - # load [e0, e1, p, 0] from memory i.e. next query data (7 cycles) + # load [e0, e1, p, poe] from memory i.e. next query data (7 cycles) push.0.0.0.0 movup.4 mem_loadw - drop - # => [p, e1, e0, layer_ptr, rem_ptr, g, rem_len, ...] - - # compute poe = g^p (43 cycles) - dup.5 - dup.1 - exp.u32 - # => [poe, p, e1, e0, layer_ptr, rem_ptr, rem_len, ...] + # => [poe, p, e1, e0, layer_ptr, rem_ptr, g, ...] # we now have everything to verify query p exec.verify_query - # prepare for next iteration (17 cycles) - # => [x, x, x, x, x, x, x, x, x, x, g, rem_len, ...] - dropw drop drop + # prepare for next iteration (18 cycles) + # => [x, x, x, x, x, x, x, x, x, x, g, ...] + dropw drop drop drop locaddr.0 - mem_loadw # load [query_start_ptr, query_end_ptr, layer_ptr, rem_ptr] + mem_loadw # load [query_ptr, layer_ptr, rem_ptr, g] add.1 locaddr.0 - mem_storew # store [query_start_ptr + 1, query_end_ptr, layer_ptr, rem_ptr] - swap - dup.1 + mem_storew # store [query_ptr + 1, layer_ptr, rem_ptr, g] + dup + dup.2 neq - # => [?, query_start_ptr + 1, layer_ptr, rem_ptr, g, rem_len, ...] + #=> [?, query_ptr + 1, layer_ptr, rem_ptr, g, ...] end + #=> [X, ..] - # verify that remainder corresponds to a low degree polynomial - - # check if remainder is of size 64 (6 cycles) - # => [x, x, rem_ptr, g, rem_len, ...] - drop drop movdn.2 drop - push.32 - eq - # => [?, rem_ptr, ...] - - if.true - # cycles 2626 - exec.ext2fri::verify_remainder_64 - else - # cycles 1356 - exec.ext2fri::verify_remainder_32 - end -end + dropw +end \ No newline at end of file diff --git a/stdlib/docs/frie2f4_fri.md b/stdlib/docs/frie2f4_fri.md index 96c7de11e2..3acc852786 100644 --- a/stdlib/docs/frie2f4_fri.md +++ b/stdlib/docs/frie2f4_fri.md @@ -2,7 +2,7 @@ ## std::crypto::fri::frie2f4 | Procedure | Description | | ----------- | ------------- | -| preprocess | Stores the layer commitments C followed by [d_size, t_depth, a1, a0] and [0, p, e1, e0] where:

1) d_size is the domain size divided by 4 of the domain corresponding to C.

2) t_depth is the tree depth of the Merkle tree with commitment C.

3) (a0, a1) is the folding challenge to create the next layer.

4) p is the query index and (e0, e1) is the evaluation at the first layer.

TODO: This processing function should in fact compute d_size and t_depth for each C

starting from the original domain size. | +| preprocess | Stores the layer commitments C followed by [d_size, t_depth, a1, a0] and [poe, p, e1, e0] where:

1) d_size is the domain size divided by 4 of the domain corresponding to C.

2) t_depth is the tree depth of the Merkle tree with commitment C.

3) (a0, a1) is the folding challenge to create the next layer.

4) p is the query index and (e0, e1) is the evaluation at the first layer and poe is g^p with

g being the initial domain generator.

TODO: This pre-processing function should in fact compute d_size and t_depth for each C

starting from the original domain size. | | verify_query_layer | Checks that, for a query with index p at layer i, the folding procedure to create layer (i + 1)

was performed correctly. This also advances layer_ptr by 2 to point to the next query layer.

Input: [layer_ptr, layer_ptr, poe, p, e1, e0, layer_ptr, rem_ptr, x, x, x, x, x, x, x, x, ...]

Output: [layer_ptr + 2, layer_ptr + 2, poe^4, f_pos, ne1, ne0, layer_ptr + 2, rem_ptr, x, x, x, x, x, x, x, x, ...]

Cycles: 76 | | verify_query | Verifies one FRI query.

Input: [poe, p, e1, e0, layer_ptr, rem_ptr, ...]

Output: [x, x, x, x, x, x, x, x, x, x, ...]

- poe is g^p.

- p is a query index at the first layer.

- (e0, e1) is an extension field element corresponding to the value of the first layer at index p.

- layer_ptr is the memory address of the layer data (Merkle tree root, alpha etc.) for the next

layer.

- rem_ptr is the memory address of the remainder codeword.

Cycles: 40 + num_layers * 76 | -| verify | Verifies a FRI proof where the proof was generated over the quadratic extension of the base

field and layer folding was performed using folding factor 4.

Input: [query_start_ptr, query_end_ptr, layer_ptr, rem_ptr, g, ...]

Output: [...]

- query_start_ptr is a pointer to a list of tuples of the form (e0, e1, p, 0) where p is a

query index at the first layer and (e0, e1) is an extension field element corresponding to

the value of the first layer at index p.

- query_end_ptr is a pointer to the first empty memory address after the last (e0, e1, p, 0)

tuple.

- layer_ptr is a pointer to the first layer commitment denoted throughout the code by C.

layer_ptr + 1 points to the first [alpha0, alpha1, t_depth, d_size] where d_size is the size

of initial domain divided by 4, t_depth is the depth of the Merkle tree commitment to the

first layer and (alpha0, alpha1) is the first challenge used in folding the first layer.

Both t_depth and d_size are expected to be smaller than 2^32. Otherwise, the result of

this procedure is undefined.

- rem_ptr is a pointer to the first tuple of two consecutive degree 2 extension field

elements making up the remainder codeword. This codeword can be of length either 32 or 64.

The memory referenced above is used contiguously, as follows:

[layer_ptr ... rem_ptr ... query_start_ptr ... query_end_ptr]

This means for example that:

1. rem_ptr - 1 points to the last (alpha0, alpha1, t_depth, d_size) tuple.

2. The length of the remainder codeword is 2 * (rem_ptr - query_start_ptr).

Cycles: for domains of size 2^n where:

- n is even: 12 + 6 + num_queries * (40 + num_layers * 76 + 69) + 2626

- n is odd: 12 + 6 + num_queries * (40 + num_layers * 76 + 69) + 1356 | +| verify | Verifies a FRI proof where the proof was generated over the quadratic extension of the base

field and layer folding was performed using folding factor 4.

Input: [query_ptr, layer_ptr, rem_ptr, g, ...]

Output: [...]

- query_ptr is a pointer to a list of tuples of the form (e0, e1, p, poe) where poe is equal

to g^p with g being the initial FRI domain generator. p is the query index at the first layer

and (e0, e1) is an extension field element corresponding to the value of the first layer at index p.

- layer_ptr is a pointer to the first layer commitment denoted throughout the code by C.

layer_ptr + 1 points to the first [alpha0, alpha1, t_depth, d_size] where d_size is the size

of initial domain divided by 4, t_depth is the depth of the Merkle tree commitment to the

first layer and (alpha0, alpha1) is the first challenge used in folding the first layer.

Both t_depth and d_size are expected to be smaller than 2^32. Otherwise, the result of

this procedure is undefined.

- rem_ptr is a pointer to the first tuple of two consecutive degree 2 extension field

elements making up the remainder codeword. This codeword can be of length either 32 or 64.

The memory referenced above is used contiguously, as follows:

[query_ptr ... layer_ptr ... rem_ptr ...]

This means for example that:

1. rem_ptr - 1 points to the last (alpha0, alpha1, t_depth, d_size) tuple.

2. layer_ptr - 1 points to the last (e0, e1, p, poe) tuple.

Cycles: for domains of size 2^n where:

- n is even: 6 + 7 + num_queries * (40 + num_layers * 76 + 26)

- n is odd: 6 + 7 + num_queries * (40 + num_layers * 76 + 26) | From 4a4ab5b6632559f35a38f8f786ae3844a079614d Mon Sep 17 00:00:00 2001 From: Al-Kindi-0 <82364884+Al-Kindi-0@users.noreply.github.com> Date: Tue, 28 Mar 2023 13:42:24 +0200 Subject: [PATCH 41/47] chore: fix nits --- .../integration/stdlib/crypto/fri/channel.rs | 8 ++------ .../tests/integration/stdlib/crypto/fri/mod.rs | 6 +++--- stdlib/asm/crypto/fri/frie2f4.masm | 17 +++++++---------- stdlib/docs/frie2f4_fri.md | 2 +- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/miden/tests/integration/stdlib/crypto/fri/channel.rs b/miden/tests/integration/stdlib/crypto/fri/channel.rs index 1859202297..d1367171ce 100644 --- a/miden/tests/integration/stdlib/crypto/fri/channel.rs +++ b/miden/tests/integration/stdlib/crypto/fri/channel.rs @@ -76,12 +76,8 @@ where // Compute remainder codeword corresponding to remainder polynomial let twiddles = fft::get_twiddles(poly.len()); - let remainder = fft::evaluate_poly_with_offset( - &poly, - &twiddles, - ::GENERATOR, - 8, - ); + let remainder = + fft::evaluate_poly_with_offset(&poly, &twiddles, E::BaseField::GENERATOR, 8); Ok(remainder) } diff --git a/miden/tests/integration/stdlib/crypto/fri/mod.rs b/miden/tests/integration/stdlib/crypto/fri/mod.rs index 68d7ec5b3e..e632a24b9b 100644 --- a/miden/tests/integration/stdlib/crypto/fri/mod.rs +++ b/miden/tests/integration/stdlib/crypto/fri/mod.rs @@ -33,7 +33,7 @@ fn fri_fold4_ext2_remainder32() { let (advice_provider, position_eval, alphas, commitments, remainder, num_queries) = fri_prove_verify_fold4_ext2(trace_len_e).expect("should not panic"); - let advice_stack = prepare_advice( + let advice_stack = prepare_advice_stack( depth, domain_size, num_queries, @@ -73,7 +73,7 @@ fn fri_fold4_ext2_remainder64() { let (advice_provider, position_eval, alphas, commitments, remainder, num_queries) = fri_prove_verify_fold4_ext2(trace_len_e).expect("should not panic"); - let advice_stack = prepare_advice( + let advice_stack = prepare_advice_stack( depth, domain_size, num_queries, @@ -94,7 +94,7 @@ fn fri_fold4_ext2_remainder64() { test.expect_stack(&[]); } -fn prepare_advice( +fn prepare_advice_stack( depth: usize, domain_size: u32, num_queries: usize, diff --git a/stdlib/asm/crypto/fri/frie2f4.masm b/stdlib/asm/crypto/fri/frie2f4.masm index 95700b3972..67f6476455 100644 --- a/stdlib/asm/crypto/fri/frie2f4.masm +++ b/stdlib/asm/crypto/fri/frie2f4.masm @@ -230,6 +230,8 @@ end #! Verifies a FRI proof where the proof was generated over the quadratic extension of the base #! field and layer folding was performed using folding factor 4. +#! Note that the check that the remainder codeword corresponds to the remainder polynomial received +#! by the verifier should now be performed by the calling procedure. #! #! Input: [query_ptr, layer_ptr, rem_ptr, g, ...] #! Output: [...] @@ -254,18 +256,15 @@ end #! 1. rem_ptr - 1 points to the last (alpha0, alpha1, t_depth, d_size) tuple. #! 2. layer_ptr - 1 points to the last (e0, e1, p, poe) tuple. #! -#! Cycles: for domains of size 2^n where: -#! - n is even: 6 + 7 + num_queries * (40 + num_layers * 76 + 26) -#! - n is odd: 6 + 7 + num_queries * (40 + num_layers * 76 + 26) +#! Cycles: 7 + 4 + num_queries * (40 + num_layers * 76 + 26) export.verify.1 # store [query_ptr, layer_ptr, rem_ptr, g] to keep track of all queries # (3 cycles) - locaddr.0 - mem_storew + loc_storew.0 # [(query_ptr == layer_ptr), query_ptr, layer_ptr, rem_ptr, g] - # (3 cycles) + # (4 cycles) dup dup.2 neq @@ -283,11 +282,9 @@ export.verify.1 # prepare for next iteration (18 cycles) # => [x, x, x, x, x, x, x, x, x, x, g, ...] dropw drop drop drop - locaddr.0 - mem_loadw # load [query_ptr, layer_ptr, rem_ptr, g] + loc_loadw.0 # load [query_ptr, layer_ptr, rem_ptr, g] add.1 - locaddr.0 - mem_storew # store [query_ptr + 1, layer_ptr, rem_ptr, g] + loc_storew.0 # store [query_ptr + 1, layer_ptr, rem_ptr, g] dup dup.2 neq diff --git a/stdlib/docs/frie2f4_fri.md b/stdlib/docs/frie2f4_fri.md index 3acc852786..57381f6794 100644 --- a/stdlib/docs/frie2f4_fri.md +++ b/stdlib/docs/frie2f4_fri.md @@ -5,4 +5,4 @@ | preprocess | Stores the layer commitments C followed by [d_size, t_depth, a1, a0] and [poe, p, e1, e0] where:

1) d_size is the domain size divided by 4 of the domain corresponding to C.

2) t_depth is the tree depth of the Merkle tree with commitment C.

3) (a0, a1) is the folding challenge to create the next layer.

4) p is the query index and (e0, e1) is the evaluation at the first layer and poe is g^p with

g being the initial domain generator.

TODO: This pre-processing function should in fact compute d_size and t_depth for each C

starting from the original domain size. | | verify_query_layer | Checks that, for a query with index p at layer i, the folding procedure to create layer (i + 1)

was performed correctly. This also advances layer_ptr by 2 to point to the next query layer.

Input: [layer_ptr, layer_ptr, poe, p, e1, e0, layer_ptr, rem_ptr, x, x, x, x, x, x, x, x, ...]

Output: [layer_ptr + 2, layer_ptr + 2, poe^4, f_pos, ne1, ne0, layer_ptr + 2, rem_ptr, x, x, x, x, x, x, x, x, ...]

Cycles: 76 | | verify_query | Verifies one FRI query.

Input: [poe, p, e1, e0, layer_ptr, rem_ptr, ...]

Output: [x, x, x, x, x, x, x, x, x, x, ...]

- poe is g^p.

- p is a query index at the first layer.

- (e0, e1) is an extension field element corresponding to the value of the first layer at index p.

- layer_ptr is the memory address of the layer data (Merkle tree root, alpha etc.) for the next

layer.

- rem_ptr is the memory address of the remainder codeword.

Cycles: 40 + num_layers * 76 | -| verify | Verifies a FRI proof where the proof was generated over the quadratic extension of the base

field and layer folding was performed using folding factor 4.

Input: [query_ptr, layer_ptr, rem_ptr, g, ...]

Output: [...]

- query_ptr is a pointer to a list of tuples of the form (e0, e1, p, poe) where poe is equal

to g^p with g being the initial FRI domain generator. p is the query index at the first layer

and (e0, e1) is an extension field element corresponding to the value of the first layer at index p.

- layer_ptr is a pointer to the first layer commitment denoted throughout the code by C.

layer_ptr + 1 points to the first [alpha0, alpha1, t_depth, d_size] where d_size is the size

of initial domain divided by 4, t_depth is the depth of the Merkle tree commitment to the

first layer and (alpha0, alpha1) is the first challenge used in folding the first layer.

Both t_depth and d_size are expected to be smaller than 2^32. Otherwise, the result of

this procedure is undefined.

- rem_ptr is a pointer to the first tuple of two consecutive degree 2 extension field

elements making up the remainder codeword. This codeword can be of length either 32 or 64.

The memory referenced above is used contiguously, as follows:

[query_ptr ... layer_ptr ... rem_ptr ...]

This means for example that:

1. rem_ptr - 1 points to the last (alpha0, alpha1, t_depth, d_size) tuple.

2. layer_ptr - 1 points to the last (e0, e1, p, poe) tuple.

Cycles: for domains of size 2^n where:

- n is even: 6 + 7 + num_queries * (40 + num_layers * 76 + 26)

- n is odd: 6 + 7 + num_queries * (40 + num_layers * 76 + 26) | +| verify | Verifies a FRI proof where the proof was generated over the quadratic extension of the base

field and layer folding was performed using folding factor 4.

Note that the check that the remainder codeword corresponds to the remainder polynomial received

by the verifier should now be performed by the calling procedure.

Input: [query_ptr, layer_ptr, rem_ptr, g, ...]

Output: [...]

- query_ptr is a pointer to a list of tuples of the form (e0, e1, p, poe) where poe is equal

to g^p with g being the initial FRI domain generator. p is the query index at the first layer

and (e0, e1) is an extension field element corresponding to the value of the first layer at index p.

- layer_ptr is a pointer to the first layer commitment denoted throughout the code by C.

layer_ptr + 1 points to the first [alpha0, alpha1, t_depth, d_size] where d_size is the size

of initial domain divided by 4, t_depth is the depth of the Merkle tree commitment to the

first layer and (alpha0, alpha1) is the first challenge used in folding the first layer.

Both t_depth and d_size are expected to be smaller than 2^32. Otherwise, the result of

this procedure is undefined.

- rem_ptr is a pointer to the first tuple of two consecutive degree 2 extension field

elements making up the remainder codeword. This codeword can be of length either 32 or 64.

The memory referenced above is used contiguously, as follows:

[query_ptr ... layer_ptr ... rem_ptr ...]

This means for example that:

1. rem_ptr - 1 points to the last (alpha0, alpha1, t_depth, d_size) tuple.

2. layer_ptr - 1 points to the last (e0, e1, p, poe) tuple.

Cycles: 7 + 4 + num_queries * (40 + num_layers * 76 + 26) | From 0697f3fc8c061e69fffc75318f2b3eef20402aa8 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Tue, 28 Mar 2023 13:51:20 +0200 Subject: [PATCH 42/47] docs: formatting is_odd as the other ops --- docs/src/user_docs/assembly/field_operations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/user_docs/assembly/field_operations.md b/docs/src/user_docs/assembly/field_operations.md index db7cda821b..95d665bf87 100644 --- a/docs/src/user_docs/assembly/field_operations.md +++ b/docs/src/user_docs/assembly/field_operations.md @@ -42,7 +42,7 @@ For instructions where one or more operands can be provided as immediate paramet | lte
- *(18 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a \le b \\ 0, & \text{otherwise}\ \end{cases}$ | | gt
- *(18 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a > b \\ 0, & \text{otherwise}\ \end{cases}$ | | gte
- *(19 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a \ge b \\ 0, & \text{otherwise}\ \end{cases}$ | -| is_odd
- *(5 cycles)* | [a, ...] | [b, ...] | Pushes $1$ to the stack if the number $a$ is an odd number $0$ otherwise | +| is_odd
- *(5 cycles)* | [a, ...] | [b, ...] | $b \leftarrow \begin{cases} 1, & \text{if}\ a \text{ is odd} \\ 0, & \text{otherwise}\ \end{cases}$ | | eqw
- *(15 cycles)* | [A, B, ...] | [c, A, B, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a_i = b_i \; \forall i \in \{0, 1, 2, 3\} \\ 0, & \text{otherwise}\ \end{cases}$ | ### Extension Field Operations From e5d4a4416b5030744e6a99f82a890cdea49a169e Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Tue, 28 Mar 2023 13:59:58 +0200 Subject: [PATCH 43/47] docs: align ops table --- .../user_docs/assembly/field_operations.md | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/docs/src/user_docs/assembly/field_operations.md b/docs/src/user_docs/assembly/field_operations.md index 95d665bf87..86fec41744 100644 --- a/docs/src/user_docs/assembly/field_operations.md +++ b/docs/src/user_docs/assembly/field_operations.md @@ -7,53 +7,53 @@ For instructions where one or more operands can be provided as immediate paramet ### Assertions and tests -| Instruction | Stack_input | Stack_output | Notes | -| ---------------- | ----------- | ------------- | ----------------------------- | -| assert
- *(1 cycle)* | [a, ...] | [...] | If $a = 1$, removes it from the stack.
Fails if $a \ne 1$ | -| assertz
- *(2 cycles)* | [ a, ...] | [...] | if $a = 0$, removes it from the stack,
Fails if $a \ne 0$ | -| assert_eq
- *(2 cycles)* | [b, a, ...] | [...] | If $a = b$, removes them from the stack.
Fails if $a \ne b$ | -| assert_eqw
- *(11 cycles)* | [B, A, ...] | [...] | If $A = B$, removes them from the stack.
Fails if $A \ne B$ | +| Instruction | Stack_input | Stack_output | Notes | +| ------------------------------- | ----------- | ------------- | ---------------------------------------------------------------- | +| assert
- *(1 cycle)* | [a, ...] | [...] | If $a = 1$, removes it from the stack.
Fails if $a \ne 1$ | +| assertz
- *(2 cycles)* | [a, ...] | [...] | If $a = 0$, removes it from the stack,
Fails if $a \ne 0$ | +| assert_eq
- *(2 cycles)* | [b, a, ...] | [...] | If $a = b$, removes them from the stack.
Fails if $a \ne b$ | +| assert_eqw
- *(11 cycles)* | [B, A, ...] | [...] | If $A = B$, removes them from the stack.
Fails if $A \ne B$ | ### Arithmetic and Boolean operations -| Instruction | Stack_input | Stack_output | Notes | -| ---------------- | ----------- | ------------- | ----------------------------- | -| add
- *(1 cycle)*
add.*b*
- *(1-2 cycle)* | [b, a, ...] | [c, ...] | $c \leftarrow (a + b) \mod p$ | -| sub
- *(2 cycles)*
sub.*b*
- *(2 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a - b) \mod p$ | -| mul
- *(1 cycle)*
mul.*b*
- *(2 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a \cdot b) \mod p$ | -| div
- *(2 cycles)*
div.*b*
- *(2 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a \cdot b^{-1}) \mod p$
Fails if $b = 0$ | -| neg
- *(1 cycle)* | [a, ...] | [b, ...] | $b \leftarrow -a \mod p$ | -| inv
- *(1 cycle)* | [a, ...] | [b, ...] | $b \leftarrow a^{-1} \mod p$
Fails if $a = 0$ | -| pow2
- *(16 cycles)* | [a, ...] | [b, ...] | $b \leftarrow 2^a$
Fails if $a > 63$ | -| exp.*uxx*
- *(9 + xx cycles)*
exp.*b*
- *(9 + log2(b) cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow a^b$
Fails if xx is outside [0, 63)
exp is equivalent to exp.u64 and needs 73 cycles| -| not
- *(1 cycle)* | [a, ...] | [b, ...] | $b \leftarrow 1 - a$
Fails if $a > 1$ | -| and
- *(1 cycle)* | [b, a, ...] | [c, ...] | $c \leftarrow a \cdot b$
Fails if $max(a, b) > 1$ | -| or
- *(1 cycle)* | [b, a, ...] | [c, ...] | $c \leftarrow a + b - a \cdot b$
Fails if $max(a, b) > 1$ | -| xor
- *(7 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow a + b - 2 \cdot a \cdot b$
Fails if $max(a, b) > 1$ | +| Instruction | Stack_input | Stack_output | Notes | +| ------------------------------------------------------------------------------ | ----------- | ------------- | ------------------------------------------------------------------------------------------------------------ | +| add
- *(1 cycle)*
add.*b*
- *(1-2 cycle)* | [b, a, ...] | [c, ...] | $c \leftarrow (a + b) \mod p$ | +| sub
- *(2 cycles)*
sub.*b*
- *(2 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a - b) \mod p$ | +| mul
- *(1 cycle)*
mul.*b*
- *(2 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a \cdot b) \mod p$ | +| div
- *(2 cycles)*
div.*b*
- *(2 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow (a \cdot b^{-1}) \mod p$
Fails if $b = 0$ | +| neg
- *(1 cycle)* | [a, ...] | [b, ...] | $b \leftarrow -a \mod p$ | +| inv
- *(1 cycle)* | [a, ...] | [b, ...] | $b \leftarrow a^{-1} \mod p$
Fails if $a = 0$ | +| pow2
- *(16 cycles)* | [a, ...] | [b, ...] | $b \leftarrow 2^a$
Fails if $a > 63$ | +| exp.*uxx*
- *(9 + xx cycles)*
exp.*b*
- *(9 + log2(b) cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow a^b$
Fails if xx is outside [0, 63)
exp is equivalent to exp.u64 and needs 73 cycles | +| not
- *(1 cycle)* | [a, ...] | [b, ...] | $b \leftarrow 1 - a$
Fails if $a > 1$ | +| and
- *(1 cycle)* | [b, a, ...] | [c, ...] | $c \leftarrow a \cdot b$
Fails if $max(a, b) > 1$ | +| or
- *(1 cycle)* | [b, a, ...] | [c, ...] | $c \leftarrow a + b - a \cdot b$
Fails if $max(a, b) > 1$ | +| xor
- *(7 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow a + b - 2 \cdot a \cdot b$
Fails if $max(a, b) > 1$ | ### Comparison operations -| Instruction | Stack_input | Stack_output | Notes | -| ---------------- | ----------- | -------------- | ----------------------------- | -| eq
- *(1 cycle)*
eq.*b*
- *(1-2 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a=b \\ 0, & \text{otherwise}\ \end{cases}$ | -| neq
- *(2 cycle)*
neq.*b*
- *(2-3 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a \ne b \\ 0, & \text{otherwise}\ \end{cases}$ | -| lt
- *(17 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a < b \\ 0, & \text{otherwise}\ \end{cases}$ | -| lte
- *(18 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a \le b \\ 0, & \text{otherwise}\ \end{cases}$ | -| gt
- *(18 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a > b \\ 0, & \text{otherwise}\ \end{cases}$ | -| gte
- *(19 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a \ge b \\ 0, & \text{otherwise}\ \end{cases}$ | -| is_odd
- *(5 cycles)* | [a, ...] | [b, ...] | $b \leftarrow \begin{cases} 1, & \text{if}\ a \text{ is odd} \\ 0, & \text{otherwise}\ \end{cases}$ | -| eqw
- *(15 cycles)* | [A, B, ...] | [c, A, B, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a_i = b_i \; \forall i \in \{0, 1, 2, 3\} \\ 0, & \text{otherwise}\ \end{cases}$ | +| Instruction | Stack_input | Stack_output | Notes | +| ---------------------------------------------------------- | ----------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| eq
- *(1 cycle)*
eq.*b*
- *(1-2 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a=b \\ 0, & \text{otherwise}\ \end{cases}$ | +| neq
- *(2 cycle)*
neq.*b*
- *(2-3 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a \ne b \\ 0, & \text{otherwise}\ \end{cases}$ | +| lt
- *(17 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a < b \\ 0, & \text{otherwise}\ \end{cases}$ | +| lte
- *(18 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a \le b \\ 0, & \text{otherwise}\ \end{cases}$ | +| gt
- *(18 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a > b \\ 0, & \text{otherwise}\ \end{cases}$ | +| gte
- *(19 cycles)* | [b, a, ...] | [c, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a \ge b \\ 0, & \text{otherwise}\ \end{cases}$ | +| is_odd
- *(5 cycles)* | [a, ...] | [b, ...] | $b \leftarrow \begin{cases} 1, & \text{if}\ a \text{ is odd} \\ 0, & \text{otherwise}\ \end{cases}$ | +| eqw
- *(15 cycles)* | [A, B, ...] | [c, A, B, ...] | $c \leftarrow \begin{cases} 1, & \text{if}\ a_i = b_i \; \forall i \in \{0, 1, 2, 3\} \\ 0, & \text{otherwise}\ \end{cases}$ | ### Extension Field Operations -| Instruction | Stack Input | Stack Output | Notes | -| ----------- | ----------- | ------------ | ----------- | -| ext2add
- *(5 cycles)*
| [b, a, ...] | [c, ...] | $c \leftarrow (a + b) \mod q$ | -| ext2sub
- *(7 cycles)*
| [b, a, ...] | [c, ...] | $c \leftarrow (a - b) \mod q$ | -| ext2mul
- *(3 cycles)*
| [b, a, ...] | [c, ...] | $c \leftarrow (a \cdot b) \mod q$ | -| ext2div
- *(11 cycles)*
| [b, a, ...] | [c, ...] | $c \leftarrow (a \cdot b^{-1}) \mod q$
Fails if $b = 0$ | -| ext2neg
- *(4 cycles)*
| [a, ...] | [b, ...] | $b \leftarrow -a \mod q$ | -| ext2inv
- *(8 cycles)*
| [a, ...] | [b, ...] | $b \leftarrow a^{-1} \mod q$
Fails if $a = 0$ | +| Instruction | Stack Input | Stack Output | Notes | +| ---------------------------------- | ----------- | ------------ | ------------------------------------------------------------ | +| ext2add
- *(5 cycles)*
| [b, a, ...] | [c, ...] | $c \leftarrow (a + b) \mod q$ | +| ext2sub
- *(7 cycles)*
| [b, a, ...] | [c, ...] | $c \leftarrow (a - b) \mod q$ | +| ext2mul
- *(3 cycles)*
| [b, a, ...] | [c, ...] | $c \leftarrow (a \cdot b) \mod q$ | +| ext2div
- *(11 cycles)*
| [b, a, ...] | [c, ...] | $c \leftarrow (a \cdot b^{-1}) \mod q$
Fails if $b = 0$ | +| ext2neg
- *(4 cycles)*
| [a, ...] | [b, ...] | $b \leftarrow -a \mod q$ | +| ext2inv
- *(8 cycles)*
| [a, ...] | [b, ...] | $b \leftarrow a^{-1} \mod q$
Fails if $a = 0$ | where $q$ is an irreducible polynomial $x^2 - x + 2$ over $F_p$ for $p = 2^{64} - 2^{32} + 1$ From bc32f117ba8ab85f79b329dd8e581e8cdf38d36b Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Mon, 27 Mar 2023 22:37:23 -0700 Subject: [PATCH 44/47] docs: update benchmarks and advice provider doc comments --- CHANGELOG.md | 10 ++++- README.md | 36 +++++++++-------- docs/src/intro/performance.md | 36 +++++++++-------- processor/src/advice/mod.rs | 71 +++++++++++++++++----------------- processor/src/advice/source.rs | 12 +++--- 5 files changed, 88 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98072c0df2..7f88776517 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,21 @@ # Changelog -## Next +## 0.5.0 (TBD) #### CLI - Renamed `ProgramInfo` to `ExecutionDetails` since there is another `ProgramInfo` struct in the source code. +- [BREAKING] renamed `stack_init` and `advice_tape` to `operand_stack` and `advice_stack` in input files. +- Enabled specifying additional advice provider inputs (i.e., advice map and Merkle store) via the input files. #### Assembly - Added new instructions: `is_odd`, `assert_eqw`, `mtree_merge`. - [BREAKING] Removed `mtree_cwm` instruction. +- Added `breakpoint` instruction to help with debugging. + +#### VM Internals +- [BREAKING] Renamed `Read`, `ReadW` operations into `AdvPop`, `AdvPopW`. +- [BREAKING] Replaced `AdviceSet` with `MerkleStore`. +- Updated Winterfell dependency to v0.6.0. ## 0.4.0 (2023-02-27) diff --git a/README.md b/README.md index fcfd8f85ee..6d24afaba2 100644 --- a/README.md +++ b/README.md @@ -81,12 +81,12 @@ When executed on a single CPU core, the current version of Miden VM operates at | VM cycles | Execution time | Proving time | RAM consumed | Proof size | | :-------------: | :------------: | :----------: | :-----------: | :--------: | -| 210 | 1 ms | 80 ms | 14 MB | 52 KB | -| 212 | 2 ms | 280 ms | 43 MB | 61 KB | -| 214 | 8 ms | 1.1 sec | 163 MB | 71 KB | -| 216 | 28 ms | 4.4 sec | 640 MB | 81 KB | -| 218 | 85 ms | 19.2 sec | 2.6 GB | 92 KB | -| 220 | 320 ms | 86 sec | 10 GB | 104 KB | +| 210 | 1 ms | 80 ms | 20 MB | 47 KB | +| 212 | 2 ms | 260 ms | 52 MB | 57 KB | +| 214 | 8 ms | 0.9 sec | 240 MB | 66 KB | +| 216 | 28 ms | 4.6 sec | 950 MB | 77 KB | +| 218 | 85 ms | 15.5 sec | 3.7 GB | 89 KB | +| 220 | 310 ms | 67 sec | 14 GB | 100 KB | As can be seen from the above, proving time roughly doubles with every doubling in the number of cycles, but proof size grows much slower. @@ -94,22 +94,24 @@ We can also generate proofs at a higher security level. The cost of doing so is | VM cycles | Execution time | Proving time | RAM consumed | Proof size | | :-------------: | :------------: | :----------: | :-----------: | :--------: | -| 210 | 1 ms | 140 ms | 26 MB | 73 KB | -| 212 | 2 ms | 510 ms | 90 MB | 87 KB | -| 214 | 8 ms | 2.1 sec | 350 MB | 98 KB | -| 216 | 28 ms | 7.9 sec | 1.4 GB | 115 KB | -| 218 | 85 ms | 35 sec | 5.6 GB | 132 KB | -| 220 | 320 ms | 151 sec | 20.3 GB | 149 KB | +| 210 | 1 ms | 300 ms | 30 MB | 61 KB | +| 212 | 2 ms | 590 ms | 106 MB | 78 KB | +| 214 | 8 ms | 1.7 sec | 500 MB | 91 KB | +| 216 | 28 ms | 6.7 sec | 2.0 GB | 106 KB | +| 218 | 85 ms | 27.5 sec | 8.0 GB | 122 KB | +| 220 | 310 ms | 126 sec | 24.0 GB | 138 KB | ### Multi-core prover performance -STARK proof generation is massively parallelizable. Thus, by taking advantage of multiple CPU cores we can dramatically reduce proof generation time. For example, when executed on a high-end 8-core CPU (Apple M1 Pro), the current version of Miden VM operates at around 80 KHz. And when executed on a high-end 64-core CPU (Amazon Graviton 3), the VM operates at around 320 KHz. +STARK proof generation is massively parallelizable. Thus, by taking advantage of multiple CPU cores we can dramatically reduce proof generation time. For example, when executed on an 8-core CPU (Apple M1 Pro), the current version of Miden VM operates at around 100 KHz. And when executed on a 64-core CPU (Amazon Graviton 3), the VM operates at around 250 KHz. In the benchmarks below, the VM executes the same Fibonacci calculator program for 220 cycles at 96-bit target security level: -| Machine | Execution time | Proving time | -| ------------------------------ | :------------: | :----------: | -| Apple M1 Pro (8 threads) | 320 ms | 13 sec | -| Amazon Graviton 3 (64 threads) | 390 ms | 3.3 sec | +| Machine | Execution time | Proving time | Execution % | +| ------------------------------ | :------------: | :----------: | :---------: | +| Apple M1 Pro (8 threads) | 310 ms | 9.8 sec | 3.1% | +| Apple M2 Max (16 threads) | 290 ms | 7.7 sec | 3.6% | +| AMD Ryzen 9 5950X (16 threads) | 270 ms | 10.7 sec | 2.6% | +| Amazon Graviton 3 (64 threads) | 330 ms | 3.7 sec | 9.0% | ## References Proofs of execution generated by Miden VM are based on STARKs. A STARK is a novel proof-of-computation scheme that allows you to create an efficiently verifiable proof that a computation was executed correctly. The scheme was developed by Eli Ben-Sasson, Michael Riabzev et al. at Technion - Israel Institute of Technology. STARKs do not require an initial trusted setup, and rely on very few cryptographic assumptions. diff --git a/docs/src/intro/performance.md b/docs/src/intro/performance.md index e96a984074..ab02a04fd7 100644 --- a/docs/src/intro/performance.md +++ b/docs/src/intro/performance.md @@ -17,12 +17,12 @@ When executed on a single CPU core, the current version of Miden VM operates at | VM cycles | Execution time | Proving time | RAM consumed | Proof size | | :-------------: | :------------: | :----------: | :-----------: | :--------: | -| 210 | 1 ms | 80 ms | 14 MB | 52 KB | -| 212 | 2 ms | 280 ms | 43 MB | 61 KB | -| 214 | 8 ms | 1.1 sec | 163 MB | 71 KB | -| 216 | 28 ms | 4.4 sec | 640 MB | 81 KB | -| 218 | 85 ms | 19.2 sec | 2.6 GB | 92 KB | -| 220 | 320 ms | 86 sec | 10 GB | 104 KB | +| 210 | 1 ms | 80 ms | 20 MB | 47 KB | +| 212 | 2 ms | 260 ms | 52 MB | 57 KB | +| 214 | 8 ms | 0.9 sec | 240 MB | 66 KB | +| 216 | 28 ms | 4.6 sec | 950 MB | 77 KB | +| 218 | 85 ms | 15.5 sec | 3.7 GB | 89 KB | +| 220 | 310 ms | 67 sec | 14 GB | 100 KB | As can be seen from the above, proving time roughly doubles with every doubling in the number of cycles, but proof size grows much slower. @@ -30,19 +30,21 @@ We can also generate proofs at a higher security level. The cost of doing so is | VM cycles | Execution time | Proving time | RAM consumed | Proof size | | :-------------: | :------------: | :----------: | :-----------: | :--------: | -| 210 | 1 ms | 140 ms | 26 MB | 73 KB | -| 212 | 2 ms | 510 ms | 90 MB | 87 KB | -| 214 | 8 ms | 2.1 sec | 350 MB | 98 KB | -| 216 | 28 ms | 7.9 sec | 1.4 GB | 115 KB | -| 218 | 85 ms | 35 sec | 5.6 GB | 132 KB | -| 220 | 320 ms | 151 sec | 20.3 GB | 149 KB | +| 210 | 1 ms | 300 ms | 30 MB | 61 KB | +| 212 | 2 ms | 590 ms | 106 MB | 78 KB | +| 214 | 8 ms | 1.7 sec | 500 MB | 91 KB | +| 216 | 28 ms | 6.7 sec | 2.0 GB | 106 KB | +| 218 | 85 ms | 27.5 sec | 8.0 GB | 122 KB | +| 220 | 310 ms | 126 sec | 24.0 GB | 138 KB | ## Multi-core prover performance -STARK proof generation is massively parallelizable. Thus, by taking advantage of multiple CPU cores we can dramatically reduce proof generation time. For example, when executed on a high-end 8-core CPU (Apple M1 Pro), the current version of Miden VM operates at around 80 KHz. And when executed on a high-end 64-core CPU (Amazon Graviton 3), the VM operates at around 320 KHz. +STARK proof generation is massively parallelizable. Thus, by taking advantage of multiple CPU cores we can dramatically reduce proof generation time. For example, when executed on an 8-core CPU (Apple M1 Pro), the current version of Miden VM operates at around 100 KHz. And when executed on a 64-core CPU (Amazon Graviton 3), the VM operates at around 250 KHz. In the benchmarks below, the VM executes the same Fibonacci calculator program for 220 cycles at 96-bit target security level: -| Machine | Execution time | Proving time | -| ------------------------------ | :------------: | :----------: | -| Apple M1 Pro (8 threads) | 320 ms | 13 sec | -| Amazon Graviton 3 (64 threads) | 390 ms | 3.3 sec | +| Machine | Execution time | Proving time | Execution % | +| ------------------------------ | :------------: | :----------: | :---------: | +| Apple M1 Pro (8 threads) | 310 ms | 9.8 sec | 3.1% | +| Apple M2 Max (16 threads) | 290 ms | 7.7 sec | 3.6% | +| AMD Ryzen 9 5950X (16 threads) | 270 ms | 10.7 sec | 2.6% | +| Amazon Graviton 3 (64 threads) | 330 ms | 3.7 sec | 9.0% | diff --git a/processor/src/advice/mod.rs b/processor/src/advice/mod.rs index d1e058f8e3..af60a53592 100644 --- a/processor/src/advice/mod.rs +++ b/processor/src/advice/mod.rs @@ -19,38 +19,29 @@ pub use source::AdviceSource; // ADVICE PROVIDER // ================================================================================================ -// TODO elaborate on why this is non-deterministic (seems it is a simple state machine). - -// TODO the `advance_clock` seems to be an internal of the VM and shouldn't necessarily be here. - -// TODO this will likely suffer a breaking change as we introduce a generic storage for big Merkle -// sets. -// The purpose of this clock is to keep the feedback of the provided in sync with the execution -// trace of the VM, but on the other hand the VM can control the calls/traces itself, without the -// need for the advice provider (or any other external component) to keep track of it. If we -// delegate this control to the advice provider - especially if it is a trait, the user might -// implement it incorrectly, creating undefined behavior on the VM side (that expects the counter -// to incrementally increase). - -/// Common behavior of advice providers for program execution. +/// Defines behavior of an advice provider. /// -/// An advice provider supplies non-deterministic inputs to the processor. +/// An advice provider is a component through which the VM processor can interact with the host +/// environment. The processor can request nondeterministic inputs from the advice provider (i.e., +/// result of a computation performed outside of the VM), as well as insert new data into the +/// advice provider. /// -/// 1. Provide a stack functionality that yields elements as a stack (last in, first out). These can -/// be yielded as elements, words or double words. -/// 2. Provide a map functionality that will store temporary stacks that can be appended to the main -/// stack. This operation should not allow key overwrite; that is: if a given key exists, the -/// implementation should error if the user attempts to insert this key again, instead of the -/// common behavior of the maps to simply override the previous contents. This is a design -/// decision to increase the runtime robustness of the execution. -/// 3. Provide merkle tree interfaces, backed by a [MerkleStore]. +/// An advice provider consists of the following components: +/// 1. Advice stack, which is a LIFO data structure. The processor can move the elements from the +/// advice stack onto the operand stack, as well as push new elements onto the advice stack. +/// 2. Advice map, which is a key-value map where keys are words (4 field elements) and values are +/// vectors of field elements. The processor can push the values from the map onto the advice +/// stack, as well as insert new values into the map. +/// 3. Merkle store, which contains structured data reducible to Merkle paths. The VM can request +/// Merkle paths from the store, as well as mutate it by updating or merging nodes contained in +/// the store. pub trait AdviceProvider { // ACCESSORS // -------------------------------------------------------------------------------------------- /// Creates a "by reference" advice provider for this instance. /// - /// The returned adapter also implements `AdviceProvider` and will simply mutably borrow this + /// The returned adapter also implements [AdviceProvider] and will simply mutably borrow this /// instance. fn by_ref(&mut self) -> &mut Self { // this trait follows the same model as @@ -74,8 +65,8 @@ pub trait AdviceProvider { /// Pops a word (4 elements) from the advice stack and returns it. /// - /// Note: a word is always stored as little-endian. A `[...,a,b,c,d]` stack will yield - /// `[d,c,b,a]`. + /// Note: a word is popped off the stack element-by-element. For example, a `[d, c, b, a, ...]` + /// stack (i.e., `d` is at the top of the stack) will yield `[d, c, b, a]`. /// /// # Errors /// Returns an error if the advice stack does not contain a full word. @@ -83,18 +74,21 @@ pub trait AdviceProvider { /// Pops a double word (8 elements) from the advice stack and returns them. /// - /// Note: a double word is always stored as little-endian. A `[...,a,b,c,d,e,f,g,h]` stack will - /// yield `[h,g,f,e],[,d,c,b,a]`. + /// Note: words are popped off the stack element-by-element. For example, a + /// `[h, g, f, e, d, c, b, a, ...]` stack (i.e., `h` is at the top of the stack) will yield + /// two words: `[h, g, f,e ], [d, c, b, a]`. /// /// # Errors /// Returns an error if the advice stack does not contain two words. fn pop_stack_dword(&mut self) -> Result<[Word; 2], ExecutionError>; - /// Writes values specified by the source to the head of the advice stack. + /// Pushes the value(s) specified by the source onto the advice stack. fn push_stack(&mut self, source: AdviceSource) -> Result<(), ExecutionError>; - /// Maps a key to a value list to be yielded by `push_stack` with the [AdviceSource::Map] - /// variant. + /// Inserts the provided value into the advice map under the specified key. + /// + /// The values in the advice map can be moved onto the advice stack by invoking + /// [AdviceProvider::push_stack()] method. /// /// # Errors /// Returns an error if the key is already present in the advice map. @@ -103,7 +97,7 @@ pub trait AdviceProvider { // ADVISE SETS // -------------------------------------------------------------------------------------------- - /// Returns a node/leaf for the given depth and index in a Merkle tree with the given root. + /// Returns a node at the specified depth and index in a Merkle tree with the given root. /// /// # Errors /// Returns an error if: @@ -114,7 +108,8 @@ pub trait AdviceProvider { fn get_tree_node(&self, root: Word, depth: &Felt, index: &Felt) -> Result; - /// Returns a path to a node at the specified index in a Merkle tree with the specified root. + /// Returns a path to a node at the specified depth and index in a Merkle tree with the + /// specified root. /// /// # Errors /// Returns an error if: @@ -129,10 +124,11 @@ pub trait AdviceProvider { index: &Felt, ) -> Result; - /// Updates a leaf at the specified index on an existing Merkle tree with the specified root; - /// returns the Merkle path from the updated leaf to the new root. + /// Updates a node at the specified depth and index in a Merkle tree with the specified root; + /// returns the Merkle path from the updated node to the new root. /// - /// Retains both the tree prior to the update, and the new updated tree. + /// The tree is cloned prior to the update. Thus, the advice provider retains the original and + /// the updated tree. /// /// # Errors /// Returns an error if: @@ -167,6 +163,9 @@ pub trait AdviceProvider { /// /// This is used to keep the state of the VM in sync with the state of the advice provider, and /// should be incrementally updated when called. + /// + /// TODO: keeping track of the clock cycle is used primarily for attaching clock cycle to error + /// messages generated by the advice provider; consider refactoring. fn advance_clock(&mut self); } diff --git a/processor/src/advice/source.rs b/processor/src/advice/source.rs index aba33d3eb3..9e7cacb3b0 100644 --- a/processor/src/advice/source.rs +++ b/processor/src/advice/source.rs @@ -3,22 +3,22 @@ use super::{Felt, Word}; // ADVICE SOURCE // ================================================================================================ -/// Placeholder for advice provider stack mutation. +/// Specifies the source of the value(s) to be pushed onto the advice stack. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum AdviceSource { /// Puts a single value onto the advice stack. Value(Felt), /// Fetches a list of elements under the specified key from the advice map and pushes them onto - /// the stack. + /// the advice stack. /// - /// Note: this operation shouldn't consume the map element so it can be called multiple times + /// Note: this operation doesn't consume the map element so it can be called multiple times /// for the same key. /// /// # Example - /// Given an advice stack `[a,b,c]`, and a map `x |-> [d,e,f]`, a call - /// `push_stack(AdviceSource::Map { key: x })` will result in `[a,b,c,f,e,d]` for the advice - /// stack, and will preserve `x |-> [d,e,f]`. + /// Given an advice stack `[a, b, c, ...]`, and a map `x |-> [d, e, f]`, a call + /// `push_stack(AdviceSource::Map { key: x })` will result in `[f, e, d, a, b, c, ...]` for + /// the advice stack, and will preserve `x |-> [d, e, f]` in the advice map. /// /// # Errors /// Returns an error if the key was not found in the key-value map. From d07b180cecba7534968a73476a0696e4f49e9471 Mon Sep 17 00:00:00 2001 From: frisitano Date: Tue, 28 Mar 2023 17:41:44 +0400 Subject: [PATCH 45/47] feat: introduce merkle store input from json for cli --- miden/src/cli/data.rs | 171 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 163 insertions(+), 8 deletions(-) diff --git a/miden/src/cli/data.rs b/miden/src/cli/data.rs index 49204e4926..040b55140f 100644 --- a/miden/src/cli/data.rs +++ b/miden/src/cli/data.rs @@ -1,11 +1,14 @@ use assembly::{Library, MaslLibrary}; use miden::{ + crypto::MerkleStore, + math::Felt, utils::{Deserializable, SliceReader}, AdviceInputs, Assembler, Digest, ExecutionProof, MemAdviceProvider, Program, StackInputs, - StackOutputs, + StackOutputs, Word, }; use serde_derive::{Deserialize, Serialize}; use std::{ + collections::HashMap, fs, io::Write, path::{Path, PathBuf}, @@ -29,15 +32,46 @@ impl Debug { } } +// MERKLE DATA +// ================================================================================================ + +/// Struct used to deserialize merkle data from input file. Merkle data can be represented as a +/// merkle tree or a sparse merkle tree. +#[derive(Deserialize, Debug)] +pub enum MerkleData { + /// String representation of a merkle tree. The merkle tree is represented as a vector of + /// 32 byte hex strings where each string represents a leaf in the tree. + #[serde(rename = "merkle_tree")] + MerkleTree(Vec), + /// String representation of a sparse merkle tree. The sparse merkle tree is represented as a + /// vector of tuples where each tuple consists of a u64 node index and a 32 byte hex string + /// representing the value of the node. + #[serde(rename = "sparse_merkle_tree")] + SparseMerkleTree(Vec<(u64, String)>), +} + // INPUT FILE // ================================================================================================ // TODO consider using final types instead of string representations. -/// Input file struct +/// Input file struct that is used to deserialize input data from file. It consists of four +/// components: +/// - operand_stack +/// - advice_stack +/// - advice_map +/// - merkle_store #[derive(Deserialize, Debug)] pub struct InputFile { + /// String representation of the initial operand stack, composed of chained field elements. pub operand_stack: Vec, + /// Opitonal string representation of the initial advice stack, composed of chained field + /// elements. pub advice_stack: Option>, + /// Optional map of 32 byte hex strings to vectors of u64s representing the initial advice map. + pub advice_map: Option>>, + /// Optional vector of merkle data which will be loaded into the initial merkle store. Merkle + /// data is represented as 32 byte hex strings and node indexes are represented as u64s. + pub merkle_store: Option>, } /// Helper methods to interact with the input file @@ -49,6 +83,8 @@ impl InputFile { return Ok(Self { operand_stack: Vec::new(), advice_stack: Some(Vec::new()), + advice_map: Some(HashMap::new()), + merkle_store: None, }); } @@ -72,18 +108,137 @@ impl InputFile { Ok(inputs) } + /// Parse advice provider data from the input file. pub fn parse_advice_provider(&self) -> Result { + let mut advice_inputs = AdviceInputs::default(); + let stack = self - .advice_stack + .parse_advice_stack() + .map_err(|e| format!("failed to parse advice provider: {e}"))?; + advice_inputs = advice_inputs.with_stack_values(stack).map_err(|e| e.to_string())?; + + if let Some(map) = self + .parse_advice_map() + .map_err(|e| format!("failed to parse advice provider: {e}"))? + { + advice_inputs = advice_inputs.with_map(map); + } + + if let Some(merkle_store) = self + .parse_merkle_store() + .map_err(|e| format!("failed to parse advice provider: {e}"))? + { + advice_inputs = advice_inputs.with_merkle_store(merkle_store); + } + + Ok(MemAdviceProvider::from(advice_inputs)) + } + + /// Parse advice stack data from the input file. + fn parse_advice_stack(&self) -> Result, String> { + self.advice_stack .as_ref() .map(Vec::as_slice) .unwrap_or(&[]) .iter() - .map(|v| v.parse::().map_err(|e| e.to_string())) - .collect::, _>>()?; - let advice_inputs = - AdviceInputs::default().with_stack_values(stack).map_err(|e| e.to_string())?; - Ok(MemAdviceProvider::from(advice_inputs)) + .map(|v| { + v.parse::() + .map_err(|e| format!("failed to parse advice stack value `{v}` - {e}")) + }) + .collect::, _>>() + } + + /// Parse advice map data from the input file. + fn parse_advice_map(&self) -> Result>>, String> { + let advice_map = match &self.advice_map { + Some(advice_map) => advice_map, + None => return Ok(None), + }; + + let map = advice_map + .iter() + .map(|(k, v)| { + // decode hex key + let mut key = [0u8; 32]; + hex::decode_to_slice(k, &mut key) + .map_err(|e| format!("failed to decode advice map key `{k}` - {e}"))?; + + // convert values to Felt + let values = v + .iter() + .map(|v| { + Felt::try_from(*v).map_err(|e| { + format!("failed to convert advice map value `{v}` to Felt - {e}") + }) + }) + .collect::, _>>()?; + Ok((key, values)) + }) + .collect::>, String>>()?; + + Ok(Some(map)) + } + + /// Parse merkle store data from the input file. + fn parse_merkle_store(&self) -> Result, String> { + let merkle_data = match &self.merkle_store { + Some(merkle_data) => merkle_data, + None => return Ok(None), + }; + + let mut merkle_store = MerkleStore::default(); + for data in merkle_data { + match data { + MerkleData::MerkleTree(data) => { + let leaves = Self::parse_merkle_tree(data)?; + merkle_store + .add_merkle_tree(leaves) + .map_err(|e| format!("failed to add merkle tree to merkle store - {e}"))?; + } + MerkleData::SparseMerkleTree(data) => { + let entries = Self::parse_sparse_merkle_tree(data)?; + merkle_store.add_sparse_merkle_tree(entries).map_err(|e| { + format!("failed to add sparse merkle tree to merkle store - {e}") + })?; + } + } + } + + Ok(Some(merkle_store)) + } + + /// Parse and return merkle tree leaves. + fn parse_merkle_tree(tree: &[String]) -> Result, String> { + tree.iter() + .map(|v| { + let leaf = Self::parse_word(v)?; + Ok(leaf) + }) + .collect() + } + + /// Parse and return sparse merkle tree entries. + fn parse_sparse_merkle_tree(tree: &[(u64, String)]) -> Result, String> { + tree.iter() + .map(|(index, v)| { + let leaf = Self::parse_word(v)?; + Ok((*index, leaf)) + }) + .collect() + } + + /// Parse a `Word` from a hex string. + pub fn parse_word(word_hex: &str) -> Result { + let mut word_data = [0u8; 32]; + hex::decode_to_slice(word_hex, &mut word_data) + .map_err(|e| format!("failed to decode `Word` from hex {word_hex} - {e}"))?; + let mut word = Word::default(); + for (i, value) in word_data.chunks(8).enumerate() { + word[i] = Felt::try_from(value).map_err(|e| { + format!("failed to convert `Word` data {word_hex} (element {i}) to Felt - {e}") + })?; + } + Ok(word) } /// Parse and return the stack inputs for the program. From 85e76ce80e05ef2b087e7c217218006d17b51e2c Mon Sep 17 00:00:00 2001 From: frisitano Date: Wed, 29 Mar 2023 15:20:01 +0400 Subject: [PATCH 46/47] fix: fix no_std support --- core/src/random.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/random.rs b/core/src/random.rs index 93abb1e314..bd0982fc96 100644 --- a/core/src/random.rs +++ b/core/src/random.rs @@ -1,5 +1,6 @@ use super::{ crypto::hash::{Rpo256, RpoDigest}, + utils::collections::Vec, Felt, FieldElement, }; From ed73c5d86d17bd5b92fb1907104a6b3b779a95bb Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Wed, 29 Mar 2023 14:39:18 -0700 Subject: [PATCH 47/47] chore: clean up v0.5.0 release --- CHANGELOG.md | 2 +- README.md | 2 +- core/src/utils/mod.rs | 13 ++----------- docs/src/intro/main.md | 2 +- docs/src/intro/usage.md | 2 +- prover/src/lib.rs | 2 +- 6 files changed, 7 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a45ef13ca..bb003e6f19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.5.0 (TBD) +## 0.5.0 (2023-03-29) #### CLI - Renamed `ProgramInfo` to `ExecutionDetails` since there is another `ProgramInfo` struct in the source code. diff --git a/README.md b/README.md index 6d24afaba2..db21a44a8a 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Miden VM is a zero-knowledge virtual machine written in Rust. For any program ex * If you'd like to learn more about STARKs, check out the [references](#references) section. ### Status and features -Miden VM is currently on release v0.4. In this release, most of the core features of the VM have been stabilized, and most of the STARK proof generation has been implemented. While we expect to keep making changes to the VM internals, the external interfaces should remain relatively stable, and we will do our best to minimize the amount of breaking changes going forward. +Miden VM is currently on release v0.5. In this release, most of the core features of the VM have been stabilized, and most of the STARK proof generation has been implemented. While we expect to keep making changes to the VM internals, the external interfaces should remain relatively stable, and we will do our best to minimize the amount of breaking changes going forward. The next version of the VM is being developed in the [next](https://github.com/0xPolygonMiden/miden-vm/tree/next) branch. There is also a documentation for the latest features and changes in the next branch [documentation next branch](https://0xpolygonmiden.github.io/miden-vm/intro/main.html). diff --git a/core/src/utils/mod.rs b/core/src/utils/mod.rs index 75ff870837..6bc11db796 100644 --- a/core/src/utils/mod.rs +++ b/core/src/utils/mod.rs @@ -6,21 +6,12 @@ use core::{ }; use winter_utils::{collections::Vec, string::String}; -// FEATURE BASED RE-EXPORT -// ================================================================================================ - -#[cfg(not(feature = "std"))] -pub use alloc::boxed::Box; - -#[cfg(feature = "std")] -pub use std::boxed::Box; - // RE-EXPORTS // ================================================================================================ pub use winter_utils::{ - collections, group_slice_elements, group_vector_elements, string, uninit_vector, ByteReader, - ByteWriter, Deserializable, DeserializationError, Serializable, SliceReader, + collections, group_slice_elements, group_vector_elements, string, uninit_vector, Box, + ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, SliceReader, }; pub mod math { diff --git a/docs/src/intro/main.md b/docs/src/intro/main.md index 751fcc7fdf..7cd76a9e87 100644 --- a/docs/src/intro/main.md +++ b/docs/src/intro/main.md @@ -2,7 +2,7 @@ Miden VM is a zero-knowledge virtual machine written in Rust. For any program executed on Miden VM, a STARK-based proof of execution is automatically generated. This proof can then be used by anyone to verify that the program was executed correctly without the need for re-executing the program or even knowing the contents of the program. ## Status and features -Miden VM is currently on release v0.4. In this release, most of the core features of the VM have been stabilized, and most of the STARK proof generation has been implemented. While we expect to keep making changes to the VM internals, the external interfaces should remain relatively stable, and we will do our best to minimize the amount of breaking changes going forward. +Miden VM is currently on release v0.5. In this release, most of the core features of the VM have been stabilized, and most of the STARK proof generation has been implemented. While we expect to keep making changes to the VM internals, the external interfaces should remain relatively stable, and we will do our best to minimize the amount of breaking changes going forward. At this point, Miden VM is good enough for experimentation, and even for real-world applications, but it is not yet ready for production use. The codebase has not been audited and contains known and unknown bugs and security flaws. diff --git a/docs/src/intro/usage.md b/docs/src/intro/usage.md index 17054d3be3..acfb3682ac 100644 --- a/docs/src/intro/usage.md +++ b/docs/src/intro/usage.md @@ -1,5 +1,5 @@ # Usage -Before you can use Miden VM, you'll need to make sure you have Rust [installed](https://www.rust-lang.org/tools/install). Miden VM v0.4 requires Rust version **1.67** or later. +Before you can use Miden VM, you'll need to make sure you have Rust [installed](https://www.rust-lang.org/tools/install). Miden VM v0.5 requires Rust version **1.67** or later. Miden VM consists of several crates, each of which exposes a small set of functionality. The most notable of these crates are: * [miden-processor](https://crates.io/crates/miden-processor), which can be used to execute Miden VM programs. diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 8190bd73fd..10cac2b4b1 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -148,7 +148,7 @@ where impl Prover for ExecutionProver where H: ElementHasher, - R: RandomCoin + Sync, + R: RandomCoin, { type Air = ProcessorAir; type BaseField = Felt;