From a57de905c51a800f87d459eb6438187a314d29d1 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 26 Jun 2026 12:12:29 -0300 Subject: [PATCH 01/20] chore: switch back miden deps to git next --- CHANGELOG.md | 4 + Cargo.lock | 519 +++++-------- Cargo.toml | 18 +- .../src/tests/agglayer/mod.rs | 8 +- .../src/tests/network_transaction.rs | 12 +- bin/integration-tests/src/tests/onchain.rs | 35 +- .../src/tests/pass_through.rs | 10 +- bin/miden-cli/src/commands/account.rs | 2 +- bin/miden-cli/src/commands/new_account.rs | 27 +- bin/miden-cli/src/utils.rs | 71 +- bin/miden-cli/tests/cli.rs | 4 +- crates/rust-client/Cargo.toml | 2 +- crates/rust-client/src/account/mod.rs | 23 +- crates/rust-client/src/errors.rs | 6 +- crates/rust-client/src/lib.rs | 4 - .../rust-client/src/rpc/domain/transaction.rs | 23 - .../rust-client/src/store/data_store/mod.rs | 14 +- crates/rust-client/src/store/errors.rs | 3 + crates/rust-client/src/store/mod.rs | 2 +- crates/rust-client/src/store/smt_forest.rs | 18 +- crates/rust-client/src/sync/mod.rs | 2 +- crates/rust-client/src/sync/state_sync.rs | 33 +- .../rust-client/src/sync/state_sync_update.rs | 518 +++++-------- crates/rust-client/src/test_utils/common.rs | 34 +- crates/rust-client/src/test_utils/mock.rs | 40 +- .../rust-client/src/transaction/batch/mod.rs | 21 +- crates/rust-client/src/transaction/mod.rs | 41 +- .../src/transaction/request/builder.rs | 67 +- .../src/transaction/request/mod.rs | 46 +- crates/rust-client/src/transaction/result.rs | 8 +- crates/sqlite-store/src/account/accounts.rs | 54 +- crates/sqlite-store/src/account/mod.rs | 2 +- crates/sqlite-store/src/account/storage.rs | 35 +- crates/sqlite-store/src/account/tests.rs | 733 ++++++++++-------- crates/sqlite-store/src/account/vault.rs | 107 +-- crates/sqlite-store/src/sync.rs | 47 +- crates/sqlite-store/src/transaction.rs | 20 +- .../testing/miden-client-tests/src/tests.rs | 70 +- .../src/tests/transaction.rs | 20 +- .../miden-client-tests/src/tests/transport.rs | 174 ++--- crates/testing/test-node-genesis/Cargo.toml | 2 +- .../testing/test-node-genesis/src/agglayer.rs | 13 +- crates/testing/test-node-genesis/src/lib.rs | 49 +- scripts/start-test-node.sh | 4 +- 44 files changed, 1282 insertions(+), 1663 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85164dfbaf..1365d03013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Changes + +* [BREAKING][rust] Migrated to `miden-protocol` 0.16. Transaction-level fees were removed, block-level `FeeParameters` are unchanged. The relative `AccountDelta` model was replaced by the absolute `AccountPatch` model for account updates: `TransactionResult::account_delta` is now `account_patch`, `AccountUpdateDetails::Public` now carries an `AccountPatch`, account reconstruction is done via `Account::try_from(&AccountPatch)` or `Account::apply_patch` instead of previous `apply_delta`. Re-exports changed accordingly: `AccountStorageDelta` for `AccountStoragePatch`, `StorageMapDelta` for `StorageMapPatch`, and so on. Standards APIs were updated: `AuthMethod` was removed (use the concrete auth components), `create_fungible_faucet` became `create_user_fungible_faucet`. ([#XXXX](https://github.com/0xMiden/rust-sdk/pull/XXXX)). + ### Fixes * [FIX][rust] State sync now range-checks `sync_transactions` records to `(current, chain_tip]`, rejecting out-of-range records that could forge transaction commit heights ([#2252](https://github.com/0xMiden/rust-sdk/pull/2252)). diff --git a/Cargo.lock b/Cargo.lock index 386af0744c..021e16b195 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,7 +64,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -81,7 +81,7 @@ dependencies = [ "proc-macro2", "quote", "sha3 0.11.0", - "syn 2.0.117", + "syn 2.0.118", "syn-solidity", ] @@ -97,7 +97,7 @@ dependencies = [ "macro-string", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "syn-solidity", ] @@ -184,9 +184,9 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" [[package]] name = "ascii-canvas" @@ -226,7 +226,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -328,6 +328,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.13.0" @@ -368,9 +374,9 @@ dependencies = [ [[package]] name = "bon" -version = "3.9.2" +version = "3.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2f04f6fef12d70d42a77b1433c9e0f065238479a6cefc4f5bab105e9873a3c3" +checksum = "a602c73c7b0148ec6d12af6fd5cc7a46e2eacc8878271a999abac56eed12f561" dependencies = [ "bon-macros", "rustversion", @@ -378,9 +384,9 @@ dependencies = [ [[package]] name = "bon-macros" -version = "3.9.2" +version = "3.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d0bd4c2f75335ad98052a37efb54f428b492f64340257143b3429c8a508fa7b" +checksum = "6dee98b0db6a962de883bf5d20362dee4d7ca0d12fe39a7c6c73c844e1cd7c1f" dependencies = [ "darling", "ident_case", @@ -388,18 +394,18 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "bstr" -version = "1.12.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79" dependencies = [ "memchr", "regex-automata", - "serde", + "serde_core", ] [[package]] @@ -431,9 +437,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" [[package]] name = "castaway" @@ -446,9 +452,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.64" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", "jobserver", @@ -541,7 +547,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -694,7 +700,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags", + "bitflags 2.13.0", "crossterm_winapi", "document-features", "parking_lot", @@ -773,7 +779,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -796,7 +802,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -807,7 +813,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -840,6 +846,38 @@ dependencies = [ "deadpool-runtime", ] +[[package]] +name = "defmt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + [[package]] name = "der" version = "0.7.10" @@ -869,7 +907,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.117", + "syn 2.0.118", "unicode-xid", ] @@ -1018,9 +1056,9 @@ dependencies = [ [[package]] name = "env_filter" -version = "1.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" +checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217" dependencies = [ "log", "regex", @@ -1028,9 +1066,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.10" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" +checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6" dependencies = [ "anstream", "anstyle", @@ -1243,7 +1281,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1336,16 +1374,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi 6.0.0", - "wasip2", - "wasip3", "wasm-bindgen", ] @@ -1386,9 +1422,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" dependencies = [ "atomic-waker", "bytes", @@ -1596,12 +1632,6 @@ dependencies = [ "cc", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ident_case" version = "1.0.1" @@ -1622,8 +1652,6 @@ checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown 0.17.1", - "serde", - "serde_core", ] [[package]] @@ -1670,10 +1698,11 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.28" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102" +checksum = "34f877a98676d2fb664698d74cc6a51ce6c484ce8c770f05d0108ec9090aeb46" dependencies = [ + "defmt", "jiff-static", "log", "portable-atomic", @@ -1683,13 +1712,13 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.28" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47" +checksum = "0666b5ab5ecaca213fc2a85b8c0083d9004e84ee2d5f9a7e0017aaf50986f25f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1704,9 +1733,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.100" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", @@ -1782,12 +1811,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libc" version = "0.2.186" @@ -1843,9 +1866,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "logos" @@ -1878,7 +1901,7 @@ dependencies = [ "quote", "regex-syntax", "rustc_version 0.4.1", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1892,7 +1915,7 @@ dependencies = [ "quote", "regex-automata", "regex-syntax", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1934,7 +1957,7 @@ checksum = "59a9dbbfc75d2688ed057456ce8a3ee3f48d12eec09229f560f3643b9f275653" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1954,9 +1977,9 @@ checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "miden-ace-codegen" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b5ca2274961cd9d366f39a18043a8e652d5f330a2b6c2081be242d483cbfa79" +checksum = "5fd45076fe4fef71f0f8b30aa0f018eb39c3086eeb5f3cafc0e12d60cd28339e" dependencies = [ "miden-core", "miden-crypto", @@ -1965,9 +1988,8 @@ dependencies = [ [[package]] name = "miden-agglayer" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ead17cc16651de0fea5fc3ea67109ad449c7bb274ca8ff91c5b25e2be4a0c9f8" +version = "0.16.0" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#2547c0e425f4ab78295850cc97df81be4ce44848" dependencies = [ "alloy-sol-types", "fs-err", @@ -1987,9 +2009,9 @@ dependencies = [ [[package]] name = "miden-air" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c3e5695f4c72322fc7ef57d4d7ccec0eb5c21ef6aa28fe1f4f4712966981377" +checksum = "69f1a80330b3e3d3f98e08817dc6a5e3d90d11ab5e88aa9c0dad5d3b4202598b" dependencies = [ "miden-ace-codegen", "miden-core", @@ -2003,9 +2025,9 @@ dependencies = [ [[package]] name = "miden-assembly" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562f777b4432663c83a110f3f5248e76f3ee837ee166bb4ffb1236e7881e0c4a" +checksum = "8582d184360be35eb2111a99245f556f43e1066ed09192fbcd0f218c466862a5" dependencies = [ "env_logger", "log", @@ -2021,9 +2043,9 @@ dependencies = [ [[package]] name = "miden-assembly-syntax" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a037e7c88430d53a9411764985a7cf26dc5121c7b76c5fa1d9bb465db1c5248" +checksum = "ffa307bc2cbd1f0cb74ed58981823f400a433900fb8f963762331fbb8389d5dc" dependencies = [ "aho-corasick", "env_logger", @@ -2046,9 +2068,8 @@ dependencies = [ [[package]] name = "miden-block-prover" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "292ded918a0ddd056dc34db471f0bef6ac7991467d513ba505a4fcc0b1ecfac4" +version = "0.16.0" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#2547c0e425f4ab78295850cc97df81be4ce44848" dependencies = [ "miden-protocol", "thiserror", @@ -2075,7 +2096,7 @@ dependencies = [ "miden-standards", "miden-testing", "miden-tx", - "miden-tx-batch-prover", + "miden-tx-batch", "miette", "prost", "prost-types", @@ -2194,9 +2215,9 @@ dependencies = [ [[package]] name = "miden-core" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b5ef72b3b09aeffe6738c7105bc9ce5dd4517f64063c9af6f16aececce0174" +checksum = "80657c32850817f5f67dcf114866495a4b055531778b7c26d0602646ce777eb8" dependencies = [ "derive_more", "log", @@ -2216,9 +2237,9 @@ dependencies = [ [[package]] name = "miden-core-lib" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b01f5fe14742432014d7dcd5410f6984b9dbd4697b2efbebcdfbc29a032eb720" +checksum = "16410655f32f98537afc9ddf57b71cb6d1ca9d980da5f4eea164cafcaf891b2e" dependencies = [ "env_logger", "fs-err", @@ -2282,7 +2303,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9068c6554db0e051f62913575de9949841a46b96ae92d4b7d28e1fed5d8f052b" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2346,9 +2367,9 @@ dependencies = [ [[package]] name = "miden-debug-types" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ca69684c7885ed3e3e1f2021aab20a35eb088f300272f42da4d93ce87007e3" +checksum = "956708ccb2f643db398b4b3d4f8d0baf199b1bfb5e34c8be1cd1bc811c005e8e" dependencies = [ "memchr", "miden-crypto", @@ -2429,9 +2450,9 @@ dependencies = [ [[package]] name = "miden-mast-package" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdd7b91f6ab27dba6b6c0a8b9e715610403152d5662f83a569f8da92278a8b6" +checksum = "f37c21836b40785ce297d363c57740d4e33edcc411f84185a524eddadd5f53c7" dependencies = [ "miden-assembly-syntax", "miden-core", @@ -2458,7 +2479,7 @@ dependencies = [ "serde_json", "spin 0.9.8", "strip-ansi-escapes", - "syn 2.0.117", + "syn 2.0.118", "textwrap", "thiserror", "trybuild", @@ -2473,14 +2494,13 @@ checksum = "86a905f3ea65634dd4d1041a4f0fd0a3e77aa4118341d265af1a94339182222f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "miden-node-proto-build" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef3b301741cedd6d0b532583690bc21dbf856d4e14218c591f3924bc905c660a" +source = "git+https://github.com/0xMiden/miden-node.git?branch=jmunoz-update-miden-protocol#1c67505215a61b4acf1a542f711e4500910c89a1" dependencies = [ "build-rs", "codegen", @@ -2504,9 +2524,9 @@ dependencies = [ [[package]] name = "miden-package-registry" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b27f25b62ae15b22f7990dff37aa58e927f3c219d53aefa618636825b083c86" +checksum = "9ece6064beb0582d1c64ba30d0c548c4b7a45f87abae6d69e22fd49c8b343258" dependencies = [ "miden-assembly-syntax", "miden-core", @@ -2520,9 +2540,9 @@ dependencies = [ [[package]] name = "miden-processor" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde1a42df6d58960389a34a0b4334f0bb6e241fa0727a7889d13ae19a68b6ce9" +checksum = "ea972ca9e45dbf26aa396367e8508db0f7292adea6f6ddf8d39d0e334285fe2b" dependencies = [ "itertools", "miden-air", @@ -2538,9 +2558,9 @@ dependencies = [ [[package]] name = "miden-project" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138ca59f6d557f13feb9efb46e19e6f76ba148fecb7f51c289ff960b7973ec9c" +checksum = "5320e7e5b562359bd6161ac752dfe43dd4f69bb06e87f94a21a27bb656e5a20d" dependencies = [ "miden-assembly-syntax", "miden-core", @@ -2555,9 +2575,8 @@ dependencies = [ [[package]] name = "miden-protocol" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66340243e37da5936cb278a8dd11037813f1dc6731c2fc866703b76ed465ebc3" +version = "0.16.0" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#2547c0e425f4ab78295850cc97df81be4ce44848" dependencies = [ "bech32", "fs-err", @@ -2585,9 +2604,9 @@ dependencies = [ [[package]] name = "miden-prover" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea0da172763e89e04e33f904314a4788ff3b685d3c0b723190df522edd648af" +checksum = "1a91bcc00840b01126cd54e3b68ce3235def2ed48803f2eeb36a035d6951fbc1" dependencies = [ "bincode", "miden-air", @@ -2601,12 +2620,11 @@ dependencies = [ [[package]] name = "miden-remote-prover-client" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2acdb9494689feeec0f60e3b61901b5eb2362b49b993b4cbc3eb75ad83514dd" +source = "git+https://github.com/0xMiden/miden-node.git?branch=jmunoz-update-miden-protocol#1c67505215a61b4acf1a542f711e4500910c89a1" dependencies = [ "build-rs", "fs-err", - "getrandom 0.4.2", + "getrandom 0.4.3", "miden-node-proto-build", "miden-protocol", "miden-tx", @@ -2632,9 +2650,8 @@ dependencies = [ [[package]] name = "miden-standards" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c7146b028e637f4079b5bdefeefc54d7d6e47a805451fa0a18859d19efa2ff" +version = "0.16.0" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#2547c0e425f4ab78295850cc97df81be4ce44848" dependencies = [ "bon", "fs-err", @@ -2671,9 +2688,8 @@ dependencies = [ [[package]] name = "miden-testing" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4096fc44a4c88f37405be284e25efdce194d6051e9472ae136ab9249659433c" +version = "0.16.0" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#2547c0e425f4ab78295850cc97df81be4ce44848" dependencies = [ "anyhow", "itertools", @@ -2684,7 +2700,7 @@ dependencies = [ "miden-protocol", "miden-standards", "miden-tx", - "miden-tx-batch-prover", + "miden-tx-batch", "rand 0.9.4", "rand_chacha", "thiserror", @@ -2692,33 +2708,33 @@ dependencies = [ [[package]] name = "miden-tx" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94092b45bc0abc656af25473c9807d1e6cee8e682c58d3b1186f0bd0fb6471fb" +version = "0.16.0" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#2547c0e425f4ab78295850cc97df81be4ce44848" dependencies = [ "miden-processor", "miden-protocol", "miden-prover", "miden-standards", - "miden-verifier", "thiserror", ] [[package]] -name = "miden-tx-batch-prover" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60add2b40559352661bc86970541f88ffcf98c528f301295f9dc3bf15481b46" +name = "miden-tx-batch" +version = "0.16.0" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#2547c0e425f4ab78295850cc97df81be4ce44848" dependencies = [ + "miden-processor", "miden-protocol", - "miden-tx", + "miden-prover", + "miden-verifier", + "thiserror", ] [[package]] name = "miden-utils-core-derive" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67bb8353ddd887582932e0e27f98c7f8023b75d0ba1a627fa8e52929fa4413" +checksum = "d0b1ee4662beb049a824e11bb21f95a79746c52874967983c9999f1b19a2f471" dependencies = [ "proc-macro2", "quote", @@ -2727,9 +2743,9 @@ dependencies = [ [[package]] name = "miden-utils-diagnostics" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b89c4ead5b59d4072af3c4f66123f89195916280e7d1abfa374369f2fb684cb8" +checksum = "9fdc1cd4eda372e1c4b99b9c3677e9b1f87a4d2e362a9f4b8f904273d395efc9" dependencies = [ "miden-crypto", "miden-debug-types", @@ -2739,9 +2755,9 @@ dependencies = [ [[package]] name = "miden-utils-indexing" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efe6a82dead13b953510f0118b757ddc5c63a6c47a664651533bc4e38eaf9862" +checksum = "c31444125649f4dad9cde647f614309b6be4f918fed276ada4eb99c01e8b9ca7" dependencies = [ "miden-crypto", "proptest", @@ -2751,9 +2767,9 @@ dependencies = [ [[package]] name = "miden-utils-sync" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c61acb01578993fffbd56fdd24673266350fc5183dbb41bb78b278f1da77668" +checksum = "807c8ae625b7652ae7246b225c907c05da72927c31a0fd71c835c4f80931e92e" dependencies = [ "lock_api", "loom", @@ -2763,9 +2779,9 @@ dependencies = [ [[package]] name = "miden-verifier" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c941890d3e5ef1ce1c4e3801489b5defea2a7d2f4631c2fecd2b30d5850ec4df" +checksum = "ec5556dac919a1c13edeb2bd7181fc6a4c2ce52764a3e518bcdcd9ed48e5b38e" dependencies = [ "bincode", "miden-air", @@ -2817,7 +2833,7 @@ checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2917,7 +2933,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3275,7 +3291,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3331,7 +3347,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3434,7 +3450,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3486,7 +3502,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3506,7 +3522,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "version_check", "yansi", ] @@ -3517,7 +3533,7 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ - "bitflags", + "bitflags 2.13.0", "num-traits", "rand 0.9.4", "rand_chacha", @@ -3534,7 +3550,7 @@ checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3564,7 +3580,7 @@ dependencies = [ "pulldown-cmark", "pulldown-cmark-to-cmark", "regex", - "syn 2.0.117", + "syn 2.0.118", "tempfile", ] @@ -3578,7 +3594,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3649,7 +3665,7 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" dependencies = [ - "bitflags", + "bitflags 2.13.0", "memchr", "unicase", ] @@ -3665,9 +3681,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -3799,7 +3815,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.13.0", ] [[package]] @@ -3898,7 +3914,7 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.1", - "syn 2.0.117", + "syn 2.0.118", "unicode-ident", ] @@ -3929,7 +3945,7 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" dependencies = [ - "bitflags", + "bitflags 2.13.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -3983,7 +3999,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags", + "bitflags 2.13.0", "errno", "libc", "linux-raw-sys", @@ -3992,9 +4008,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.40" +version = "0.23.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" dependencies = [ "log", "once_cell", @@ -4099,7 +4115,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags", + "bitflags 2.13.0", "core-foundation", "core-foundation-sys", "libc", @@ -4180,7 +4196,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4204,7 +4220,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4248,7 +4264,7 @@ checksum = "94e153fc76e1c6a068703d6d29c508a0b15c061c4b7e43da59cc097bc342673c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4330,9 +4346,9 @@ dependencies = [ [[package]] name = "smawk" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +checksum = "e8e2fb0f499abb4d162f2bedad68f5ef91a1682b5a03596ddb67efd37768d100" [[package]] name = "socket2" @@ -4451,9 +4467,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -4469,7 +4485,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4491,7 +4507,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys 0.61.2", @@ -4571,7 +4587,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4615,7 +4631,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4808,7 +4824,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4846,7 +4862,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.117", + "syn 2.0.118", "tempfile", "tonic-build", ] @@ -4926,7 +4942,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4999,9 +5015,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c635f0191bd3a2941013e5062667100969f8c4e9cd787c14f977265d73616e" +checksum = "0710d4dfbeae4f9c390baa784c49858a7468fa433f3fe5d0ec5ebef651cf59f9" dependencies = [ "dissimilar", "glob", @@ -5118,11 +5134,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.3" +version = "1.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.4.3", "js-sys", "serde_core", "wasm-bindgen", @@ -5204,23 +5220,14 @@ version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.123" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -5231,9 +5238,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.73" +version = "0.4.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54568702fabf5d4849ce2b90fadfa64168a097eaf4b351ce9df8b687a0086aaf" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" dependencies = [ "js-sys", "wasm-bindgen", @@ -5241,9 +5248,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.123" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5251,48 +5258,26 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.123" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.123" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - [[package]] name = "wasm-streams" version = "0.5.0" @@ -5306,23 +5291,11 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver 1.0.28", -] - [[package]] name = "web-sys" -version = "0.3.100" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0871acf327f283dc6da28a1696cdc64fb355ba9f935d052021fa77f35cce69" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" dependencies = [ "js-sys", "wasm-bindgen", @@ -5380,7 +5353,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5391,7 +5364,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5518,100 +5491,12 @@ dependencies = [ "memchr", ] -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver 1.0.28", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "x25519-dalek" version = "2.0.1" @@ -5645,7 +5530,7 @@ checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 677a41cf34..4c4dad4219 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,17 +34,19 @@ miden-client = { default-features = false, path = "crates/rust-clie miden-client-sqlite-store = { default-features = false, path = "crates/sqlite-store", version = "0.15.2" } # Miden protocol dependencies -miden-agglayer = { default-features = false, version = "0.15.3" } -miden-protocol = { default-features = false, version = "0.15.3" } -miden-standards = { default-features = false, version = "0.15.3" } -miden-testing = { default-features = false, version = "0.15.3" } -miden-tx = { default-features = false, version = "0.15.3" } -miden-tx-batch-prover = { default-features = false, version = "0.15.3" } +miden-agglayer = { branch = "next", default-features = false, git = "https://github.com/0xMiden/protocol.git" } +miden-protocol = { branch = "next", default-features = false, git = "https://github.com/0xMiden/protocol.git" } +miden-standards = { branch = "next", default-features = false, git = "https://github.com/0xMiden/protocol.git" } +miden-testing = { branch = "next", default-features = false, git = "https://github.com/0xMiden/protocol.git" } +miden-tx = { branch = "next", default-features = false, git = "https://github.com/0xMiden/protocol.git" } +miden-tx-batch = { branch = "next", default-features = false, git = "https://github.com/0xMiden/protocol.git" } # Miden node dependencies -miden-node-proto-build = { default-features = false, version = "0.15.0" } +miden-node-proto-build = { branch = "jmunoz-update-miden-protocol", default-features = false, git = "https://github.com/0xMiden/miden-node.git" } miden-note-transport-proto-build = { default-features = false, version = "0.4.1" } -miden-remote-prover-client = { default-features = false, features = ["tx-prover"], version = "0.15.0" } +miden-remote-prover-client = { branch = "jmunoz-update-miden-protocol", default-features = false, features = [ + "tx-prover", +], git = "https://github.com/0xMiden/miden-node.git" } # Miden debug dependency miden-debug = { default-features = false, features = ["dap", "std"], version = "0.8" } diff --git a/bin/integration-tests/src/tests/agglayer/mod.rs b/bin/integration-tests/src/tests/agglayer/mod.rs index 75b1b65574..0fd039990b 100644 --- a/bin/integration-tests/src/tests/agglayer/mod.rs +++ b/bin/integration-tests/src/tests/agglayer/mod.rs @@ -22,12 +22,6 @@ pub mod agglayer_bridge_in_out; mod agglayer_test_utils; pub mod ger; -/// `AggLayer` network ID assigned to the Miden chain (the protocol's `MIDEN_NETWORK_ID` MASM -/// constant). Claim validation compares the leaf's `destination_network` to this value, so it -/// must match the `MIDEN_NETWORK_ID` constant in the foundry vectors -/// (`foundry-vectors/test/ClaimAssetTestVectorsLocalTx.t.sol`). -pub const MIDEN_AGGLAYER_NETWORK_ID: u32 = 77; - // AGGLAYER CONFIG // ================================================================================================ @@ -227,7 +221,7 @@ pub async fn setup_core_accounts( bridge_seed, bridge_admin_account.id(), ger_manager_account.id(), - MIDEN_AGGLAYER_NETWORK_ID, + ger_manager_account.id(), ); println!("[setup] bridge admin: {}", bridge_admin_account.id()); println!("[setup] GER manager: {}", ger_manager_account.id()); diff --git a/bin/integration-tests/src/tests/network_transaction.rs b/bin/integration-tests/src/tests/network_transaction.rs index ba93f53a23..0f3550e6a9 100644 --- a/bin/integration-tests/src/tests/network_transaction.rs +++ b/bin/integration-tests/src/tests/network_transaction.rs @@ -8,11 +8,10 @@ use miden_client::account::component::{ AccountComponent, AccountComponentMetadata, AuthNetworkAccount, - BurnPolicyConfig, + BurnPolicy, FungibleFaucet, - MintPolicyConfig, + MintPolicy, PausableManager, - PolicyRegistration, TokenName, TokenPolicyManager, }; @@ -268,9 +267,10 @@ async fn deploy_network_fungible_faucet( .max_supply(AssetAmount::new(9_999_999)?) .build() .map_err(|e| anyhow!("failed to build fungible faucet component: {e}"))?; - let policy_manager = TokenPolicyManager::new() - .with_mint_policy(MintPolicyConfig::OwnerOnly, PolicyRegistration::Active)? - .with_burn_policy(BurnPolicyConfig::AllowAll, PolicyRegistration::Active)?; + let policy_manager = TokenPolicyManager::builder() + .active_mint_policy(MintPolicy::owner_only()) + .active_burn_policy(BurnPolicy::allow_all()) + .build(); let faucet = AccountBuilder::new(init_seed) .account_type(AccountType::Public) .with_auth_component(network_auth) diff --git a/bin/integration-tests/src/tests/onchain.rs b/bin/integration-tests/src/tests/onchain.rs index 65f6049869..11f1ea5238 100644 --- a/bin/integration-tests/src/tests/onchain.rs +++ b/bin/integration-tests/src/tests/onchain.rs @@ -7,6 +7,7 @@ use miden_client::auth::RPO_FALCON_SCHEME_ID; use miden_client::keystore::Keystore; use miden_client::note::{ BlockNumber, + Note, NoteAttachment, NoteAttachmentScheme, NoteAttachments, @@ -758,22 +759,24 @@ pub async fn test_sync_note_with_attachment(client_config: ClientConfig) -> Resu )])?; let asset = FungibleAsset::new(faucet_account.id(), MINT_AMOUNT)?; - let public_note = P2idNote::create( - faucet_account.id(), - wallet.id(), - vec![asset.into()], - NoteType::Public, - public_attachments, - client_1.rng(), - )?; - let private_note = P2idNote::create( - faucet_account.id(), - wallet.id(), - vec![asset.into()], - NoteType::Private, - private_attachments, - client_1.rng(), - )?; + let public_note: Note = P2idNote::builder() + .sender(faucet_account.id()) + .target(wallet.id()) + .asset(asset) + .note_type(NoteType::Public) + .attachments(public_attachments.into_vec()) + .generate_serial_number(client_1.rng()) + .build()? + .into(); + let private_note: Note = P2idNote::builder() + .sender(faucet_account.id()) + .target(wallet.id()) + .asset(asset) + .note_type(NoteType::Private) + .attachments(private_attachments.into_vec()) + .generate_serial_number(client_1.rng()) + .build()? + .into(); info!(public = %public_note.id(), private = %private_note.id(), "Minting P2ID notes with attachments"); let tx_request = TransactionRequestBuilder::new() diff --git a/bin/integration-tests/src/tests/pass_through.rs b/bin/integration-tests/src/tests/pass_through.rs index 57310e2985..5e34dfa759 100644 --- a/bin/integration-tests/src/tests/pass_through.rs +++ b/bin/integration-tests/src/tests/pass_through.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeSet; + use anyhow::Result; use miden_client::account::component::BasicWallet; use miden_client::account::{ @@ -205,14 +207,10 @@ async fn create_pass_through_account( let key_pair = SecretKey::with_rng(client.rng()); let pub_key = key_pair.public_key().to_commitment(); - let acl_config = AuthSingleSigAclConfig::new() - .with_allow_unauthorized_input_notes(true) - .with_allow_unauthorized_output_notes(true); + let acl_config = AuthSingleSigAclConfig::new(BTreeSet::new()).unwrap(); let auth_component = - AuthSingleSigAcl::new(pub_key.into(), AuthSchemeId::Falcon512Poseidon2, acl_config) - .unwrap(); - + AuthSingleSigAcl::new(pub_key.into(), AuthSchemeId::Falcon512Poseidon2, acl_config); let account = AccountBuilder::new(init_seed) .account_type(AccountType::Private) .with_auth_component(auth_component) diff --git a/bin/miden-cli/src/commands/account.rs b/bin/miden-cli/src/commands/account.rs index 692efa5268..f8d877daaf 100644 --- a/bin/miden-cli/src/commands/account.rs +++ b/bin/miden-cli/src/commands/account.rs @@ -318,7 +318,7 @@ fn account_kind_display_name(token_symbol: Option<&str>) -> String { /// Takes the [`AccountCode`] rather than the full [`Account`] so callers can avoid loading the /// account's vault and storage just to inspect its interface. pub(crate) fn account_code_has_basic_wallet(account_id: AccountId, code: &AccountCode) -> bool { - AccountInterface::from_code(account_id, Vec::new(), code) + AccountInterface::from_code(account_id, code) .components() .iter() .any(|c| matches!(c, AccountComponentInterface::BasicWallet)) diff --git a/bin/miden-cli/src/commands/new_account.rs b/bin/miden-cli/src/commands/new_account.rs index d4b4151c5f..0bb7ea0ee8 100644 --- a/bin/miden-cli/src/commands/new_account.rs +++ b/bin/miden-cli/src/commands/new_account.rs @@ -9,12 +9,11 @@ use miden_client::Client; use miden_client::account::component::{ AccountComponent, AccountComponentMetadata, - BurnPolicyConfig, + BurnPolicy, FungibleFaucet, InitStorageData, MIDEN_PACKAGE_EXTENSION, - MintPolicyConfig, - PolicyRegistration, + MintPolicy, StorageSlotSchema, TokenName, TokenPolicyManager, @@ -529,15 +528,10 @@ async fn create_client_account( // default `allow_all` policy manager implicitly. if should_add_implicit_token_policy_manager(®ular_components) { debug!("Adding implicit TokenPolicyManager component for fungible faucet"); - let policy_manager = TokenPolicyManager::new() - .with_mint_policy(MintPolicyConfig::AllowAll, PolicyRegistration::Active) - .map_err(|err| { - CliError::Faucet(err.into(), "Failed to register mint policy".to_string()) - })? - .with_burn_policy(BurnPolicyConfig::AllowAll, PolicyRegistration::Active) - .map_err(|err| { - CliError::Faucet(err.into(), "Failed to register burn policy".to_string()) - })?; + let policy_manager = TokenPolicyManager::builder() + .active_mint_policy(MintPolicy::allow_all()) + .active_burn_policy(BurnPolicy::allow_all()) + .build(); regular_components.extend(policy_manager); } // Add the auth component (either from packages or default Falcon) @@ -717,11 +711,10 @@ mod tests { #[test] fn implicit_token_policy_manager_is_skipped_when_component_already_present() { let mut regular_components: Vec = vec![test_fungible_faucet_component()]; - let policy_manager = TokenPolicyManager::new() - .with_mint_policy(MintPolicyConfig::AllowAll, PolicyRegistration::Active) - .unwrap() - .with_burn_policy(BurnPolicyConfig::AllowAll, PolicyRegistration::Active) - .unwrap(); + let policy_manager = TokenPolicyManager::builder() + .active_mint_policy(MintPolicy::allow_all()) + .active_burn_policy(BurnPolicy::allow_all()) + .build(); regular_components.extend(policy_manager); assert!(!should_add_implicit_token_policy_manager(®ular_components)); diff --git a/bin/miden-cli/src/utils.rs b/bin/miden-cli/src/utils.rs index c4bc5de4ce..29b499479a 100644 --- a/bin/miden-cli/src/utils.rs +++ b/bin/miden-cli/src/utils.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use miden_client::account::{AccountId, FaucetMetadata}; use miden_client::address::{Address, AddressId}; -use miden_client::asset::{FungibleAsset, NonFungibleDeltaAction}; +use miden_client::asset::{Asset, FungibleAsset}; use miden_client::transaction::{ExecutedTransaction, InputNote}; use miden_client::utils::{base_units_to_tokens, tokens_to_base_units}; use miden_client::vm::MIN_STACK_DEPTH; @@ -108,7 +108,7 @@ pub async fn print_executed_transaction( ) -> Result<(), CliError> { println!("The transaction will have the following effects:\n"); - let delta = executed_tx.account_delta(); + let patch = executed_tx.account_patch(); // INPUT NOTES let input_note_ids = executed_tx.input_notes().iter().map(InputNote::id).collect::>(); @@ -135,10 +135,10 @@ pub async fn print_executed_transaction( println!(); // STORAGE VALUES - if delta.storage().values().next().is_some() { - let mut table = create_dynamic_table(&["Storage Slot", "Effect"]); - for (slot, new_value) in delta.storage().values() { - table.add_row(vec![slot.to_string(), format!("Updated ({})", new_value.to_hex())]); + if patch.storage().values().next().is_some() { + let mut table = create_dynamic_table(&["Storage Slot", "New Value"]); + for (slot, new_value) in patch.storage().values() { + table.add_row(vec![slot.to_string(), new_value.to_hex()]); } println!("Storage changes:"); println!("{table}"); @@ -147,10 +147,10 @@ pub async fn print_executed_transaction( } // STORAGE MAPS - if delta.storage().maps().next().is_some() { + if patch.storage().maps().next().is_some() { let mut table = create_dynamic_table(&["Storage Slot", "Map Key", "New Value"]); - for (slot, map_delta) in delta.storage().maps() { - for (key, value) in map_delta.entries() { + for (slot, map_patch) in patch.storage().maps() { + for (key, value) in map_patch.entries() { table.add_row(vec![slot.to_string(), Word::from(*key).to_hex(), value.to_hex()]); } } @@ -159,49 +159,48 @@ pub async fn print_executed_transaction( } // VAULT - if delta.vault().is_empty() { + // The patch carries the new absolute value of each changed asset, cleared entries are listed as + // removed. + if patch.vault().is_empty() { println!("Account Vault will not be changed."); } else { let resolver = load_faucet_metadata_resolver()?; - let mut table = create_dynamic_table(&["Asset Type", "Faucet ID", "Amount"]); - - for (vault_key, amount) in delta.vault().fungible().iter() { - let asset = FungibleAsset::new(vault_key.faucet_id(), amount.unsigned_abs()) - .map_err(CliError::Asset)?; - let (faucet_fmt, amount_fmt) = resolver.format_fungible_asset(client, &asset).await?; - - if amount.is_positive() { - table.add_row(vec!["Fungible Asset", &faucet_fmt, &format!("+{amount_fmt}")]); - } else { - table.add_row(vec!["Fungible Asset", &faucet_fmt, &format!("-{amount_fmt}")]); - } - } - - for (asset, action) in delta.vault().non_fungible().iter() { - match action { - NonFungibleDeltaAction::Add => { - table.add_row(vec![ - "Non Fungible Asset", - &asset.faucet_id().prefix().to_hex(), - "1", - ]); + let mut table = create_dynamic_table(&["Asset Type", "Faucet ID", "New Amount"]); + + for asset in patch.vault().updated_assets() { + match asset { + Asset::Fungible(fungible) => { + let (faucet_fmt, amount_fmt) = + resolver.format_fungible_asset(client, &fungible).await?; + table.add_row(vec!["Fungible Asset", &faucet_fmt, &amount_fmt]); }, - NonFungibleDeltaAction::Remove => { + Asset::NonFungible(non_fungible) => { table.add_row(vec![ "Non Fungible Asset", - &asset.faucet_id().prefix().to_hex(), - "-1", + &non_fungible.faucet_id().prefix().to_hex(), + "1", ]); }, } } + for vault_key in patch.vault().removed_asset_keys() { + table.add_row(vec![ + "Removed Asset", + &vault_key.faucet_id().prefix().to_hex(), + "removed", + ]); + } + println!("Vault changes:"); println!("{table}"); } // NONCE - println!("Nonce incremented by: {}.", delta.nonce_delta()); + match patch.final_nonce() { + Some(nonce) => println!("New account nonce: {nonce}."), + None => println!("Account nonce will not be changed."), + } Ok(()) } diff --git a/bin/miden-cli/tests/cli.rs b/bin/miden-cli/tests/cli.rs index c21bd805e9..75a3c33cc7 100644 --- a/bin/miden-cli/tests/cli.rs +++ b/bin/miden-cli/tests/cli.rs @@ -1838,8 +1838,8 @@ fn call_shows_nonce_delta() { let stdout = String::from_utf8_lossy(&output.stdout); assert!( - stdout.contains("Nonce incremented by:"), - "Expected nonce delta in output:\n{stdout}" + stdout.contains("New account nonce:"), + "Expected the new account nonce in output:\n{stdout}" ); } diff --git a/crates/rust-client/Cargo.toml b/crates/rust-client/Cargo.toml index f64fa4a614..a15b989879 100644 --- a/crates/rust-client/Cargo.toml +++ b/crates/rust-client/Cargo.toml @@ -82,7 +82,7 @@ miden-remote-prover-client = { default-features = false, features = ["tx-prover" miden-standards = { workspace = true } miden-testing = { optional = true, workspace = true } miden-tx = { workspace = true } -miden-tx-batch-prover = { workspace = true } +miden-tx-batch = { workspace = true } # External dependencies anyhow = { workspace = true } diff --git a/crates/rust-client/src/account/mod.rs b/crates/rust-client/src/account/mod.rs index c789bb9778..61816268b2 100644 --- a/crates/rust-client/src/account/mod.rs +++ b/crates/rust-client/src/account/mod.rs @@ -37,7 +37,6 @@ use alloc::vec::Vec; use miden_protocol::Felt; use miden_protocol::account::auth::PublicKey; -pub use miden_protocol::account::delta::AccountUpdateDetails; pub use miden_protocol::account::{ Account, AccountBuilder, @@ -52,23 +51,27 @@ pub use miden_protocol::account::{ AccountIdPrefixV1, AccountIdV1, AccountIdVersion, + AccountPatch, AccountProcedureRoot, AccountStorage, + AccountStoragePatch, AccountType, + AccountUpdateDetails, + AccountVaultPatch, PartialAccount, PartialStorage, PartialStorageMap, RoleSymbol, StorageMap, - StorageMapDelta, StorageMapKey, StorageMapKeyHash, + StorageMapPatch, StorageMapWitness, StorageSlot, StorageSlotContent, - StorageSlotDelta, StorageSlotId, StorageSlotName, + StorageSlotPatch, StorageSlotType, }; pub use miden_protocol::address::{Address, AddressInterface, AddressType, NetworkId}; @@ -186,7 +189,8 @@ pub mod component { TokenMetadata, TokenMetadataError, TokenName, - create_fungible_faucet, + create_network_fungible_faucet, + create_user_fungible_faucet, }; pub use miden_standards::account::policies::{ AllowlistOwnerControlled, @@ -197,15 +201,18 @@ pub mod component { BlocklistStorage, BurnAllowAll, BurnOwnerOnly, - BurnPolicyConfig, + BurnPolicy, + BurnPolicyError, + MinBurnAmount, MintAllowAll, MintOwnerOnly, - MintPolicyConfig, - PolicyRegistration, + MintPolicy, + MintPolicyError, TokenPolicyManager, - TokenPolicyManagerError, + TokenPolicyManagerBuilder, TransferAllowAll, TransferPolicy, + TransferPolicyError, }; pub use miden_standards::account::wallets::BasicWallet; } diff --git a/crates/rust-client/src/errors.rs b/crates/rust-client/src/errors.rs index 9866ccac9a..21a4c5a922 100644 --- a/crates/rust-client/src/errors.rs +++ b/crates/rust-client/src/errors.rs @@ -16,10 +16,10 @@ use miden_protocol::errors::{ TransactionScriptError, }; use miden_protocol::note::NoteId; -use miden_standards::account::interface::AccountInterfaceError; // RE-EXPORTS // ================================================================================================ pub use miden_standards::errors::CodeBuilderError; +use miden_standards::tx_script::SendNotesTransactionScriptError; pub use miden_tx::AuthenticationError; use miden_tx::utils::HexParseError; use miden_tx::utils::serde::DeserializationError; @@ -166,8 +166,8 @@ pub enum ClientError { TransactionProvingError(#[from] TransactionProverError), #[error("invalid transaction request")] TransactionRequestError(#[from] TransactionRequestError), - #[error("failed to build transaction script from account interface")] - AccountInterfaceError(#[from] AccountInterfaceError), + #[error("failed to build the send-notes transaction script")] + SendNotesTransactionScriptError(#[from] SendNotesTransactionScriptError), #[error("transaction script error")] TransactionScriptError(#[source] TransactionScriptError), #[error("client initialization error: {0}")] diff --git a/crates/rust-client/src/lib.rs b/crates/rust-client/src/lib.rs index 1609206ab2..fa3082a88c 100644 --- a/crates/rust-client/src/lib.rs +++ b/crates/rust-client/src/lib.rs @@ -175,13 +175,10 @@ pub mod assembly { /// Provides types and utilities for working with assets within the Miden network. pub mod asset { pub use miden_protocol::account::delta::{ - AccountStorageDelta, AccountVaultDelta, FungibleAssetDelta, NonFungibleAssetDelta, NonFungibleDeltaAction, - StorageMapDelta, - StorageSlotDelta, }; pub use miden_protocol::account::{ AccountStorageHeader, @@ -217,7 +214,6 @@ pub mod auth { PublicKeyCommitment, Signature, }; - pub use miden_standards::AuthMethod; pub use miden_standards::account::auth::{ AuthMultisig, AuthMultisigConfig, diff --git a/crates/rust-client/src/rpc/domain/transaction.rs b/crates/rust-client/src/rpc/domain/transaction.rs index a97cc3b069..43147095c9 100644 --- a/crates/rust-client/src/rpc/domain/transaction.rs +++ b/crates/rust-client/src/rpc/domain/transaction.rs @@ -3,7 +3,6 @@ use alloc::string::ToString; use alloc::vec::Vec; use miden_protocol::Word; -use miden_protocol::asset::Asset; use miden_protocol::block::BlockNumber; use miden_protocol::note::{NoteHeader, NoteId, NoteInclusionProof, Nullifier}; use miden_protocol::transaction::{ @@ -16,10 +15,6 @@ use miden_protocol::transaction::{ use super::note::CommittedNote; use crate::rpc::{RpcConversionError, RpcError, generated as proto}; -/// A native asset faucet ID for use in testing scenarios. -#[cfg(test)] -pub const ACCOUNT_ID_NATIVE_ASSET_FAUCET: u128 = 0xab00_0000_0000_cd21_0000_ac00_0000_de00_u128; - // INTO TRANSACTION ID // ================================================================================================ @@ -180,30 +175,12 @@ fn convert_transaction_header( } } - let fee_asset: Asset = value - .fee - .ok_or(RpcConversionError::MissingFieldInProtobufRepresentation { - entity: "TransactionHeader", - field_name: "fee", - })? - .try_into()?; - - let fee = match fee_asset { - Asset::Fungible(fungible) => fungible, - Asset::NonFungible(_) => { - return Err(RpcError::InvalidResponse( - "expected fungible asset for transaction fee".into(), - )); - }, - }; - let transaction_header = TransactionHeader::new( account_id.try_into()?, initial_state_commitment.try_into()?, final_state_commitment.try_into()?, input_notes, output_note_headers, - fee, ); Ok((transaction_header, committed_output_notes, erased_output_notes)) } diff --git a/crates/rust-client/src/store/data_store/mod.rs b/crates/rust-client/src/store/data_store/mod.rs index dbad8212ce..f4787f4245 100644 --- a/crates/rust-client/src/store/data_store/mod.rs +++ b/crates/rust-client/src/store/data_store/mod.rs @@ -290,13 +290,13 @@ impl DataStore for ClientDataStore { return Err(DataStoreError::other("Vault root mismatch")); } - let asset_witness = - AssetWitness::new(vault.open(vault_key).into()).map_err(|err| { - DataStoreError::other_with_source( - "Failed to open vault asset tree", - err, - ) - })?; + let asset_witness = AssetWitness::new( + vault.open(vault_key).into(), + [vault_key], + ) + .map_err(|err| { + DataStoreError::other_with_source("Failed to open vault asset tree", err) + })?; asset_witnesses.push(asset_witness); }, Err(err) => { diff --git a/crates/rust-client/src/store/errors.rs b/crates/rust-client/src/store/errors.rs index c4b7e00217..331246148d 100644 --- a/crates/rust-client/src/store/errors.rs +++ b/crates/rust-client/src/store/errors.rs @@ -10,6 +10,7 @@ use miden_protocol::errors::{ AccountDeltaError, AccountError, AccountIdError, + AccountPatchError, AddressError, AssetError, AssetVaultError, @@ -42,6 +43,8 @@ pub enum StoreError { AccountDataNotFound(AccountId), #[error("account delta error")] AccountDeltaError(#[from] AccountDeltaError), + #[error("account patch error")] + AccountPatchError(#[from] AccountPatchError), #[error("account error")] AccountError(#[from] AccountError), #[error("address error")] diff --git a/crates/rust-client/src/store/mod.rs b/crates/rust-client/src/store/mod.rs index 3a2ed543ec..9f135dba02 100644 --- a/crates/rust-client/src/store/mod.rs +++ b/crates/rust-client/src/store/mod.rs @@ -612,7 +612,7 @@ pub trait Store: Send + Sync { return Ok(None); }; - let witness = AssetWitness::new(vault.open(vault_key).into())?; + let witness = AssetWitness::new(vault.open(vault_key).into(), [vault_key])?; Ok(Some((asset, witness))) } diff --git a/crates/rust-client/src/store/smt_forest.rs b/crates/rust-client/src/store/smt_forest.rs index c70af10f85..e4b0e4a538 100644 --- a/crates/rust-client/src/store/smt_forest.rs +++ b/crates/rust-client/src/store/smt_forest.rs @@ -52,16 +52,16 @@ impl AccountSmtForest { vault_root: Word, vault_key: AssetVaultKey, ) -> Result<(Asset, AssetWitness), StoreError> { - let vault_key_word = vault_key.into(); - let proof = self.forest.open(vault_root, vault_key_word)?; - let asset_word = - proof.get(&vault_key_word).ok_or(MerkleError::UntrackedKey(vault_key_word))?; + let vault_key_word: Word = vault_key.into(); + let hashed_key: Word = vault_key.hash().into(); + let proof = self.forest.open(vault_root, hashed_key)?; + let asset_word = proof.get(&hashed_key).ok_or(MerkleError::UntrackedKey(hashed_key))?; if asset_word == EMPTY_WORD { - return Err(MerkleError::UntrackedKey(vault_key_word).into()); + return Err(MerkleError::UntrackedKey(hashed_key).into()); } let asset = Asset::from_key_value_words(vault_key_word, asset_word)?; - let witness = AssetWitness::new(proof)?; + let witness = AssetWitness::new(proof, [vault_key])?; Ok((asset, witness)) } @@ -158,11 +158,11 @@ impl AccountSmtForest { ) -> Result { let entries: Vec<(Word, Word)> = new_assets .map(|asset| { - let key: Word = asset.vault_key().into(); + let key: Word = asset.vault_key().hash().into(); let value = asset.to_value_word(); (key, value) }) - .chain(removed_vault_keys.map(|vault_key| (vault_key.into(), EMPTY_WORD))) + .chain(removed_vault_keys.map(|vault_key| (vault_key.hash().into(), EMPTY_WORD))) .collect(); if entries.is_empty() { @@ -196,7 +196,7 @@ impl AccountSmtForest { /// Inserts the asset vault SMT nodes to the SMT forest. pub fn insert_asset_nodes(&mut self, vault: &AssetVault) -> Result<(), StoreError> { let smt = Smt::with_entries(vault.assets().map(|asset| { - let key: Word = asset.vault_key().into(); + let key: Word = asset.vault_key().hash().into(); let value = asset.to_value_word(); (key, value) })) diff --git a/crates/rust-client/src/sync/mod.rs b/crates/rust-client/src/sync/mod.rs index cce42a14b0..21c5cadc8d 100644 --- a/crates/rust-client/src/sync/mod.rs +++ b/crates/rust-client/src/sync/mod.rs @@ -87,7 +87,7 @@ mod state_sync_update; pub use state_sync_update::{ AccountUpdates, PartialBlockchainUpdates, - PublicAccountDelta, + PublicAccountPatch, PublicAccountUpdate, StateSyncUpdate, TransactionUpdateTracker, diff --git a/crates/rust-client/src/sync/state_sync.rs b/crates/rust-client/src/sync/state_sync.rs index 5171a86b23..6a83d9b69c 100644 --- a/crates/rust-client/src/sync/state_sync.rs +++ b/crates/rust-client/src/sync/state_sync.rs @@ -18,7 +18,7 @@ use super::{ AccountUpdates, NoteObserver, PartialBlockchainUpdates, - PublicAccountDelta, + PublicAccountPatch, PublicAccountUpdate, StateSyncUpdate, }; @@ -910,7 +910,7 @@ impl StateSync { .await .map_err(ClientError::RpcError)?; - Ok(PublicAccountUpdate::Delta(PublicAccountDelta::new( + Ok(PublicAccountUpdate::Patch(PublicAccountPatch::new( details.header.clone(), block_from, block_to, @@ -1250,7 +1250,6 @@ mod tests { use miden_testing::{MockChainBuilder, TxContextInput}; use super::*; - use crate::rpc::domain::transaction::ACCOUNT_ID_NATIVE_ASSET_FAUCET; use crate::store::{OutputNoteRecord, OutputNoteState}; use crate::test_utils::mock::MockRpcApi; @@ -1460,16 +1459,12 @@ mod tests { mod compute_nullifiers_tests { use alloc::vec; - use miden_protocol::asset::FungibleAsset; use miden_protocol::block::BlockNumber; use miden_protocol::note::Nullifier; use miden_protocol::transaction::{InputNoteCommitment, InputNotes, TransactionHeader}; use super::word; - use crate::rpc::domain::transaction::{ - ACCOUNT_ID_NATIVE_ASSET_FAUCET, - TransactionRecord as RpcTransactionRecord, - }; + use crate::rpc::domain::transaction::TransactionRecord as RpcTransactionRecord; fn make_rpc_tx( init_state: u64, @@ -1489,10 +1484,6 @@ mod tests { .collect(), ); - let fee = - FungibleAsset::new(ACCOUNT_ID_NATIVE_ASSET_FAUCET.try_into().expect("valid"), 0u64) - .unwrap(); - RpcTransactionRecord { block_num: BlockNumber::from(block_number), transaction_header: TransactionHeader::new( @@ -1501,7 +1492,6 @@ mod tests { word(final_state), input_notes, vec![], - fee, ), output_notes: vec![], erased_output_notes: vec![], @@ -1538,10 +1528,6 @@ mod tests { ) .unwrap(); - let fee = - FungibleAsset::new(ACCOUNT_ID_NATIVE_ASSET_FAUCET.try_into().expect("valid"), 0u64) - .unwrap(); - let tx_b1 = RpcTransactionRecord { block_num: BlockNumber::from(5u32), transaction_header: TransactionHeader::new( @@ -1552,7 +1538,6 @@ mod tests { Nullifier::from_raw(word(40)), )]), vec![], - fee, ), output_notes: vec![], erased_output_notes: vec![], @@ -1605,9 +1590,6 @@ mod tests { /// - Account B, block 6: single tx 10 - 20 (final state = 20). #[test] fn derive_account_commitments_walks_chains_per_account() { - let fee = - FungibleAsset::new(ACCOUNT_ID_NATIVE_ASSET_FAUCET.try_into().expect("valid"), 0u64) - .unwrap(); let make_tx = |account: AccountId, init_state: u64, final_state: u64, block_num: u32| { RpcTransactionRecord { block_num: BlockNumber::from(block_num), @@ -1617,7 +1599,6 @@ mod tests { word(final_state), InputNotes::new_unchecked(vec![]), vec![], - fee, ), output_notes: vec![], erased_output_notes: vec![], @@ -1711,7 +1692,7 @@ mod tests { ) .await .unwrap(); - current_account.apply_delta(tx.account_delta()).unwrap(); + current_account.apply_patch(tx.account_patch()).unwrap(); chain.add_pending_executed_transaction(&tx).unwrap(); } @@ -1919,7 +1900,7 @@ mod tests { note_tags.insert(output_note.metadata().tag()); } - faucet_account.apply_delta(tx.account_delta()).unwrap(); + faucet_account.apply_patch(tx.account_patch()).unwrap(); chain.add_pending_executed_transaction(&tx).unwrap(); chain.prove_next_block().unwrap(); } @@ -2325,9 +2306,6 @@ mod tests { /// Builds a minimal RPC transaction record at `block_num`, for range-validation tests. fn make_tx_record(account_id: AccountId, block_num: u32) -> RpcTransactionRecord { - let fee = - FungibleAsset::new(ACCOUNT_ID_NATIVE_ASSET_FAUCET.try_into().expect("valid"), 0u64) - .unwrap(); RpcTransactionRecord { block_num: BlockNumber::from(block_num), transaction_header: TransactionHeader::new( @@ -2336,7 +2314,6 @@ mod tests { word(2), InputNotes::new_unchecked(vec![]), vec![], - fee, ), output_notes: vec![], erased_output_notes: vec![], diff --git a/crates/rust-client/src/sync/state_sync_update.rs b/crates/rust-client/src/sync/state_sync_update.rs index 4adc570fbf..ae7d49f840 100644 --- a/crates/rust-client/src/sync/state_sync_update.rs +++ b/crates/rust-client/src/sync/state_sync_update.rs @@ -3,22 +3,20 @@ use alloc::vec::Vec; use miden_protocol::account::{ Account, - AccountDelta, + AccountCode, AccountHeader, AccountId, - AccountStorage, - AccountStorageDelta, - AccountVaultDelta, - StorageMapKey, + AccountPatch, + AccountStoragePatch, + AccountVaultPatch, StorageSlotName, }; -use miden_protocol::asset::{Asset, AssetVault, AssetVaultKey}; use miden_protocol::block::{BlockHeader, BlockNumber}; use miden_protocol::crypto::merkle::mmr::{InOrderIndex, MmrPeaks}; -use miden_protocol::errors::{AccountDeltaError, AccountError}; +use miden_protocol::errors::AccountPatchError; use miden_protocol::note::{NoteId, Nullifier}; use miden_protocol::transaction::TransactionId; -use miden_protocol::{Felt, Word}; +use miden_protocol::{Felt, ONE, Word}; use super::SyncSummary; use crate::note::{NoteUpdateTracker, NoteUpdateType}; @@ -341,18 +339,18 @@ impl TransactionUpdateTracker { /// /// - [`PublicAccountUpdate::Full`] carries the new [`Account`] state directly (used when no storage /// map is oversized and the vault fits in the response). The store applies it by replacing the -/// local state — no delta computation needed. -/// - [`PublicAccountUpdate::Delta`] carries a [`PublicAccountDelta`] payload (new header plus +/// local state. +/// - [`PublicAccountUpdate::Patch`] carries a [`PublicAccountPatch`] payload (new header plus /// incremental updates from `sync_storage_maps` and `sync_account_vault`, used when any part of -/// the account is oversized). The store calls [`PublicAccountDelta::compute_account_delta`] to -/// derive the [`AccountDelta`] to apply. +/// the account is oversized). The store calls [`PublicAccountPatch::compute_account_patch`] to +/// build the absolute [`AccountPatch`] to apply. #[derive(Debug, Clone)] pub enum PublicAccountUpdate { /// The account fits in a single proof response — the new full state is carried as-is. Full(Account), - /// The account is oversized in some dimension. The new state must be reconstructed by - /// replaying the carried incremental updates against the locally-stored state. - Delta(PublicAccountDelta), + /// The account is oversized in some dimension. The new state is described by the absolute + /// patch built from the carried incremental updates. + Patch(PublicAccountPatch), } impl PublicAccountUpdate { @@ -360,7 +358,7 @@ impl PublicAccountUpdate { pub fn id(&self) -> AccountId { match self { Self::Full(account) => account.id(), - Self::Delta(delta) => delta.id(), + Self::Patch(patch) => patch.id(), } } @@ -368,19 +366,18 @@ impl PublicAccountUpdate { pub fn nonce(&self) -> Felt { match self { Self::Full(account) => account.nonce(), - Self::Delta(delta) => delta.new_header().nonce(), + Self::Patch(patch) => patch.new_header().nonce(), } } } -/// Incremental delta payload for a public account update. +/// Patch payload for a public account update. /// /// Carries the new account header plus the per-block updates fetched from the node's incremental -/// endpoints (`sync_storage_maps` and `sync_account_vault`). The store derives the -/// [`AccountDelta`] to apply by replaying these updates against its locally-stored account state -/// via [`Self::compute_account_delta`]. +/// endpoints (`sync_storage_maps` and `sync_account_vault`). The store turns these absolute +/// updates into the [`AccountPatch`] to apply via [`Self::compute_account_patch`]. #[derive(Debug, Clone)] -pub struct PublicAccountDelta { +pub struct PublicAccountPatch { /// The new account header after applying these updates. new_header: AccountHeader, /// First block of the synced range (the client's previous sync height). @@ -396,8 +393,8 @@ pub struct PublicAccountDelta { vault_updates: Vec, } -impl PublicAccountDelta { - /// Creates a new [`PublicAccountDelta`]. +impl PublicAccountPatch { + /// Creates a new [`PublicAccountPatch`]. pub fn new( new_header: AccountHeader, block_from: BlockNumber, @@ -431,8 +428,7 @@ impl PublicAccountDelta { self.block_from } - /// Returns the names of the value slots referenced by this delta. The store can use this to - /// load only the slots needed by [`Self::compute_account_delta`] instead of the full storage. + /// Returns the names of the value slots referenced by this delta. pub fn value_slot_names(&self) -> Vec { self.value_slot_updates.iter().map(|(name, _)| name.clone()).collect() } @@ -442,124 +438,53 @@ impl PublicAccountDelta { self.block_to } - /// Computes the [`AccountDelta`] implied by this payload by replaying the carried - /// incremental updates against the locally-stored account state. - // TODO #2171: - // skip building AccountDelta; have the store accept raw RPC updates directly. - pub fn compute_account_delta( + /// Builds the absolute [`AccountPatch`] implied by this payload. + /// + /// The node's incremental endpoints already report the new absolute value of each changed + /// storage slot, map entry, and vault asset, so the patch is assembled directly from them with + /// no need to load the prior account state: value slots and map entries become storage-patch + /// entries, present assets become vault-patch insertions, and absent assets (the node's removal + /// signal) become vault-patch removals. + /// + /// An update of an existing account (final nonce > 1) yields a partial-state patch with no + /// code. A newly created account (final nonce 1) cannot be represented as a partial-state + /// patch, so it must carry the account code: the caller supplies `code` (the account's code + /// is fixed at creation, so the locally-tracked code matches), and the patch becomes a + /// full-state patch. + pub fn compute_account_patch( &self, - local_header: &AccountHeader, - local_storage: &AccountStorage, - local_vault: &AssetVault, - ) -> Result { - let old_nonce = local_header.nonce().as_canonical_u64(); - let new_nonce = self.new_header.nonce().as_canonical_u64(); - if new_nonce <= old_nonce { - return Err(AccountDeltaError::AccountDeltaApplicationFailed { - account_id: self.new_header.id(), - source: AccountError::other(format!( - "node returned non-monotonic account nonce: local {old_nonce} >= new {new_nonce}" - )), - }); - } - - let storage_delta = replay_storage_updates( - local_storage, - &self.value_slot_updates, - &self.storage_map_updates, - )?; - let vault_delta = replay_vault_updates(local_vault, &self.vault_updates)?; - - let nonce_delta = Felt::new(new_nonce - old_nonce).expect( - "new_nonce was checked to be higher than old_nonce; should return a valid nonce", - ); - - AccountDelta::new(self.new_header.id(), storage_delta, vault_delta, nonce_delta) - } -} - -// DELTA REPLAY HELPERS -// ================================================================================================ + code: Option, + ) -> Result { + let account_id = self.new_header.id(); -/// Computes a storage delta by replaying incremental updates onto the locally-stored state. -fn replay_storage_updates( - local_storage: &AccountStorage, - value_slot_updates: &[(StorageSlotName, Word)], - storage_map_updates: &[StorageMapUpdate], -) -> Result { - let mut storage_delta = AccountStorageDelta::new(); - - // Value slots: emit only the slots whose new value differs from local. - for (slot_name, new_value) in value_slot_updates { - let local_value = local_storage.get_item(slot_name).ok(); - if local_value.as_ref() != Some(new_value) { - storage_delta.set_item(slot_name.clone(), *new_value)?; + let mut storage = AccountStoragePatch::new(); + for (slot_name, new_value) in &self.value_slot_updates { + storage.set_item(slot_name.clone(), *new_value)?; } - } - - // Map slots: dedup updates per (slot, key) keeping the latest value by block number. - let mut by_slot: BTreeMap> = BTreeMap::new(); - let mut sorted: Vec<&StorageMapUpdate> = storage_map_updates.iter().collect(); - sorted.sort_by_key(|u| u.block_num); - for update in sorted { - by_slot - .entry(update.slot_name.clone()) - .or_default() - .insert(update.key, update.value); - } - for (slot_name, entries) in by_slot { - for (key, value) in entries { - storage_delta.set_map_item(slot_name.clone(), key, value)?; + // Map entries are absolute per (slot, key), applying them in block order lets a later block + // overwrite an earlier one for the same key, yielding the final value. + let mut map_updates: Vec<&StorageMapUpdate> = self.storage_map_updates.iter().collect(); + map_updates.sort_by_key(|u| u.block_num); + for update in map_updates { + storage.set_map_item(update.slot_name.clone(), update.key, update.value)?; } - } - - Ok(storage_delta) -} -/// Computes a vault delta by replaying incremental updates onto the locally-stored vault. -fn replay_vault_updates( - local_vault: &AssetVault, - vault_updates: &[AccountVaultUpdate], -) -> Result { - let mut vault_delta = AccountVaultDelta::default(); - - let mut final_vault: BTreeMap = - local_vault.assets().map(|asset| (asset.vault_key(), asset)).collect(); - - let mut sorted: Vec<&AccountVaultUpdate> = vault_updates.iter().collect(); - sorted.sort_by_key(|u| u.block_num); - for update in sorted { - match update.asset { - Some(asset) => { - final_vault.insert(update.vault_key, asset); - }, - None => { - final_vault.remove(&update.vault_key); - }, + // Vault entries are absolute per key, applying them in block order yields the final value, + // with a `None` asset encoding a removal. + let mut vault = AccountVaultPatch::default(); + let mut vault_updates: Vec<&AccountVaultUpdate> = self.vault_updates.iter().collect(); + vault_updates.sort_by_key(|u| u.block_num); + for update in vault_updates { + match update.asset { + Some(asset) => vault.insert_asset(asset), + None => vault.remove_asset(update.vault_key), + } } - } - let local_assets: BTreeMap = - local_vault.assets().map(|a| (a.vault_key(), a)).collect(); - for (key, final_asset) in &final_vault { - match local_assets.get(key) { - None => { - vault_delta.add_asset(*final_asset)?; - }, - Some(local_asset) if local_asset != final_asset => { - vault_delta.remove_asset(*local_asset)?; - vault_delta.add_asset(*final_asset)?; - }, - _ => {}, - } - } - for (key, local_asset) in &local_assets { - if !final_vault.contains_key(key) { - vault_delta.remove_asset(*local_asset)?; - } - } + let code = if self.new_header.nonce() == ONE { code } else { None }; - Ok(vault_delta) + AccountPatch::new(account_id, storage, vault, code, Some(self.new_header.nonce())) + } } // ACCOUNT UPDATES @@ -615,8 +540,8 @@ impl AccountUpdates { mod tests { use alloc::vec; - use miden_protocol::account::{StorageMapKey, StorageSlot}; - use miden_protocol::asset::{Asset, AssetVault, FungibleAsset}; + use miden_protocol::account::StorageMapKey; + use miden_protocol::asset::{Asset, AssetVaultKey, FungibleAsset}; use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, @@ -663,8 +588,8 @@ mod tests { ) } - fn empty_payload(new_header: AccountHeader) -> PublicAccountDelta { - PublicAccountDelta::new( + fn empty_payload(new_header: AccountHeader) -> PublicAccountPatch { + PublicAccountPatch::new( new_header, BlockNumber::from(0u32), BlockNumber::from(1u32), @@ -674,274 +599,181 @@ mod tests { ) } - // REPLAY STORAGE UPDATES - // -------------------------------------------------------------------------------------------- + fn storage_map_update( + block: u32, + slot: &StorageSlotName, + key: StorageMapKey, + value: Word, + ) -> StorageMapUpdate { + StorageMapUpdate { + block_num: BlockNumber::from(block), + slot_name: slot.clone(), + key, + value, + } + } - #[test] - fn replay_storage_empty_inputs_returns_empty_delta() { - let storage = AccountStorage::new(vec![]).unwrap(); - let delta = replay_storage_updates(&storage, &[], &[]).unwrap(); - assert!(delta.is_empty()); + fn vault_update( + block: u32, + asset: Option, + vault_key: AssetVaultKey, + ) -> AccountVaultUpdate { + AccountVaultUpdate { + block_num: BlockNumber::from(block), + asset, + vault_key, + } } - #[test] - fn replay_storage_value_slot_changed_emits_delta() { - let value_slot = slot_name("miden::test::value"); - let storage = - AccountStorage::new(vec![StorageSlot::with_value(value_slot.clone(), word(1))]) - .unwrap(); + fn payload( + new_nonce: u64, + value_slot_updates: Vec<(StorageSlotName, Word)>, + storage_map_updates: Vec, + vault_updates: Vec, + ) -> PublicAccountPatch { + PublicAccountPatch::new( + header_with_nonce(new_nonce), + BlockNumber::from(0u32), + BlockNumber::from(1u32), + value_slot_updates, + storage_map_updates, + vault_updates, + ) + } - let delta = - replay_storage_updates(&storage, &[(value_slot.clone(), word(2))], &[]).unwrap(); + // COMPUTE ACCOUNT PATCH - STORAGE + // -------------------------------------------------------------------------------------------- + + #[test] + fn compute_patch_empty_payload_carries_only_nonce() { + let patch = empty_payload(header_with_nonce(4)).compute_account_patch(None).unwrap(); - let entry = delta.get(&value_slot).expect("delta should contain value slot"); - assert_eq!(entry.clone().unwrap_value(), word(2)); + assert_eq!(patch.final_nonce(), Some(Felt::new_unchecked(4))); + assert!(patch.storage().is_empty()); + assert!(patch.vault().is_empty()); + assert!(!patch.is_full_state()); } #[test] - fn replay_storage_value_slot_unchanged_is_skipped() { + fn compute_patch_sets_value_slot_absolutely() { let value_slot = slot_name("miden::test::value"); - let storage = - AccountStorage::new(vec![StorageSlot::with_value(value_slot.clone(), word(1))]) - .unwrap(); - - let delta = - replay_storage_updates(&storage, &[(value_slot.clone(), word(1))], &[]).unwrap(); + let patch = payload(2, vec![(value_slot.clone(), word(2))], vec![], vec![]) + .compute_account_patch(None) + .unwrap(); - assert!(delta.is_empty()); + assert_eq!(patch.storage().get_value(&value_slot), Some(word(2))); } #[test] - fn replay_storage_map_dedup_keeps_latest_block_per_key() { + fn compute_patch_map_dedup_keeps_latest_block_per_key() { let map_slot = slot_name("miden::test::map"); - let storage = - AccountStorage::new(vec![StorageSlot::with_empty_map(map_slot.clone())]).unwrap(); - let key = map_key(42); let updates = vec![ - StorageMapUpdate { - block_num: BlockNumber::from(1u32), - slot_name: map_slot.clone(), - key, - value: word(100), - }, - StorageMapUpdate { - block_num: BlockNumber::from(3u32), - slot_name: map_slot.clone(), - key, - value: word(300), - }, - StorageMapUpdate { - block_num: BlockNumber::from(2u32), - slot_name: map_slot.clone(), - key, - value: word(200), - }, + storage_map_update(1, &map_slot, key, word(100)), + storage_map_update(3, &map_slot, key, word(300)), + storage_map_update(2, &map_slot, key, word(200)), ]; - let delta = replay_storage_updates(&storage, &[], &updates).unwrap(); + let patch = payload(2, vec![], updates, vec![]).compute_account_patch(None).unwrap(); - let map_delta = delta.get(&map_slot).expect("delta should contain map slot").clone(); - let map = map_delta.unwrap_map(); + let map = patch.storage().get_map(&map_slot).expect("patch should contain map slot"); assert_eq!(map.entries().len(), 1); assert_eq!(*map.entries().values().next().unwrap(), word(300)); } #[test] - fn replay_storage_map_multiple_keys_in_same_slot_all_kept() { + fn compute_patch_map_multiple_keys_in_same_slot_all_kept() { let map_slot = slot_name("miden::test::map"); - let storage = - AccountStorage::new(vec![StorageSlot::with_empty_map(map_slot.clone())]).unwrap(); - let updates = vec![ - StorageMapUpdate { - block_num: BlockNumber::from(1u32), - slot_name: map_slot.clone(), - key: map_key(1), - value: word(100), - }, - StorageMapUpdate { - block_num: BlockNumber::from(2u32), - slot_name: map_slot.clone(), - key: map_key(2), - value: word(200), - }, + storage_map_update(1, &map_slot, map_key(1), word(100)), + storage_map_update(2, &map_slot, map_key(2), word(200)), ]; - let delta = replay_storage_updates(&storage, &[], &updates).unwrap(); - let map = delta.get(&map_slot).unwrap().clone().unwrap_map(); + let patch = payload(2, vec![], updates, vec![]).compute_account_patch(None).unwrap(); + let map = patch.storage().get_map(&map_slot).unwrap(); assert_eq!(map.entries().len(), 2); } - // REPLAY VAULT UPDATES + // COMPUTE ACCOUNT PATCH - VAULT // -------------------------------------------------------------------------------------------- #[test] - fn replay_vault_empty_inputs_returns_empty_delta() { - let vault = AssetVault::new(&[]).unwrap(); - let delta = replay_vault_updates(&vault, &[]).unwrap(); - assert!(delta.is_empty()); - } - - #[test] - fn replay_vault_added_asset_emits_add() { - let vault = AssetVault::new(&[]).unwrap(); + fn compute_patch_inserts_asset_absolutely() { let asset = fungible(100); - let updates = vec![AccountVaultUpdate { - block_num: BlockNumber::from(1u32), - asset: Some(asset), - vault_key: asset.vault_key(), - }]; + let updates = vec![vault_update(1, Some(asset), asset.vault_key())]; + + let patch = payload(2, vec![], vec![], updates).compute_account_patch(None).unwrap(); - let delta = replay_vault_updates(&vault, &updates).unwrap(); - let added: Vec<_> = delta.added_assets().collect(); - assert_eq!(added, vec![asset]); - assert_eq!(delta.removed_assets().count(), 0); + let updated_assets: Vec<_> = patch.vault().updated_assets().collect(); + assert_eq!(updated_assets, vec![asset]); + assert_eq!(patch.vault().removed_asset_keys().count(), 0); } #[test] - fn replay_vault_removed_asset_emits_remove() { + fn compute_patch_removed_asset_is_a_removal() { let asset = fungible(100); - let vault = AssetVault::new(&[asset]).unwrap(); - let updates = vec![AccountVaultUpdate { - block_num: BlockNumber::from(1u32), - asset: None, - vault_key: asset.vault_key(), - }]; + let updates = vec![vault_update(1, None, asset.vault_key())]; - let delta = replay_vault_updates(&vault, &updates).unwrap(); - let removed: Vec<_> = delta.removed_assets().collect(); - assert_eq!(removed, vec![asset]); - assert_eq!(delta.added_assets().count(), 0); - } + let patch = payload(2, vec![], vec![], updates).compute_account_patch(None).unwrap(); - #[test] - fn replay_vault_replace_asset_emits_net_diff() { - let asset_a = fungible(100); - let asset_b = fungible(150); - let vault = AssetVault::new(&[asset_a]).unwrap(); - let updates = vec![AccountVaultUpdate { - block_num: BlockNumber::from(1u32), - asset: Some(asset_b), - vault_key: asset_b.vault_key(), - }]; - - let delta = replay_vault_updates(&vault, &updates).unwrap(); - let added: Vec<_> = delta.added_assets().collect(); - assert_eq!(added, vec![fungible(50)]); - assert_eq!(delta.removed_assets().count(), 0); + let removed: Vec<_> = patch.vault().removed_asset_keys().copied().collect(); + assert_eq!(removed, vec![asset.vault_key()]); + assert_eq!(patch.vault().updated_assets().count(), 0); } #[test] - fn replay_vault_dedup_keeps_latest_block_per_key() { - let vault = AssetVault::new(&[]).unwrap(); + fn compute_patch_vault_dedup_keeps_latest_block_per_key() { let asset_v1 = fungible(100); - let asset_v2 = fungible(200); let asset_v3 = fungible(300); + let asset_v2 = fungible(200); let key = asset_v1.vault_key(); - let updates = vec![ - AccountVaultUpdate { - block_num: BlockNumber::from(1u32), - asset: Some(asset_v1), - vault_key: key, - }, - AccountVaultUpdate { - block_num: BlockNumber::from(3u32), - asset: Some(asset_v3), - vault_key: key, - }, - AccountVaultUpdate { - block_num: BlockNumber::from(2u32), - asset: Some(asset_v2), - vault_key: key, - }, + vault_update(1, Some(asset_v1), key), + vault_update(3, Some(asset_v3), key), + vault_update(2, Some(asset_v2), key), ]; - let delta = replay_vault_updates(&vault, &updates).unwrap(); - let added: Vec<_> = delta.added_assets().collect(); - assert_eq!(added, vec![asset_v3]); + let patch = payload(2, vec![], vec![], updates).compute_account_patch(None).unwrap(); + let updated_assets: Vec<_> = patch.vault().updated_assets().collect(); + assert_eq!(updated_assets, vec![asset_v3]); } #[test] - fn replay_vault_added_then_removed_is_noop() { - let vault = AssetVault::new(&[]).unwrap(); + fn compute_patch_added_then_removed_is_a_removal() { let asset = fungible(100); let key = asset.vault_key(); + let updates = vec![vault_update(1, Some(asset), key), vault_update(2, None, key)]; - let updates = vec![ - AccountVaultUpdate { - block_num: BlockNumber::from(1u32), - asset: Some(asset), - vault_key: key, - }, - AccountVaultUpdate { - block_num: BlockNumber::from(2u32), - asset: None, - vault_key: key, - }, - ]; - - let delta = replay_vault_updates(&vault, &updates).unwrap(); - assert!(delta.is_empty()); + let patch = payload(2, vec![], vec![], updates).compute_account_patch(None).unwrap(); + assert_eq!(patch.vault().updated_assets().count(), 0); + let removed: Vec<_> = patch.vault().removed_asset_keys().copied().collect(); + assert_eq!(removed, vec![key]); } - // COMPUTE ACCOUNT DELTA - // -------------------------------------------------------------------------------------------- - #[test] - fn compute_delta_happy_path_emits_nonce_delta() { - let local_header = header_with_nonce(1); - let local_storage = AccountStorage::new(vec![]).unwrap(); - let local_vault = AssetVault::new(&[]).unwrap(); - let payload = empty_payload(header_with_nonce(4)); - - let delta = payload - .compute_account_delta(&local_header, &local_storage, &local_vault) - .unwrap(); - - assert_eq!(delta.nonce_delta(), Felt::new_unchecked(3)); - assert!(delta.storage().is_empty()); - assert!(delta.vault().is_empty()); + fn compute_patch_rejects_zero_nonce() { + let result = empty_payload(header_with_nonce(0)).compute_account_patch(None); + assert!(result.is_err()); } + /// A newly created account (final nonce 1) observed via the oversized sync path yields a + /// full-state patch carrying the supplied code, rather than failing to build. #[test] - fn compute_delta_rejects_equal_nonce() { - let local_header = header_with_nonce(5); - let local_storage = AccountStorage::new(vec![]).unwrap(); - let local_vault = AssetVault::new(&[]).unwrap(); - let payload = empty_payload(header_with_nonce(5)); - - let err = payload - .compute_account_delta(&local_header, &local_storage, &local_vault) - .unwrap_err(); - - assert!(matches!( - err, - AccountDeltaError::AccountDeltaApplicationFailed { - source: AccountError::Other { .. }, - .. - } - )); + fn compute_patch_for_new_account_is_full_state() { + let value_slot = slot_name("miden::test::value"); + let patch = payload(1, vec![(value_slot, word(1))], vec![], vec![]) + .compute_account_patch(Some(AccountCode::mock())) + .unwrap(); + + assert!(patch.is_full_state()); + assert_eq!(patch.final_nonce(), Some(ONE)); } + /// A final-nonce-1 patch cannot be built without the account code (it must be a full-state + /// patch), so the missing-code case is reported rather than silently producing a wrong patch. #[test] - fn compute_delta_rejects_decreasing_nonce() { - let local_header = header_with_nonce(10); - let local_storage = AccountStorage::new(vec![]).unwrap(); - let local_vault = AssetVault::new(&[]).unwrap(); - let payload = empty_payload(header_with_nonce(9)); - - let err = payload - .compute_account_delta(&local_header, &local_storage, &local_vault) - .unwrap_err(); - - assert!(matches!( - err, - AccountDeltaError::AccountDeltaApplicationFailed { - source: AccountError::Other { .. }, - .. - } - )); + fn compute_patch_for_new_account_without_code_errors() { + let result = payload(1, vec![], vec![], vec![]).compute_account_patch(None); + assert!(result.is_err()); } } diff --git a/crates/rust-client/src/test_utils/common.rs b/crates/rust-client/src/test_utils/common.rs index 48ce81fd8e..164e54b679 100644 --- a/crates/rust-client/src/test_utils/common.rs +++ b/crates/rust-client/src/test_utils/common.rs @@ -24,17 +24,16 @@ use uuid::Uuid; use crate::account::component::{ AccountComponent, BasicWallet, - BurnPolicyConfig, + BurnPolicy, FungibleFaucet, - MintPolicyConfig, - PolicyRegistration, + MintPolicy, TokenPolicyManager, }; use crate::account::{AccountBuilder, AccountBuilderSchemaCommitmentExt, AccountType, StorageSlot}; use crate::auth::AuthSchemeId; use crate::crypto::FeltRng; pub use crate::keystore::{FilesystemKeyStore, Keystore}; -use crate::note::{Note, NoteAttachments, NoteConsumability, P2idNote}; +use crate::note::{Note, NoteConsumability, P2idNote}; use crate::rpc::RpcError; use crate::store::{InputNoteRecord, NoteFilter, TransactionFilter}; use crate::sync::SyncSummary; @@ -141,11 +140,10 @@ pub async fn insert_new_fungible_faucet( // `AssetCallbackFlag::Enabled`. Tests construct assets via `FungibleAsset::new`, which // defaults to `Disabled`, so adding transfer policies makes `mint_and_send` reject the // mint with `ERR_FUNGIBLE_MINT_NOTE_ASSET_NOT_FROM_THIS_FAUCET`. - let policy_manager = TokenPolicyManager::new() - .with_mint_policy(MintPolicyConfig::AllowAll, PolicyRegistration::Active) - .unwrap() - .with_burn_policy(BurnPolicyConfig::AllowAll, PolicyRegistration::Active) - .unwrap(); + let policy_manager = TokenPolicyManager::builder() + .active_mint_policy(MintPolicy::allow_all()) + .active_burn_policy(BurnPolicy::allow_all()) + .build(); let account = AccountBuilder::new(init_seed) .account_type(visibility) .with_auth_component(auth_component) @@ -539,15 +537,15 @@ pub fn mint_multiple_fungible_asset( let notes = target_id .iter() .map(|account_id| { - P2idNote::create( - asset.faucet_id(), - *account_id, - vec![asset.into()], - note_type, - NoteAttachments::empty(), - rng, - ) - .unwrap() + P2idNote::builder() + .sender(asset.faucet_id()) + .target(*account_id) + .asset(asset) + .note_type(note_type) + .generate_serial_number(rng) + .build() + .expect("note creation failed") + .into() }) .collect::>(); diff --git a/crates/rust-client/src/test_utils/mock.rs b/crates/rust-client/src/test_utils/mock.rs index d568f691fb..90b30dce79 100644 --- a/crates/rust-client/src/test_utils/mock.rs +++ b/crates/rust-client/src/test_utils/mock.rs @@ -4,8 +4,13 @@ use alloc::sync::Arc; use alloc::vec::Vec; use miden_protocol::Word; -use miden_protocol::account::delta::AccountUpdateDetails; -use miden_protocol::account::{AccountId, StorageSlot, StorageSlotContent, StorageSlotType}; +use miden_protocol::account::{ + AccountId, + AccountUpdateDetails, + StorageSlot, + StorageSlotContent, + StorageSlotType, +}; use miden_protocol::address::NetworkId; use miden_protocol::batch::{ProposedBatch, ProvenBatch}; use miden_protocol::block::{BlockHeader, BlockNumber, ProvenBlock}; @@ -165,19 +170,26 @@ impl MockRpcApi { .iter() .filter(|block_acc_update| block_acc_update.account_id() == account_id) { - let AccountUpdateDetails::Delta(account_delta) = update.details().clone() else { + let AccountUpdateDetails::Public(patch) = update.details().clone() else { continue; }; - let vault_delta = account_delta.vault(); + let vault_patch = patch.vault(); - for asset in vault_delta.added_assets() { - let account_vault_update = AccountVaultUpdate { + for asset in vault_patch.updated_assets() { + updates.push(AccountVaultUpdate { block_num: block_number, asset: Some(asset), vault_key: asset.vault_key(), - }; - updates.push(account_vault_update); + }); + } + + for vault_key in vault_patch.removed_asset_keys() { + updates.push(AccountVaultUpdate { + block_num: block_number, + asset: None, + vault_key: *vault_key, + }); } } } @@ -252,14 +264,14 @@ impl MockRpcApi { .iter() .filter(|block_acc_update| block_acc_update.account_id() == account_id) { - let AccountUpdateDetails::Delta(account_delta) = update.details().clone() else { + let AccountUpdateDetails::Public(patch) = update.details().clone() else { continue; }; - let storage_delta = account_delta.storage(); + let storage_patch = patch.storage(); - for (slot_name, map_delta) in storage_delta.maps() { - for (key, value) in map_delta.entries() { + for (slot_name, map_patch) in storage_patch.maps() { + for (key, value) in map_patch.entries() { let storage_map_info = StorageMapUpdate { block_num: block_number, slot_name: slot_name.clone(), @@ -298,7 +310,7 @@ impl MockRpcApi { .read() .committed_notes() .values() - .filter(|n| matches!(n, MockChainNote::Private(_, _, _))) + .filter(|n| matches!(n, MockChainNote::Private(_, _, _, _))) .cloned() .collect() } @@ -419,7 +431,7 @@ impl NodeRpcClient for MockRpcApi { let mut return_notes = vec![]; for note in hit_notes { let fetched_note = match note { - MockChainNote::Private(note_id, note_metadata, note_inclusion_proof) => { + MockChainNote::Private(note_id, note_metadata, _, note_inclusion_proof) => { let attachments = self .private_note_attachments .read() diff --git a/crates/rust-client/src/transaction/batch/mod.rs b/crates/rust-client/src/transaction/batch/mod.rs index 57cd8edfa2..1c03bdc71c 100644 --- a/crates/rust-client/src/transaction/batch/mod.rs +++ b/crates/rust-client/src/transaction/batch/mod.rs @@ -62,7 +62,7 @@ use miden_protocol::block::{BlockHeader, BlockNumber}; use miden_protocol::note::NoteId; use miden_protocol::transaction::{PartialBlockchain, ProvenTransaction, TransactionInputs}; use miden_tx::auth::TransactionAuthenticator; -use miden_tx_batch_prover::LocalBatchProver; +use miden_tx_batch::{BatchExecutor, LocalBatchProver}; use crate::store::data_store::build_partial_mmr_with_paths; use crate::transaction::{ @@ -185,11 +185,12 @@ where ref_block_header, partial_blockchain, unauthenticated_note_proofs, + MIN_PROOF_SECURITY_LEVEL, )?; - // 6. Prove synchronously. - let proven_batch = - LocalBatchProver::new(MIN_PROOF_SECURITY_LEVEL).prove(proposed_batch.clone())?; + // 6. Execute the batch kernel, then prove synchronously. + let executed_batch = BatchExecutor::new().execute(proposed_batch.clone())?; + let proven_batch = LocalBatchProver::new().prove(executed_batch)?; // 7. Submit via RPC. let mut updates: Vec = Vec::with_capacity(len); @@ -310,10 +311,14 @@ where .execute_transaction(account_id, prep.block_num, notes, prep.tx_args) .await?; - // Cache new account state in memory data store - account - .apply_delta(executed_transaction.account_delta()) - .map_err(ClientError::AccountError)?; + // Cache new account state in memory data store. + let patch = executed_transaction.account_patch(); + let account = if patch.is_full_state() { + Account::try_from(patch).map_err(ClientError::AccountError)? + } else { + account.apply_patch(patch).map_err(ClientError::AccountError)?; + account + }; data_store.cache_account(account_id, account); validate_executed_transaction(&executed_transaction, &prep.output_recipients)?; diff --git a/crates/rust-client/src/transaction/mod.rs b/crates/rust-client/src/transaction/mod.rs index bd2b32a75c..d4c25435db 100644 --- a/crates/rust-client/src/transaction/mod.rs +++ b/crates/rust-client/src/transaction/mod.rs @@ -83,7 +83,7 @@ use miden_protocol::note::{ use miden_protocol::transaction::AccountInputs; use miden_protocol::vm::MIN_STACK_DEPTH; use miden_protocol::{Felt, Word}; -use miden_standards::account::interface::AccountInterfaceExt; +use miden_standards::account::faucets::FungibleFaucet; use miden_tx::{DataStore, NoteConsumptionChecker, TransactionExecutor}; use tracing::info; @@ -396,8 +396,8 @@ where let future_notes: Vec<(NoteDetails, NoteTag)> = transaction_request.expected_future_notes().cloned().collect(); - let tx_script = transaction_request - .build_transaction_script(&self.get_account_interface(account_id).await?)?; + let account = self.try_get_account(account_id).await?; + let tx_script = transaction_request.build_transaction_script(&account.code_interface())?; let foreign_accounts = transaction_request.foreign_accounts().clone(); @@ -861,15 +861,6 @@ where .with_program_executor::()) } - /// Loads the account and constructs an [`AccountInterface`] from it. - pub(crate) async fn get_account_interface( - &self, - account_id: AccountId, - ) -> Result { - let account = self.try_get_account(account_id).await?; - Ok(AccountInterface::from_account(&account)) - } - /// Returns [`NoteUpdateTracker`] containing the note updates generated by an executed /// transaction. async fn get_note_updates( @@ -1053,11 +1044,7 @@ pub(super) fn validate_account_request( transaction_request: &TransactionRequest, account: &Account, ) -> Result<(), ClientError> { - let account_interface = AccountInterface::from_account(account); - if account_interface - .components() - .contains(&AccountComponentInterface::FungibleFaucet) - { + if account.code_interface().contains([FungibleFaucet::mint_and_send_root()]) { // TODO(SantiagoPittella): Add faucet validations. Ok(()) } else { @@ -1255,7 +1242,7 @@ mod tests { use miden_protocol::account::AccountId; use miden_protocol::asset::FungibleAsset; use miden_protocol::crypto::rand::RandomCoin; - use miden_protocol::note::{Note, NoteAttachments, NoteType}; + use miden_protocol::note::{Note, NoteType}; use miden_protocol::testing::account_id::{ ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, @@ -1273,15 +1260,15 @@ mod tests { AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(); let mut rng = RandomCoin::new(Word::default()); - P2idNote::create( - sender, - target_id, - vec![FungibleAsset::new(faucet_id, 100).unwrap().into()], - NoteType::Public, - NoteAttachments::empty(), - &mut rng, - ) - .unwrap() + P2idNote::builder() + .sender(sender) + .target(target_id) + .asset(FungibleAsset::new(faucet_id, 100).unwrap()) + .note_type(NoteType::Public) + .generate_serial_number(&mut rng) + .build() + .expect("note creation failed") + .into() } #[test] diff --git a/crates/rust-client/src/transaction/request/builder.rs b/crates/rust-client/src/transaction/request/builder.rs index 52b19e1bea..f6b188ef09 100644 --- a/crates/rust-client/src/transaction/request/builder.rs +++ b/crates/rust-client/src/transaction/request/builder.rs @@ -29,14 +29,7 @@ use miden_protocol::note::{ use miden_protocol::transaction::TransactionScript; use miden_protocol::vm::AdviceMap; use miden_protocol::{Felt, Word}; -use miden_standards::note::{ - P2idNote, - P2ideNote, - P2ideNoteStorage, - PswapNote, - PswapNoteStorage, - SwapNote, -}; +use miden_standards::note::{P2idNote, P2ideNote, PswapNote, PswapNoteStorage, SwapNote}; use super::{ ForeignAccount, @@ -328,14 +321,14 @@ impl TransactionRequestBuilder { note_type: NoteType, rng: &mut ClientRng, ) -> Result { - let created_note = P2idNote::create( - asset.faucet_id(), - target_id, - vec![asset.into()], - note_type, - NoteAttachments::empty(), - rng, - )?; + let created_note = P2idNote::builder() + .sender(asset.faucet_id()) + .target(target_id) + .asset(asset) + .note_type(note_type) + .generate_serial_number(rng) + .build()? + .into(); self.own_output_notes(vec![created_note]).build() } @@ -576,6 +569,10 @@ impl TransactionRequestBuilder { } } + if self.expiration_delta == Some(0) { + return Err(TransactionRequestError::ZeroExpirationDelta); + } + let script_template = match (self.custom_script, self.own_output_notes.is_empty()) { (Some(_), false) => { return Err(TransactionRequestError::ScriptTemplateError( @@ -709,28 +706,26 @@ impl PaymentNoteDescription { ) -> Result { if self.reclaim_height.is_none() && self.timelock_height.is_none() { // Create a P2ID note - P2idNote::create( - self.sender_account_id, - self.target_account_id, - self.assets, - note_type, - NoteAttachments::empty(), - rng, - ) + Ok(P2idNote::builder() + .sender(self.sender_account_id) + .target(self.target_account_id) + .assets(self.assets) + .note_type(note_type) + .generate_serial_number(rng) + .build()? + .into()) } else { // Create a P2IDE note - P2ideNote::create( - self.sender_account_id, - P2ideNoteStorage::new( - self.target_account_id, - self.reclaim_height, - self.timelock_height, - ), - self.assets, - note_type, - NoteAttachments::empty(), - rng, - ) + Ok(P2ideNote::builder() + .sender(self.sender_account_id) + .target(self.target_account_id) + .assets(self.assets) + .note_type(note_type) + .maybe_reclaim_height(self.reclaim_height) + .maybe_timelock_height(self.timelock_height) + .generate_serial_number(rng) + .build()? + .into()) } } } diff --git a/crates/rust-client/src/transaction/request/mod.rs b/crates/rust-client/src/transaction/request/mod.rs index 9fd669733f..1dd8a4e3d2 100644 --- a/crates/rust-client/src/transaction/request/mod.rs +++ b/crates/rust-client/src/transaction/request/mod.rs @@ -4,9 +4,10 @@ use alloc::boxed::Box; use alloc::collections::{BTreeMap, BTreeSet}; use alloc::string::{String, ToString}; use alloc::vec::Vec; +use core::num::NonZeroU16; use miden_protocol::Word; -use miden_protocol::account::AccountId; +use miden_protocol::account::{AccountCodeInterface, AccountId}; use miden_protocol::asset::{Asset, NonFungibleAsset}; use miden_protocol::crypto::merkle::MerkleError; use miden_protocol::crypto::merkle::store::MerkleStore; @@ -31,8 +32,8 @@ use miden_protocol::note::{ }; use miden_protocol::transaction::{InputNote, InputNotes, TransactionArgs, TransactionScript}; use miden_protocol::vm::AdviceMap; -use miden_standards::account::interface::{AccountInterface, AccountInterfaceError}; use miden_standards::errors::CodeBuilderError; +use miden_standards::tx_script::{SendNotesTransactionScript, SendNotesTransactionScriptError}; use miden_tx::utils::serde::{ ByteReader, ByteWriter, @@ -339,14 +340,20 @@ impl TransactionRequest { /// [`Client::code_builder`](crate::Client::code_builder)). pub(crate) fn build_transaction_script( &self, - account_interface: &AccountInterface, + code_interface: &AccountCodeInterface, ) -> Result, TransactionRequestError> { match &self.script_template { Some(TransactionScriptTemplate::CustomScript(script)) => Ok(Some(script.clone())), Some(TransactionScriptTemplate::SendNotes(notes)) => { - // TODO: We could pass `SourceManager` to this call, but it needs to be supported - // in the protocol struct (however, this should also not fail to build often) - Ok(Some(account_interface.build_send_notes_script(notes, self.expiration_delta)?)) + let script = match self.expiration_delta.and_then(NonZeroU16::new) { + Some(delta) => SendNotesTransactionScript::with_expiration_delta( + code_interface, + notes, + delta, + )?, + None => SendNotesTransactionScript::new(code_interface, notes)?, + }; + Ok(Some(script.into())) }, None => Ok(None), } @@ -481,14 +488,16 @@ impl Default for TransactionRequestBuilder { // Errors related to a [TransactionRequest] #[derive(Debug, Error)] pub enum TransactionRequestError { - #[error("account interface error")] - AccountInterfaceError(#[from] AccountInterfaceError), + #[error("failed to build the send-notes transaction script")] + SendNotesTransactionScriptError(#[from] SendNotesTransactionScriptError), #[error("account error")] AccountError(#[from] AccountError), #[error("asset error")] AssetError(#[from] AssetError), #[error("duplicate input note: note {0} was added more than once to the transaction")] DuplicateInputNote(NoteId), + #[error("transaction expiration delta must be greater than zero")] + ZeroExpirationDelta, #[error( "the account proof does not contain the required foreign account data; re-fetch the proof and retry" )] @@ -567,7 +576,7 @@ mod tests { }; use miden_protocol::asset::FungibleAsset; use miden_protocol::crypto::rand::{FeltRng, RandomCoin}; - use miden_protocol::note::{NoteAttachments, NoteTag, NoteType}; + use miden_protocol::note::{NoteTag, NoteType}; use miden_protocol::testing::account_id::{ ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, @@ -614,16 +623,15 @@ mod tests { let mut notes = vec![]; for i in 0..6 { - let note = P2idNote::create( - sender_id, - target_id, - vec![FungibleAsset::new(faucet_id, 100 + i).unwrap().into()], - NoteType::Private, - NoteAttachments::empty(), - &mut rng, - ) - .unwrap(); - notes.push(note); + let note = P2idNote::builder() + .sender(sender_id) + .target(target_id) + .assets(vec![FungibleAsset::new(faucet_id, 100 + i).unwrap()]) + .note_type(NoteType::Private) + .generate_serial_number(&mut rng) + .build() + .expect("note creation failed"); + notes.push(note.into()); } let mut advice_vec: Vec<(Word, Vec)> = vec![]; diff --git a/crates/rust-client/src/transaction/result.rs b/crates/rust-client/src/transaction/result.rs index a6e4e57b4c..b4ac394462 100644 --- a/crates/rust-client/src/transaction/result.rs +++ b/crates/rust-client/src/transaction/result.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; -use miden_protocol::account::AccountDelta; +use miden_protocol::account::AccountPatch; use miden_protocol::block::BlockNumber; use miden_protocol::note::{NoteDetails, NoteTag}; use miden_protocol::transaction::{ @@ -81,9 +81,9 @@ impl TransactionResult { self.transaction.tx_inputs() } - /// Returns the [`AccountDelta`] that describes the change of state for the executing account. - pub fn account_delta(&self) -> &AccountDelta { - self.transaction.account_delta() + /// Returns the [`AccountPatch`] that describes the change of state for the executing account. + pub fn account_patch(&self) -> &AccountPatch { + self.transaction.account_patch() } /// Returns input notes that were consumed as part of the transaction. diff --git a/crates/sqlite-store/src/account/accounts.rs b/crates/sqlite-store/src/account/accounts.rs index 37878354d6..65a7980191 100644 --- a/crates/sqlite-store/src/account/accounts.rs +++ b/crates/sqlite-store/src/account/accounts.rs @@ -9,9 +9,9 @@ use std::vec::Vec; use miden_client::account::{ Account, AccountCode, - AccountDelta, AccountHeader, AccountId, + AccountPatch, AccountStorage, Address, PartialAccount, @@ -22,7 +22,7 @@ use miden_client::account::{ StorageSlotName, StorageSlotType, }; -use miden_client::asset::{Asset, AssetVault, AssetWitness, FungibleAsset}; +use miden_client::asset::{Asset, AssetVault, AssetWitness}; use miden_client::store::{ AccountRecord, AccountRecordData, @@ -440,33 +440,31 @@ impl SqliteStore { /// Applies the account delta to the account state, updating the vault and storage maps. /// /// Archives old values from latest to historical and updates latest via INSERT OR REPLACE. - pub(crate) fn apply_account_delta( + pub(crate) fn apply_account_patch( tx: &Transaction<'_>, smt_forest: &mut AccountSmtForest, init_account_state: &AccountHeader, final_account_state: &AccountHeader, - updated_fungible_assets: BTreeMap, old_map_roots: &BTreeMap, - delta: &AccountDelta, + patch: &AccountPatch, ) -> Result<(), StoreError> { let account_id = final_account_state.id(); // Archive old header and insert the new one Self::replace_account_header(tx, final_account_state, init_account_state)?; - Self::apply_account_vault_delta( + Self::apply_account_vault_patch( tx, smt_forest, account_id, init_account_state, final_account_state, - updated_fungible_assets, - delta, + patch.vault(), )?; // Build the final roots from the init state's registered roots: // - Replace vault root with the final one - // - Replace changed map roots with their new values (done by apply_account_storage_delta) + // - Replace changed map roots with their new values (done by apply_account_storage_patch) // - Unchanged map roots continue as they were let mut final_roots = smt_forest .get_roots(&init_account_state.id()) @@ -480,9 +478,9 @@ impl SqliteStore { let default_map_root = StorageMap::default().root(); let updated_storage_slots = - Self::apply_account_storage_delta(smt_forest, old_map_roots, delta)?; + Self::apply_account_storage_patch(smt_forest, old_map_roots, patch.storage())?; - // Update map roots in final_roots with new values from the delta + // Update map roots in final_roots with new values from the patch for (slot_name, (new_root, slot_type)) in &updated_storage_slots { if *slot_type == StorageSlotType::Map { let old_root = old_map_roots.get(slot_name).copied().unwrap_or(default_map_root); @@ -495,12 +493,12 @@ impl SqliteStore { } } - Self::write_storage_delta( + Self::write_storage_patch( tx, account_id, final_account_state.nonce().as_canonical_u64(), &updated_storage_slots, - delta, + patch.storage(), )?; smt_forest.stage_roots(final_account_state.id(), final_roots); @@ -852,11 +850,11 @@ impl SqliteStore { } /// Applies an incremental delta to a public account's state during sync. - pub(crate) fn apply_sync_account_delta( + pub(crate) fn apply_sync_account_patch( tx: &Transaction<'_>, smt_forest: &mut AccountSmtForest, new_header: &AccountHeader, - delta: &AccountDelta, + patch: &AccountPatch, ) -> Result<(), StoreError> { let account_id = new_header.id(); @@ -867,23 +865,19 @@ impl SqliteStore { .map(|(header, ..)| header) .ok_or(StoreError::AccountDataNotFound(account_id))?; - // Read the fungible assets that will be affected by the delta. - // Transaction derefs to Connection, so we can pass it where Connection is expected. - let updated_fungible_assets = - Self::get_account_fungible_assets_for_delta(tx, account_id, delta)?; + if new_header.nonce().as_canonical_u64() <= init_header.nonce().as_canonical_u64() { + return Err(StoreError::DatabaseError(format!( + "apply_sync_account_patch: new nonce {} is not greater than local nonce {} for account {}", + new_header.nonce().as_canonical_u64(), + init_header.nonce().as_canonical_u64(), + account_id, + ))); + } - // Read the old map roots for slots affected by the delta. - let old_map_roots = Self::get_storage_map_roots_for_delta(tx, account_id, delta)?; + // Transaction derefs to Connection, so we can pass it where Connection is expected. + let old_map_roots = Self::get_storage_map_roots_for_patch(tx, account_id, patch.storage())?; - Self::apply_account_delta( - tx, - smt_forest, - &init_header, - new_header, - updated_fungible_assets, - &old_map_roots, - delta, - ) + Self::apply_account_patch(tx, smt_forest, &init_header, new_header, &old_map_roots, patch) } /// Locks the account if the mismatched digest doesn't belong to a previous account state (stale diff --git a/crates/sqlite-store/src/account/mod.rs b/crates/sqlite-store/src/account/mod.rs index b909e4518d..f0f39a97e7 100644 --- a/crates/sqlite-store/src/account/mod.rs +++ b/crates/sqlite-store/src/account/mod.rs @@ -3,7 +3,7 @@ #![allow(clippy::items_after_statements)] mod accounts; -mod helpers; +pub(crate) mod helpers; mod storage; mod vault; diff --git a/crates/sqlite-store/src/account/storage.rs b/crates/sqlite-store/src/account/storage.rs index 1a069c4096..5aa9cbb06c 100644 --- a/crates/sqlite-store/src/account/storage.rs +++ b/crates/sqlite-store/src/account/storage.rs @@ -6,8 +6,8 @@ use std::string::ToString; use std::vec::Vec; use miden_client::account::{ - AccountDelta, AccountId, + AccountStoragePatch, StorageMap, StorageSlot, StorageSlotContent, @@ -31,13 +31,12 @@ impl SqliteStore { /// Only queries the slot values (roots) from the latest storage table, avoiding the need to /// load full storage map entries into memory. The `AccountSmtForest` handles the actual /// Merkle tree operations. - pub(crate) fn get_storage_map_roots_for_delta( + pub(crate) fn get_storage_map_roots_for_patch( conn: &rusqlite::Connection, account_id: AccountId, - delta: &AccountDelta, + storage_patch: &AccountStoragePatch, ) -> Result, StoreError> { - let map_slot_names: Vec = delta - .storage() + let map_slot_names: Vec = storage_patch .maps() .map(|(slot_name, _)| Value::Text(slot_name.to_string())) .collect(); @@ -124,12 +123,12 @@ impl SqliteStore { /// For each changed slot, the old value is read from latest and archived to historical. /// NULL `old_slot_value` means the slot was new. For map entries, the old entry value is /// similarly archived before updating latest. - pub(crate) fn write_storage_delta( + pub(crate) fn write_storage_patch( tx: &Transaction<'_>, account_id: AccountId, nonce: u64, updated_slots: &BTreeMap, - delta: &AccountDelta, + storage_patch: &AccountStoragePatch, ) -> Result<(), StoreError> { const LATEST_SLOT_QUERY: &str = insert_sql!( latest_account_storage { @@ -169,12 +168,11 @@ impl SqliteStore { let account_id_hex = account_id.to_hex(); let nonce_val = u64_to_value(nonce); - // Collect the delta's changed map entries for efficient lookup - let delta_map_entries: BTreeMap<&StorageSlotName, Vec<(Word, Word)>> = delta - .storage() + // Collect the patch's changed map entries for efficient lookup + let patch_map_entries: BTreeMap<&StorageSlotName, Vec<(Word, Word)>> = storage_patch .maps() - .map(|(slot_name, map_delta)| { - let entries: Vec<(Word, Word)> = map_delta + .map(|(slot_name, map_patch)| { + let entries: Vec<(Word, Word)> = map_patch .entries() .iter() .map(|(key, value)| ((*key).into(), *value)) @@ -213,7 +211,7 @@ impl SqliteStore { .execute(params![&account_id_hex, &slot_name_str, &slot_value_hex, slot_type_val]) .into_store_error()?; - if let Some(changed_entries) = delta_map_entries.get(slot_name) { + if let Some(changed_entries) = patch_map_entries.get(slot_name) { Self::write_map_entry_delta( tx, &mut latest_map_stmt, @@ -290,23 +288,22 @@ impl SqliteStore { /// root is used to update the SMT forest with the delta entries, producing the new root. /// Full storage maps are never loaded into memory — the `AccountSmtForest` handles all /// Merkle tree operations. - pub(crate) fn apply_account_storage_delta( + pub(crate) fn apply_account_storage_patch( smt_forest: &mut AccountSmtForest, old_map_roots: &BTreeMap, - delta: &AccountDelta, + storage_patch: &AccountStoragePatch, ) -> Result, StoreError> { - let mut updated_slots: BTreeMap = delta - .storage() + let mut updated_slots: BTreeMap = storage_patch .values() .map(|(slot_name, value)| (slot_name.clone(), (*value, StorageSlotType::Value))) .collect(); let default_map_root = StorageMap::default().root(); - for (slot_name, map_delta) in delta.storage().maps() { + for (slot_name, map_patch) in storage_patch.maps() { let old_root = old_map_roots.get(slot_name).copied().unwrap_or(default_map_root); let entries: Vec<_> = - map_delta.entries().iter().map(|(key, value)| (*key, *value)).collect(); + map_patch.entries().iter().map(|(key, value)| (*key, *value)).collect(); let new_root = smt_forest.update_storage_map_nodes(old_root, entries.into_iter())?; updated_slots.insert(slot_name.clone(), (new_root, StorageSlotType::Map)); diff --git a/crates/sqlite-store/src/account/tests.rs b/crates/sqlite-store/src/account/tests.rs index 25b3752844..8a1067d2fe 100644 --- a/crates/sqlite-store/src/account/tests.rs +++ b/crates/sqlite-store/src/account/tests.rs @@ -8,10 +8,12 @@ use miden_client::account::{ AccountBuilder, AccountBuilderSchemaCommitmentExt, AccountCode, - AccountDelta, AccountHeader, AccountId, + AccountPatch, + AccountStoragePatch, AccountType, + AccountVaultPatch, Address, StorageMap, StorageMapKey, @@ -20,14 +22,7 @@ use miden_client::account::{ StorageSlotName, }; use miden_client::assembly::CodeBuilder; -use miden_client::asset::{ - AccountStorageDelta, - AccountVaultDelta, - Asset, - FungibleAsset, - NonFungibleAsset, - NonFungibleAssetDetails, -}; +use miden_client::asset::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails}; use miden_client::auth::{AuthSchemeId, AuthSingleSig, PublicKeyCommitment}; use miden_client::store::{ClientAccountType, Store, StoreError}; use miden_client::testing::common::ACCOUNT_ID_REGULAR; @@ -94,7 +89,7 @@ async fn account_code_insertion_no_duplicates() -> anyhow::Result<()> { } #[tokio::test] -async fn apply_account_delta_additions() -> anyhow::Result<()> { +async fn apply_account_patch_additions() -> anyhow::Result<()> { let store = create_test_store().await; let value_slot_name = @@ -124,55 +119,53 @@ async fn apply_account_delta_additions() -> anyhow::Result<()> { AuthSchemeId::Falcon512Poseidon2, )) .with_component(dummy_component) - .build_with_schema_commitment()?; + .build_existing()?; let default_address = Address::new(account.id()); store .insert_account(&account, default_address, ClientAccountType::Native) .await?; - let mut storage_delta = AccountStorageDelta::new(); - storage_delta.set_item(value_slot_name.clone(), [ZERO, ZERO, ZERO, ONE].into())?; - storage_delta.set_map_item( + let mut storage_patch = AccountStoragePatch::new(); + storage_patch.set_item(value_slot_name.clone(), [ZERO, ZERO, ZERO, ONE].into())?; + storage_patch.set_map_item( map_slot_name.clone(), StorageMapKey::new([ONE, ZERO, ZERO, ZERO].into()), [ONE, ONE, ONE, ONE].into(), )?; - let vault_delta = AccountVaultDelta::from_iters( - vec![ - FungibleAsset::new(AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?, 100)? - .into(), - NonFungibleAsset::new(&NonFungibleAssetDetails::new( - AccountId::try_from(ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET)?, - NON_FUNGIBLE_ASSET_DATA.into(), - )) - .into(), - ], - [], - ); + // The account starts with an empty vault, so the absolute values of the added assets are the + // assets themselves. + let vault_patch = AccountVaultPatch::with_assets([ + FungibleAsset::new(AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?, 100)?.into(), + NonFungibleAsset::new(&NonFungibleAssetDetails::new( + AccountId::try_from(ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET)?, + NON_FUNGIBLE_ASSET_DATA.into(), + )) + .into(), + ]); - let delta = AccountDelta::new(account.id(), storage_delta, vault_delta, ONE)?; + let patch = + AccountPatch::new(account.id(), storage_patch, vault_patch, None, Some(Felt::from(2u32)))?; - let mut account_after_delta = account.clone(); - account_after_delta.apply_delta(&delta)?; + let mut account_after_patch = account.clone(); + account_after_patch.apply_patch(&patch)?; let account_id = account.id(); - let final_state: AccountHeader = (&account_after_delta).into(); + let final_state: AccountHeader = (&account_after_patch).into(); let smt_forest = store.smt_forest.clone(); store .interact_with_connection(move |conn| { let tx = conn.transaction().into_store_error()?; let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned"); - SqliteStore::apply_account_delta( + SqliteStore::apply_account_patch( &tx, &mut smt_forest, &account.into(), &final_state, - BTreeMap::default(), &BTreeMap::new(), - &delta, + &patch, )?; tx.commit().into_store_error()?; @@ -186,7 +179,7 @@ async fn apply_account_delta_additions() -> anyhow::Result<()> { .context("failed to find inserted account")? .try_into()?; - assert_eq!(updated_account, account_after_delta); + assert_eq!(updated_account, account_after_patch); // The untouched second map slot must still be empty despite sharing the same // initial root as the modified map slot. @@ -204,21 +197,20 @@ async fn apply_account_delta_additions() -> anyhow::Result<()> { Ok(()) } -/// Regression test: applying a fungible vault delta must preserve the asset's +/// Regression test: applying a fungible vault patch must preserve the asset's /// [`AssetCallbackFlag`]. /// /// The callback flag is part of an asset's vault key *and* value encoding, so if the store -/// drops it while replaying a delta, the locally recomputed vault root diverges from the one -/// the transaction kernel produced (which carries the flag, exactly as -/// `AssetVault::apply_delta` does in miden-protocol). That divergence surfaces as a -/// `MerkleStoreError`/`ConflictingRoots` when `apply_account_vault_delta` compares the +/// drops it while applying a patch, the locally recomputed vault root diverges from the one +/// the transaction kernel produced (which carries the flag). That divergence surfaces as a +/// `MerkleStoreError`/`ConflictingRoots` when `apply_account_vault_patch` compares the /// recomputed root against `final_account_state.vault_root()`. /// /// Callback-bearing fungible assets are produced by agglayer faucets (B2AGG), so this path /// is exercised when a wallet consumes an agglayer-minted note. Ordinary assets use the /// disabled flag, where preserving it is a no-op — which is why only agglayer hit the bug. #[tokio::test] -async fn apply_account_delta_preserves_fungible_callback_flag() -> anyhow::Result<()> { +async fn apply_account_patch_preserves_fungible_callback_flag() -> anyhow::Result<()> { let store = create_test_store().await; // Create and insert an account with an empty vault. @@ -240,16 +232,25 @@ async fn apply_account_delta_preserves_fungible_callback_flag() -> anyhow::Resul .with_callbacks(AssetCallbackFlag::Enabled) .into(); - let vault_delta = AccountVaultDelta::from_iters(vec![callback_asset], []); - let delta = AccountDelta::new(account.id(), AccountStorageDelta::new(), vault_delta, ONE)?; + // The account starts with an empty vault, so the absolute value of the added asset is the + // asset itself. + let mut vault_patch = AccountVaultPatch::default(); + vault_patch.insert_asset(callback_asset); + let patch = AccountPatch::new( + account.id(), + AccountStoragePatch::new(), + vault_patch, + None, + Some(Felt::from(2u32)), + )?; - // `apply_delta` uses miden-protocol's `AssetVault::apply_delta`, which preserves the - // callback flag; the resulting header carries the authoritative (with-callback) vault root. - let mut account_after_delta = account.clone(); - account_after_delta.apply_delta(&delta)?; + // `apply_patch` preserves the callback flag, the resulting header carries the authoritative + // (with-callback) vault root. + let mut account_after_patch = account.clone(); + account_after_patch.apply_patch(&patch)?; let account_id = account.id(); - let final_state: AccountHeader = (&account_after_delta).into(); + let final_state: AccountHeader = (&account_after_patch).into(); let expected_vault_root = final_state.vault_root(); let smt_forest = store.smt_forest.clone(); store @@ -259,14 +260,13 @@ async fn apply_account_delta_preserves_fungible_callback_flag() -> anyhow::Resul // Without preserving the callback flag this fails with a `ConflictingRoots` // merkle store error (recomputed root != final_state.vault_root()). - SqliteStore::apply_account_delta( + SqliteStore::apply_account_patch( &tx, &mut smt_forest, &account.into(), &final_state, - BTreeMap::default(), &BTreeMap::new(), - &delta, + &patch, )?; tx.commit().into_store_error()?; @@ -280,14 +280,14 @@ async fn apply_account_delta_preserves_fungible_callback_flag() -> anyhow::Resul .context("failed to find inserted account")? .try_into()?; - assert_eq!(updated_account, account_after_delta); + assert_eq!(updated_account, account_after_patch); assert_eq!(updated_account.vault().root(), expected_vault_root); Ok(()) } #[tokio::test] -async fn apply_account_delta_removals() -> anyhow::Result<()> { +async fn apply_account_patch_removals() -> anyhow::Result<()> { let store = create_test_store().await; let value_slot_name = @@ -331,42 +331,47 @@ async fn apply_account_delta_removals() -> anyhow::Result<()> { .insert_account(&account, default_address, ClientAccountType::Native) .await?; - let mut storage_delta = AccountStorageDelta::new(); - storage_delta.set_item(value_slot_name.clone(), EMPTY_WORD)?; - storage_delta.set_map_item( + let mut storage_patch = AccountStoragePatch::new(); + // A cleared value slot is represented by an empty value. + storage_patch.set_item(value_slot_name.clone(), EMPTY_WORD)?; + // A removed map entry is represented by an empty value for the key. + storage_patch.set_map_item( map_slot_name.clone(), StorageMapKey::new([ONE, ZERO, ZERO, ZERO].into()), EMPTY_WORD, )?; - let vault_delta = AccountVaultDelta::from_iters([], assets.clone()); + // Both assets are removed: the absolute final state is an empty vault, so each asset's vault + // key is marked as removed. + let mut vault_patch = AccountVaultPatch::default(); + for asset in &assets { + vault_patch.remove_asset(asset.vault_key()); + } - let delta = AccountDelta::new(account.id(), storage_delta, vault_delta, ONE)?; + let patch = + AccountPatch::new(account.id(), storage_patch, vault_patch, None, Some(Felt::from(2u32)))?; - let mut account_after_delta = account.clone(); - account_after_delta.apply_delta(&delta)?; + let mut account_after_patch = account.clone(); + account_after_patch.apply_patch(&patch)?; let account_id = account.id(); - let final_state: AccountHeader = (&account_after_delta).into(); + let final_state: AccountHeader = (&account_after_patch).into(); let smt_forest = store.smt_forest.clone(); store .interact_with_connection(move |conn| { - let fungible_assets = - SqliteStore::get_account_fungible_assets_for_delta(conn, account.id(), &delta)?; let old_map_roots = - SqliteStore::get_storage_map_roots_for_delta(conn, account.id(), &delta)?; + SqliteStore::get_storage_map_roots_for_patch(conn, account.id(), patch.storage())?; let tx = conn.transaction().into_store_error()?; let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned"); - SqliteStore::apply_account_delta( + SqliteStore::apply_account_patch( &tx, &mut smt_forest, &account.into(), &final_state, - fungible_assets, &old_map_roots, - &delta, + &patch, )?; tx.commit().into_store_error()?; @@ -380,7 +385,7 @@ async fn apply_account_delta_removals() -> anyhow::Result<()> { .context("failed to find inserted account")? .try_into()?; - assert_eq!(updated_account, account_after_delta); + assert_eq!(updated_account, account_after_patch); assert!(updated_account.vault().is_empty()); assert_eq!(updated_account.storage().get_item(&value_slot_name)?, EMPTY_WORD); let map_slot = updated_account @@ -763,33 +768,33 @@ async fn prune_account_history_removes_old_committed_states() -> anyhow::Result< let store = create_test_store().await; let map_slot_name = StorageSlotName::new("test::prune::map").expect("valid slot name"); - // Insert account with 5 map entries (nonce 0) + // Insert account with 5 map entries (nonce 1) let mut account = setup_account_with_map(&store, 5, &map_slot_name).await?; let account_id = account.id(); - // Apply delta 1 (nonce 0 to 1, delta increment = 1) - apply_single_entry_update(&store, &mut account, &map_slot_name, 1).await?; + // Apply patch 1 (nonce 1 to 2) + apply_single_entry_update(&store, &mut account, &map_slot_name, 2).await?; - // Apply delta 2 (nonce 1 to 2, delta increment = 1) - apply_single_entry_update(&store, &mut account, &map_slot_name, 1).await?; + // Apply patch 2 (nonce 2 to 3) + apply_single_entry_update(&store, &mut account, &map_slot_name, 3).await?; - // Before prune: 2 historical headers (nonce 0, 1). - // The latest state (nonce 2) is in latest_account_headers, not historical. + // Before prune: 2 historical headers (nonce 1, 2). + // The latest state (nonce 3) is in latest_account_headers, not historical. let m = get_storage_metrics(&store).await; assert_eq!(m.historical_account_headers, 2); assert!(m.historical_storage_map_entries > 0); - // Prune up to nonce 1 (should delete nonce 0 historical entry) + // Prune up to nonce 2 (should delete the nonce-1 historical entry, replaced_at_nonce = 2) let deleted = store .interact_with_connection(move |conn| { - SqliteStore::prune_account_history(conn, account_id, Felt::from(1u32)) + SqliteStore::prune_account_history(conn, account_id, Felt::from(2u32)) }) .await?; assert!(deleted > 0, "Should have deleted some rows"); - // After prune: only 1 historical header remains (nonce 1, replaced_at_nonce = 2). - // Nonce 2 is in latest_account_headers (not historical). + // After prune: only 1 historical header remains (nonce 2, replaced_at_nonce = 3). + // Nonce 3 is in latest_account_headers (not historical). let m = get_storage_metrics(&store).await; assert_eq!(m.historical_account_headers, 1); @@ -797,7 +802,7 @@ async fn prune_account_history_removes_old_committed_states() -> anyhow::Result< assert_eq!(m.latest_account_headers, 1); assert!(m.latest_storage_map_entries > 0); - // The remaining historical header should be nonce 1 + // The remaining historical header should be nonce 2 let remaining_nonce: u64 = store .interact_with_connection(move |conn| { conn.query_row( @@ -808,7 +813,7 @@ async fn prune_account_history_removes_old_committed_states() -> anyhow::Result< .into_store_error() }) .await?; - assert_eq!(remaining_nonce, 1); + assert_eq!(remaining_nonce, 2); // Account data should still be fully readable let account_record = store.get_account(account_id).await?; @@ -822,16 +827,16 @@ async fn prune_account_history_noop_with_single_state() -> anyhow::Result<()> { let store = create_test_store().await; let map_slot_name = StorageSlotName::new("test::prune_noop::map").expect("valid slot name"); - // Insert account (nonce 0 only) + // Insert account (nonce 1 only) let account = setup_account_with_map(&store, 3, &map_slot_name).await?; let account_id = account.id(); let m_before = get_storage_metrics(&store).await; - // Prune with nonce 0: no historical entries have replaced_at_nonce <= 0 + // Prune with nonce 1: no historical entries have replaced_at_nonce <= 1 let deleted = store .interact_with_connection(move |conn| { - SqliteStore::prune_account_history(conn, account_id, Felt::from(0u32)) + SqliteStore::prune_account_history(conn, account_id, Felt::from(1u32)) }) .await?; @@ -849,11 +854,11 @@ async fn prune_account_history_multiple_accounts() -> anyhow::Result<()> { let map_slot_name_a = StorageSlotName::new("test::prune_all::map_a").expect("valid slot name"); let map_slot_name_b = StorageSlotName::new("test::prune_all::map_b").expect("valid slot name"); - // Account A: nonce 0 to 1 to 2 + // Account A: nonce 1 to 2 to 3 let mut account_a = setup_account_with_map(&store, 3, &map_slot_name_a).await?; let a_id = account_a.id(); - apply_single_entry_update(&store, &mut account_a, &map_slot_name_a, 1).await?; - apply_single_entry_update(&store, &mut account_a, &map_slot_name_a, 1).await?; + apply_single_entry_update(&store, &mut account_a, &map_slot_name_a, 2).await?; + apply_single_entry_update(&store, &mut account_a, &map_slot_name_a, 3).await?; // Account B: different seed to different account. We need a different builder seed. let component_b = AccountComponent::new( @@ -868,35 +873,35 @@ async fn prune_account_history_multiple_accounts() -> anyhow::Result<()> { AuthSchemeId::Falcon512Poseidon2, )) .with_component(component_b) - .build_with_schema_commitment()?; + .build_existing()?; let b_id = account_b.id(); store .insert_account(&account_b, Address::new(account_b.id()), ClientAccountType::Native) .await?; let mut account_b_mut = account_b.clone(); - apply_single_entry_update(&store, &mut account_b_mut, &map_slot_name_b, 1).await?; + apply_single_entry_update(&store, &mut account_b_mut, &map_slot_name_b, 2).await?; - // Before prune: 2 headers for A (nonce 0, 1) + 1 for B (nonce 0) = 3. + // Before prune: 2 headers for A (nonce 1, 2) + 1 for B (nonce 1) = 3. // Latest states are in latest_account_headers, not historical. let m = get_storage_metrics(&store).await; assert_eq!(m.historical_account_headers, 3); - // Prune account A up to nonce 1, account B up to nonce 1 + // Prune account A up to nonce 2, account B up to nonce 2 let deleted_a = store .interact_with_connection(move |conn| { - SqliteStore::prune_account_history(conn, a_id, Felt::from(1u32)) + SqliteStore::prune_account_history(conn, a_id, Felt::from(2u32)) }) .await?; let deleted_b = store .interact_with_connection(move |conn| { - SqliteStore::prune_account_history(conn, b_id, Felt::from(1u32)) + SqliteStore::prune_account_history(conn, b_id, Felt::from(2u32)) }) .await?; assert!(deleted_a + deleted_b > 0); - // After prune: 1 header for A (nonce 1) + 0 for B (nonce 0 was replaced_at_nonce 1, pruned) + // After prune: 1 header for A (nonce 2) + 0 for B (nonce 1 was replaced_at_nonce 2, pruned) let m = get_storage_metrics(&store).await; assert!(m.historical_account_headers <= 2); @@ -912,15 +917,15 @@ async fn prune_removes_orphaned_account_code() -> anyhow::Result<()> { let store = create_test_store().await; let map_slot_name = StorageSlotName::new("test::prune_code::map").expect("valid slot name"); - // Insert account with map entries (nonce 0), then apply a delta (nonce 0 to 1). - // The delta creates a historical header at nonce 0 whose code_commitment points + // Insert account with map entries (nonce 1), then apply a patch (nonce 1 to 2). + // The patch creates a historical header at nonce 1 whose code_commitment points // to the original account code. let mut account = setup_account_with_map(&store, 2, &map_slot_name).await?; let account_id = account.id(); - apply_single_entry_update(&store, &mut account, &map_slot_name, 1).await?; + apply_single_entry_update(&store, &mut account, &map_slot_name, 2).await?; - // Simulate the nonce-1 state having a different code commitment by updating - // the latest header's code_commitment directly. This makes the nonce-0 + // Simulate the nonce-2 state having a different code commitment by updating + // the latest header's code_commitment directly. This makes the nonce-1 // historical header the only reference to the original code. let original_code_commitment: String = store .interact_with_connection(move |conn| { @@ -958,11 +963,11 @@ async fn prune_removes_orphaned_account_code() -> anyhow::Result<()> { }) .await?; - // Prune nonce-0 history: the historical header referencing original_code_commitment + // Prune nonce-1 history: the historical header referencing original_code_commitment // is deleted, and since no other header references it, the code should be removed. let deleted = store .interact_with_connection(move |conn| { - SqliteStore::prune_account_history(conn, account_id, Felt::from(1u32)) + SqliteStore::prune_account_history(conn, account_id, Felt::from(2u32)) }) .await?; assert!(deleted > 0); @@ -1075,7 +1080,7 @@ async fn setup_account_with_map( AuthSchemeId::Falcon512Poseidon2, )) .with_component(component) - .build_with_schema_commitment()?; + .build_existing()?; store .insert_account(&account, Address::new(account.id()), ClientAccountType::Native) @@ -1084,48 +1089,52 @@ async fn setup_account_with_map( } /// Applies a delta that changes a single map entry (key=1) and persists it. +/// `target_nonce` must be strictly greater than the account's current nonce. async fn apply_single_entry_update( store: &SqliteStore, account: &mut Account, map_slot_name: &StorageSlotName, - nonce: u64, + target_nonce: u64, ) -> anyhow::Result<()> { - let mut storage_delta = AccountStorageDelta::new(); - storage_delta.set_map_item( + let mut storage_patch = AccountStoragePatch::new(); + storage_patch.set_map_item( map_slot_name.clone(), StorageMapKey::new([Felt::from(1u32), ZERO, ZERO, ZERO].into()), - [Felt::new_unchecked(nonce * 1000), ZERO, ZERO, ZERO].into(), + [Felt::new_unchecked(target_nonce * 1000), ZERO, ZERO, ZERO].into(), )?; - let delta = AccountDelta::new( + let patch = AccountPatch::new( account.id(), - storage_delta, - AccountVaultDelta::from_iters([], []), - Felt::new_unchecked(nonce), + storage_patch, + AccountVaultPatch::default(), + None, + Some(Felt::new_unchecked(target_nonce)), )?; let prev_header: AccountHeader = (&*account).into(); - account.apply_delta(&delta)?; + account.apply_patch(&patch)?; let final_header: AccountHeader = (&*account).into(); let smt_forest = store.smt_forest.clone(); - let delta_clone = delta.clone(); + let patch_clone = patch.clone(); let account_id = account.id(); store .interact_with_connection(move |conn| { - let old_map_roots = - SqliteStore::get_storage_map_roots_for_delta(conn, account_id, &delta_clone)?; + let old_map_roots = SqliteStore::get_storage_map_roots_for_patch( + conn, + account_id, + patch_clone.storage(), + )?; let tx = conn.transaction().into_store_error()?; let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned"); - SqliteStore::apply_account_delta( + SqliteStore::apply_account_patch( &tx, &mut smt_forest, &prev_header, &final_header, - BTreeMap::default(), &old_map_roots, - &delta, + &patch, )?; tx.commit().into_store_error()?; @@ -1141,71 +1150,73 @@ async fn apply_single_entry_update( /// Verifies that `undo_account_state` correctly reverts the latest tables to the previous state. /// -/// The delta includes both storage and vault changes so that the vault root changes between -/// nonce 0 and nonce 1. This is required because `undo_account_state` pops SMT roots from the +/// The patch includes both storage and vault changes so that the vault root changes between +/// nonce 1 and nonce 2. This is required because `undo_account_state` pops SMT roots from the /// forest, and the vault root must differ to avoid removing the initial state's root. #[tokio::test] async fn undo_account_state_restores_previous_latest() -> anyhow::Result<()> { let store = create_test_store().await; let map_slot_name = StorageSlotName::new("test::undo::map").expect("valid slot name"); - // Insert account with 5 map entries (nonce 0) + // Insert account with 5 map entries (nonce 1) let mut account = setup_account_with_map(&store, 5, &map_slot_name).await?; let initial_commitment = account.to_commitment(); - // Apply a delta (nonce 1) that changes a map entry AND adds a fungible asset. - // The vault change ensures the vault root differs between nonce 0 and 1, + // Apply a patch (nonce 2) that changes a map entry AND adds a fungible asset. + // The vault change ensures the vault root differs between nonce 1 and 2, // which is needed for pop_roots to work correctly. - let mut storage_delta = AccountStorageDelta::new(); - storage_delta.set_map_item( + let mut storage_patch = AccountStoragePatch::new(); + storage_patch.set_map_item( map_slot_name.clone(), StorageMapKey::new([Felt::from(1u32), ZERO, ZERO, ZERO].into()), [Felt::from(1000u32), ZERO, ZERO, ZERO].into(), )?; - let vault_delta = AccountVaultDelta::from_iters( - vec![ - FungibleAsset::new(AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?, 100)? - .into(), - ], - [], + // The account starts with an empty vault, so the absolute value of the added asset is the + // asset itself. + let mut vault_patch = AccountVaultPatch::default(); + vault_patch.insert_asset( + FungibleAsset::new(AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?, 100)?.into(), ); - let delta = AccountDelta::new(account.id(), storage_delta, vault_delta, ONE)?; + let patch = + AccountPatch::new(account.id(), storage_patch, vault_patch, None, Some(Felt::from(2u32)))?; let prev_header: AccountHeader = (&account).into(); - account.apply_delta(&delta)?; + account.apply_patch(&patch)?; let final_header: AccountHeader = (&account).into(); - let post_delta_commitment = account.to_commitment(); + let post_patch_commitment = account.to_commitment(); let smt_forest = store.smt_forest.clone(); let account_id = account.id(); - let delta_clone = delta.clone(); + let patch_clone = patch.clone(); store .interact_with_connection(move |conn| { - let old_map_roots = - SqliteStore::get_storage_map_roots_for_delta(conn, account_id, &delta_clone)?; + let old_map_roots = SqliteStore::get_storage_map_roots_for_patch( + conn, + account_id, + patch_clone.storage(), + )?; let tx = conn.transaction().into_store_error()?; let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned"); - SqliteStore::apply_account_delta( + SqliteStore::apply_account_patch( &tx, &mut smt_forest, &prev_header, &final_header, - BTreeMap::default(), &old_map_roots, - &delta, + &patch, )?; tx.commit().into_store_error()?; Ok(()) }) .await?; - // Pre-undo: 1 historical header (old nonce-0 header, replaced_at_nonce=1), 1 latest + // Pre-undo: 1 historical header (old nonce-1 header, replaced_at_nonce=2), 1 latest let m = get_storage_metrics(&store).await; assert_eq!(m.historical_account_headers, 1); assert_eq!(m.latest_account_headers, 1); assert_eq!(m.latest_account_assets, 1); - // Undo the nonce-1 state + // Undo the nonce-2 state let smt_forest = store.smt_forest.clone(); store .interact_with_connection(move |conn| { @@ -1214,27 +1225,27 @@ async fn undo_account_state_restores_previous_latest() -> anyhow::Result<()> { SqliteStore::undo_account_state( &tx, &mut smt_forest, - &[(account_id, post_delta_commitment)], + &[(account_id, post_patch_commitment)], )?; tx.commit().into_store_error()?; Ok(()) }) .await?; - // After undo: historical entries consumed by undo (deleted), latest restored to nonce 0 + // After undo: historical entries consumed by undo (deleted), latest restored to nonce 1 let m = get_storage_metrics(&store).await; assert_eq!(m.historical_account_headers, 0); assert_eq!(m.latest_account_headers, 1); assert_eq!(m.latest_storage_map_entries, 5); assert_eq!(m.historical_storage_map_entries, 0); - assert_eq!(m.latest_account_assets, 0, "Vault should be empty after undo to nonce 0"); + assert_eq!(m.latest_account_assets, 0, "Vault should be empty after undo to nonce 1"); - // Latest header should reflect nonce 0 with the initial commitment + // Latest header should reflect nonce 1 with the initial commitment let (header, _status) = store .interact_with_connection(move |conn| SqliteStore::get_account_header(conn, account_id)) .await? .expect("account should still exist after undo"); - assert_eq!(header.nonce().as_canonical_u64(), 0); + assert_eq!(header.nonce().as_canonical_u64(), 1); assert_eq!(header.to_commitment(), initial_commitment); Ok(()) @@ -1329,52 +1340,54 @@ async fn lock_account_affects_latest_and_historical() -> anyhow::Result<()> { let store = create_test_store().await; let map_slot_name = StorageSlotName::new("test::lock::map").expect("valid slot name"); - // Insert account (nonce 0) + // Insert account (nonce 1) let mut account = setup_account_with_map(&store, 3, &map_slot_name).await?; let account_id = account.id(); - // Apply a delta (nonce 1) with vault change - let mut storage_delta = AccountStorageDelta::new(); - storage_delta.set_map_item( + // Apply a patch (nonce 2) with vault change + let mut storage_patch = AccountStoragePatch::new(); + storage_patch.set_map_item( map_slot_name.clone(), StorageMapKey::new([Felt::from(1u32), ZERO, ZERO, ZERO].into()), [Felt::from(2000u32), ZERO, ZERO, ZERO].into(), )?; - let vault_delta = AccountVaultDelta::from_iters( - vec![ - FungibleAsset::new(AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?, 100)? - .into(), - ], - [], + // The account starts with an empty vault, so the absolute value of the added asset is the + // asset itself. + let mut vault_patch = AccountVaultPatch::default(); + vault_patch.insert_asset( + FungibleAsset::new(AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?, 100)?.into(), ); - let delta = AccountDelta::new(account.id(), storage_delta, vault_delta, ONE)?; + let patch = + AccountPatch::new(account.id(), storage_patch, vault_patch, None, Some(Felt::from(2u32)))?; let prev_header: AccountHeader = (&account).into(); - account.apply_delta(&delta)?; + account.apply_patch(&patch)?; let final_header: AccountHeader = (&account).into(); let smt_forest = store.smt_forest.clone(); - let delta_clone = delta.clone(); + let patch_clone = patch.clone(); store .interact_with_connection(move |conn| { - let old_map_roots = - SqliteStore::get_storage_map_roots_for_delta(conn, account_id, &delta_clone)?; + let old_map_roots = SqliteStore::get_storage_map_roots_for_patch( + conn, + account_id, + patch_clone.storage(), + )?; let tx = conn.transaction().into_store_error()?; let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned"); - SqliteStore::apply_account_delta( + SqliteStore::apply_account_patch( &tx, &mut smt_forest, &prev_header, &final_header, - BTreeMap::default(), &old_map_roots, - &delta, + &patch, )?; tx.commit().into_store_error()?; Ok(()) }) .await?; - // Pre-lock: 1 historical header (old nonce-0 header, replaced_at_nonce=1) + // Pre-lock: 1 historical header (old nonce-1 header, replaced_at_nonce=2) let m = get_storage_metrics(&store).await; assert_eq!(m.historical_account_headers, 1); @@ -1413,21 +1426,21 @@ async fn lock_account_affects_latest_and_historical() -> anyhow::Result<()> { Ok(rows) }) .await?; - assert_eq!(historical_locked.len(), 1, "Should have 1 historical entry (old nonce-0 state)"); - assert!(historical_locked[0], "Historical nonce-0 should be locked"); + assert_eq!(historical_locked.len(), 1, "Should have 1 historical entry (old nonce-1 state)"); + assert!(historical_locked[0], "Historical nonce-1 should be locked"); Ok(()) } -/// Verifies that undoing a delta after `update_account_state` does not resurrect entries that +/// Verifies that undoing a patch after `update_account_state` does not resurrect entries that /// were removed by the update. This exercises the archival logic in `update_account_state`. /// /// Flow: -/// 1. Insert account with map entries {A, B, C} and an asset X at nonce 0 -/// 2. Apply delta at nonce 1: add asset Y (changes vault root) -/// 3. `update_account_state` with in-memory state at nonce 2: {A, B} and {X} (C and Y removed) -/// 4. Apply delta at nonce 3: change entry A, add asset Z -/// 5. Undo nonce 3 +/// 1. Insert account with map entries {A, B, C} and an asset X at nonce 1 +/// 2. Apply patch at nonce 2: add asset Y (changes vault root) +/// 3. `update_account_state` with in-memory state at nonce 3: {A, B} and {X} (C and Y removed) +/// 4. Apply patch at nonce 4: change entry A, add asset Z +/// 5. Undo nonce 4 /// 6. Assert C and Y are not in latest tables #[tokio::test] #[allow(clippy::too_many_lines)] @@ -1458,7 +1471,7 @@ async fn undo_after_update_account_state_does_not_resurrect_removed_entries() -> AccountComponentMetadata::new("miden::testing::dummy_component"), )?; - // Build with build() at nonce 0: no initial assets + // Build an existing account at nonce 1: no initial assets let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) .with_auth_component(AuthSingleSig::new( @@ -1466,41 +1479,48 @@ async fn undo_after_update_account_state_does_not_resurrect_removed_entries() -> AuthSchemeId::Falcon512Poseidon2, )) .with_component(component) - .build_with_schema_commitment()?; + .build_existing()?; let account_id = account.id(); store .insert_account(&account, Address::new(account_id), ClientAccountType::Native) .await?; - // Step 1+2: Apply delta at nonce 1 adding assets X and Y + // Step 1+2: Apply patch at nonce 2 adding assets X and Y let asset_x = FungibleAsset::new(faucet_id, 100)?; let asset_y = NonFungibleAsset::new(&NonFungibleAssetDetails::new( nf_faucet_id, NON_FUNGIBLE_ASSET_DATA.into(), )); - let vault_delta_1 = AccountVaultDelta::from_iters(vec![asset_x.into(), asset_y.into()], []); - let delta_1 = AccountDelta::new(account_id, AccountStorageDelta::new(), vault_delta_1, ONE)?; + // The account starts with an empty vault, so the absolute values of the added assets are the + // assets themselves. + let vault_patch_1 = AccountVaultPatch::with_assets([asset_x.into(), asset_y.into()]); + let patch_1 = AccountPatch::new( + account_id, + AccountStoragePatch::new(), + vault_patch_1, + None, + Some(Felt::from(2u32)), + )?; - let prev_header_0: AccountHeader = (&account).into(); - let mut account_nonce1 = account.clone(); - account_nonce1.apply_delta(&delta_1)?; - let final_header_1: AccountHeader = (&account_nonce1).into(); + let prev_header_1: AccountHeader = (&account).into(); + let mut account_nonce2 = account.clone(); + account_nonce2.apply_patch(&patch_1)?; + let final_header_2: AccountHeader = (&account_nonce2).into(); let smt_forest = store.smt_forest.clone(); store .interact_with_connection(move |conn| { let tx = conn.transaction().into_store_error()?; let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned"); - SqliteStore::apply_account_delta( + SqliteStore::apply_account_patch( &tx, &mut smt_forest, - &prev_header_0, - &final_header_1, - BTreeMap::default(), + &prev_header_1, + &final_header_2, &BTreeMap::new(), - &delta_1, + &patch_1, )?; smt_forest.commit_roots(account_id); tx.commit().into_store_error()?; @@ -1508,20 +1528,27 @@ async fn undo_after_update_account_state_does_not_resurrect_removed_entries() -> }) .await?; - // Now: map entries {A, B, C} and assets {X, Y} at nonce 1 + // Now: map entries {A, B, C} and assets {X, Y} at nonce 2 let m = get_storage_metrics(&store).await; assert_eq!(m.latest_storage_map_entries, 3, "Should have 3 map entries"); assert_eq!(m.latest_account_assets, 2, "Should have 2 assets (X + Y)"); // Step 3: Build in-memory state with only {A, B} and {X} (C and Y removed) - let mut storage_delta_remove = AccountStorageDelta::new(); - storage_delta_remove.set_map_item(map_slot_name.clone(), key_c, EMPTY_WORD)?; - let vault_delta_remove = AccountVaultDelta::from_iters([], vec![asset_y.into()]); - let delta_remove = - AccountDelta::new(account_id, storage_delta_remove, vault_delta_remove, ONE)?; - - let mut account_updated = account_nonce1.clone(); - account_updated.apply_delta(&delta_remove)?; + let mut storage_patch_remove = AccountStoragePatch::new(); + storage_patch_remove.set_map_item(map_slot_name.clone(), key_c, EMPTY_WORD)?; + // Y is removed, so its vault key is marked as removed (absolute final vault is {X}). + let mut vault_patch_remove = AccountVaultPatch::default(); + vault_patch_remove.remove_asset(asset_y.vault_key()); + let patch_remove = AccountPatch::new( + account_id, + storage_patch_remove, + vault_patch_remove, + None, + Some(Felt::from(3u32)), + )?; + + let mut account_updated = account_nonce2.clone(); + account_updated.apply_patch(&patch_remove)?; let updated_nonce = account_updated.nonce().as_canonical_u64(); // Call update_account_state with the updated state @@ -1542,9 +1569,9 @@ async fn undo_after_update_account_state_does_not_resurrect_removed_entries() -> assert_eq!(m.latest_storage_map_entries, 2, "Should have 2 map entries after update"); assert_eq!(m.latest_account_assets, 1, "Should have 1 asset after update"); - // Step 4: Apply a delta that changes entry A and adds asset Z - let mut storage_delta_next = AccountStorageDelta::new(); - storage_delta_next.set_map_item( + // Step 4: Apply a patch that changes entry A and adds asset Z + let mut storage_patch_next = AccountStoragePatch::new(); + storage_patch_next.set_map_item( map_slot_name.clone(), key_a, [Felt::from(999u32), ZERO, ZERO, ZERO].into(), @@ -1552,44 +1579,54 @@ async fn undo_after_update_account_state_does_not_resurrect_removed_entries() -> let asset_z = NonFungibleAsset::new(&NonFungibleAssetDetails::new(nf_faucet_id, vec![5, 6, 7, 8])); - let vault_delta_next = AccountVaultDelta::from_iters(vec![asset_z.into()], []); - - let delta_next = AccountDelta::new(account_id, storage_delta_next, vault_delta_next, ONE)?; + // The vault holds {X} here, so adding Z is the only vault change. + let mut vault_patch_next = AccountVaultPatch::default(); + vault_patch_next.insert_asset(asset_z.into()); + + let patch_next = AccountPatch::new( + account_id, + storage_patch_next, + vault_patch_next, + None, + Some(Felt::from(4u32)), + )?; let prev_header: AccountHeader = (&account_updated).into(); let mut account_next = account_updated.clone(); - account_next.apply_delta(&delta_next)?; + account_next.apply_patch(&patch_next)?; let final_header: AccountHeader = (&account_next).into(); let commitment_next = account_next.to_commitment(); let smt_forest = store.smt_forest.clone(); - let delta_next_clone = delta_next.clone(); + let patch_next_clone = patch_next.clone(); store .interact_with_connection(move |conn| { - let old_map_roots = - SqliteStore::get_storage_map_roots_for_delta(conn, account_id, &delta_next_clone)?; + let old_map_roots = SqliteStore::get_storage_map_roots_for_patch( + conn, + account_id, + patch_next_clone.storage(), + )?; let tx = conn.transaction().into_store_error()?; let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned"); - SqliteStore::apply_account_delta( + SqliteStore::apply_account_patch( &tx, &mut smt_forest, &prev_header, &final_header, - BTreeMap::default(), &old_map_roots, - &delta_next, + &patch_next, )?; tx.commit().into_store_error()?; Ok(()) }) .await?; - // After delta: 2 map entries (A modified, B unchanged), 2 assets (X + Z) + // After patch: 2 map entries (A modified, B unchanged), 2 assets (X + Z) let m = get_storage_metrics(&store).await; - assert_eq!(m.latest_storage_map_entries, 2, "Should have 2 map entries after delta"); - assert_eq!(m.latest_account_assets, 2, "Should have 2 assets after delta (X + Z)"); + assert_eq!(m.latest_storage_map_entries, 2, "Should have 2 map entries after patch"); + assert_eq!(m.latest_account_assets, 2, "Should have 2 assets after patch (X + Z)"); - // Step 5: Undo the last delta + // Step 5: Undo the last patch let smt_forest = store.smt_forest.clone(); store .interact_with_connection(move |conn| { @@ -1632,17 +1669,17 @@ async fn update_account_state_rejects_stale_full_snapshot_without_mutating() -> let store = create_test_store().await; let map_slot_name = StorageSlotName::new("test::stale_update::map").expect("valid slot name"); - // Insert nonce-0 account, then advance the persisted state to nonce 1. + // Insert nonce-1 account, then advance the persisted state to nonce 2. let stale_account = setup_account_with_map(&store, 3, &map_slot_name).await?; let account_id = stale_account.id(); let mut current_account = stale_account.clone(); - apply_single_entry_update(&store, &mut current_account, &map_slot_name, 1).await?; + apply_single_entry_update(&store, &mut current_account, &map_slot_name, 2).await?; assert!(stale_account.nonce().as_canonical_u64() < current_account.nonce().as_canonical_u64()); assert_ne!(stale_account.to_commitment(), current_account.to_commitment()); let metrics_before_stale_update = get_storage_metrics(&store).await; - // Feed the older nonce-0 full snapshot through the same path used for public account sync. + // Feed the older nonce-1 full snapshot through the same path used for public account sync. let smt_forest = store.smt_forest.clone(); let result = store .interact_with_connection(move |conn| { @@ -1654,7 +1691,7 @@ async fn update_account_state_rejects_stale_full_snapshot_without_mutating() -> }) .await; assert!( - matches!(&result, Err(StoreError::DatabaseError(err)) if err.contains("new nonce 0 is less than old nonce 1")), + matches!(&result, Err(StoreError::DatabaseError(err)) if err.contains("new nonce 1 is less than old nonce 2")), "expected stale update to be rejected before mutating state, got {result:?}" ); @@ -1686,16 +1723,16 @@ async fn get_account_header_by_commitment_returns_historical() -> anyhow::Result let store = create_test_store().await; let map_slot_name = StorageSlotName::new("test::commitment::map").expect("valid slot name"); - // Insert account (nonce 0) + // Insert account (nonce 1) let mut account = setup_account_with_map(&store, 3, &map_slot_name).await?; let initial_commitment = account.to_commitment(); - // Apply a delta (nonce 1) - apply_single_entry_update(&store, &mut account, &map_slot_name, 1).await?; - let post_delta_commitment = account.to_commitment(); - assert_ne!(initial_commitment, post_delta_commitment); + // Apply a patch (nonce 2) + apply_single_entry_update(&store, &mut account, &map_slot_name, 2).await?; + let post_patch_commitment = account.to_commitment(); + assert_ne!(initial_commitment, post_patch_commitment); - // Look up the initial commitment: should find the nonce-0 state in historical + // Look up the initial commitment: should find the nonce-1 state in historical let lookup = initial_commitment; let header = store .interact_with_connection(move |conn| { @@ -1703,18 +1740,18 @@ async fn get_account_header_by_commitment_returns_historical() -> anyhow::Result }) .await? .expect("Initial commitment should exist in historical"); - assert_eq!(header.nonce().as_canonical_u64(), 0); + assert_eq!(header.nonce().as_canonical_u64(), 1); assert_eq!(header.to_commitment(), initial_commitment); - // Look up the post-delta commitment: should NOT be in historical (it's the current + // Look up the post-patch commitment: should NOT be in historical (it's the current // latest state, not an old one that was replaced) - let lookup = post_delta_commitment; + let lookup = post_patch_commitment; let result = store .interact_with_connection(move |conn| { SqliteStore::get_account_header_by_commitment(conn, lookup) }) .await?; - assert!(result.is_none(), "Post-delta commitment should not be in historical"); + assert!(result.is_none(), "Post-patch commitment should not be in historical"); Ok(()) } @@ -1722,11 +1759,11 @@ async fn get_account_header_by_commitment_returns_historical() -> anyhow::Result /// Verifies that undoing multiple nonces at once correctly reverts to the original state. /// /// Flow: -/// 1. Insert account with 3 map entries at nonce 0 -/// 2. Apply delta at nonce 1: change map entry + add asset -/// 3. Apply delta at nonce 2: change another map entry + add different asset +/// 1. Insert account with 3 map entries at nonce 1 +/// 2. Apply patch at nonce 2: change map entry + add asset +/// 3. Apply patch at nonce 3: change another map entry + add different asset /// 4. Undo both nonces at once (pass both commitments to `undo_account_state`) -/// 5. Verify latest is restored to nonce 0 state +/// 5. Verify latest is restored to nonce 1 state #[tokio::test] #[allow(clippy::too_many_lines)] async fn undo_multiple_nonces_at_once() -> anyhow::Result<()> { @@ -1736,58 +1773,69 @@ async fn undo_multiple_nonces_at_once() -> anyhow::Result<()> { let faucet_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?; let nf_faucet_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET)?; - // Insert account with 3 map entries at nonce 0 + // Insert account with 3 map entries at nonce 1 let account = setup_account_with_map(&store, 3, &map_slot_name).await?; let account_id = account.id(); let initial_commitment = account.to_commitment(); - // Verify nonce 0 state + // Verify nonce 1 state let m = get_storage_metrics(&store).await; assert_eq!(m.latest_storage_map_entries, 3, "Initial: 3 map entries"); assert_eq!(m.latest_account_assets, 0, "Initial: no assets"); - // Apply delta at nonce 1: change map entry key=1, add fungible asset - let mut storage_delta_1 = AccountStorageDelta::new(); - storage_delta_1.set_map_item( + // Apply patch at nonce 2: change map entry key=1, add fungible asset + let mut storage_patch_1 = AccountStoragePatch::new(); + storage_patch_1.set_map_item( map_slot_name.clone(), StorageMapKey::new([Felt::from(1u32), ZERO, ZERO, ZERO].into()), [Felt::from(1000u32), ZERO, ZERO, ZERO].into(), )?; let asset_1 = FungibleAsset::new(faucet_id, 100)?; - let vault_delta_1 = AccountVaultDelta::from_iters(vec![asset_1.into()], []); - let delta_1 = AccountDelta::new(account_id, storage_delta_1, vault_delta_1, ONE)?; + // The account starts with an empty vault, so the absolute value of the added asset is the + // asset itself. + let mut vault_patch_1 = AccountVaultPatch::default(); + vault_patch_1.insert_asset(asset_1.into()); + let patch_1 = AccountPatch::new( + account_id, + storage_patch_1, + vault_patch_1, + None, + Some(Felt::from(2u32)), + )?; - let prev_header_0: AccountHeader = (&account).into(); - let mut account_nonce1 = account.clone(); - account_nonce1.apply_delta(&delta_1)?; - let final_header_1: AccountHeader = (&account_nonce1).into(); - let commitment_nonce1 = account_nonce1.to_commitment(); + let prev_header_1: AccountHeader = (&account).into(); + let mut account_nonce2 = account.clone(); + account_nonce2.apply_patch(&patch_1)?; + let final_header_2: AccountHeader = (&account_nonce2).into(); + let commitment_nonce2 = account_nonce2.to_commitment(); let smt_forest = store.smt_forest.clone(); - let delta_1_clone = delta_1.clone(); + let patch_1_clone = patch_1.clone(); store .interact_with_connection(move |conn| { - let old_map_roots = - SqliteStore::get_storage_map_roots_for_delta(conn, account_id, &delta_1_clone)?; + let old_map_roots = SqliteStore::get_storage_map_roots_for_patch( + conn, + account_id, + patch_1_clone.storage(), + )?; let tx = conn.transaction().into_store_error()?; let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned"); - SqliteStore::apply_account_delta( + SqliteStore::apply_account_patch( &tx, &mut smt_forest, - &prev_header_0, - &final_header_1, - BTreeMap::default(), + &prev_header_1, + &final_header_2, &old_map_roots, - &delta_1, + &patch_1, )?; tx.commit().into_store_error()?; Ok(()) }) .await?; - // Apply delta at nonce 2: change map entry key=2, add non-fungible asset - let mut storage_delta_2 = AccountStorageDelta::new(); - storage_delta_2.set_map_item( + // Apply patch at nonce 3: change map entry key=2, add non-fungible asset + let mut storage_patch_2 = AccountStoragePatch::new(); + storage_patch_2.set_map_item( map_slot_name.clone(), StorageMapKey::new([Felt::from(2u32), ZERO, ZERO, ZERO].into()), [Felt::from(2000u32), ZERO, ZERO, ZERO].into(), @@ -1796,41 +1844,51 @@ async fn undo_multiple_nonces_at_once() -> anyhow::Result<()> { nf_faucet_id, NON_FUNGIBLE_ASSET_DATA.into(), )); - let vault_delta_2 = AccountVaultDelta::from_iters(vec![asset_2.into()], []); - let delta_2 = AccountDelta::new(account_id, storage_delta_2, vault_delta_2, Felt::from(2u32))?; + // The vault holds {asset_1} here, so adding asset_2 is the only vault change. + let mut vault_patch_2 = AccountVaultPatch::default(); + vault_patch_2.insert_asset(asset_2.into()); + let patch_2 = AccountPatch::new( + account_id, + storage_patch_2, + vault_patch_2, + None, + Some(Felt::from(3u32)), + )?; - let prev_header_1: AccountHeader = (&account_nonce1).into(); - let mut account_nonce2 = account_nonce1.clone(); - account_nonce2.apply_delta(&delta_2)?; - let final_header_2: AccountHeader = (&account_nonce2).into(); - let commitment_nonce2 = account_nonce2.to_commitment(); + let prev_header_2: AccountHeader = (&account_nonce2).into(); + let mut account_nonce3 = account_nonce2.clone(); + account_nonce3.apply_patch(&patch_2)?; + let final_header_3: AccountHeader = (&account_nonce3).into(); + let commitment_nonce3 = account_nonce3.to_commitment(); let smt_forest = store.smt_forest.clone(); - let delta_2_clone = delta_2.clone(); + let patch_2_clone = patch_2.clone(); store .interact_with_connection(move |conn| { - let old_map_roots = - SqliteStore::get_storage_map_roots_for_delta(conn, account_id, &delta_2_clone)?; + let old_map_roots = SqliteStore::get_storage_map_roots_for_patch( + conn, + account_id, + patch_2_clone.storage(), + )?; let tx = conn.transaction().into_store_error()?; let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned"); - SqliteStore::apply_account_delta( + SqliteStore::apply_account_patch( &tx, &mut smt_forest, - &prev_header_1, - &final_header_2, - BTreeMap::default(), + &prev_header_2, + &final_header_3, &old_map_roots, - &delta_2, + &patch_2, )?; tx.commit().into_store_error()?; Ok(()) }) .await?; - // Pre-undo: 2 historical headers (nonce 0 replaced at nonce 1, nonce 1 replaced at nonce 2) + // Pre-undo: 2 historical headers (nonce 1 replaced at nonce 2, nonce 2 replaced at nonce 3) let m = get_storage_metrics(&store).await; assert_eq!(m.historical_account_headers, 2, "Should have 2 historical headers"); - assert_eq!(m.latest_account_assets, 2, "Should have 2 assets at nonce 2"); + assert_eq!(m.latest_account_assets, 2, "Should have 2 assets at nonce 3"); // Undo BOTH nonces at once let smt_forest = store.smt_forest.clone(); @@ -1841,27 +1899,27 @@ async fn undo_multiple_nonces_at_once() -> anyhow::Result<()> { SqliteStore::undo_account_state( &tx, &mut smt_forest, - &[(account_id, commitment_nonce1), (account_id, commitment_nonce2)], + &[(account_id, commitment_nonce2), (account_id, commitment_nonce3)], )?; tx.commit().into_store_error()?; Ok(()) }) .await?; - // After undo: all historical entries consumed, latest restored to nonce 0 + // After undo: all historical entries consumed, latest restored to nonce 1 let m = get_storage_metrics(&store).await; assert_eq!(m.historical_account_headers, 0, "All historical headers consumed by undo"); assert_eq!(m.latest_account_headers, 1, "Latest header should still exist"); assert_eq!(m.latest_storage_map_entries, 3, "All 3 original map entries should be restored"); assert_eq!(m.historical_storage_map_entries, 0, "No historical map entries should remain"); - assert_eq!(m.latest_account_assets, 0, "Vault should be empty after undo to nonce 0"); + assert_eq!(m.latest_account_assets, 0, "Vault should be empty after undo to nonce 1"); - // Verify the header is at nonce 0 with original commitment + // Verify the header is at nonce 1 with original commitment let (header, _) = store .interact_with_connection(move |conn| SqliteStore::get_account_header(conn, account_id)) .await? .expect("account should exist after undo"); - assert_eq!(header.nonce().as_canonical_u64(), 0); + assert_eq!(header.nonce().as_canonical_u64(), 1); assert_eq!(header.to_commitment(), initial_commitment); Ok(()) @@ -1871,9 +1929,9 @@ async fn undo_multiple_nonces_at_once() -> anyhow::Result<()> { /// are correctly removed from latest on undo. These entries get NULL `old_value` in historical. /// /// Flow: -/// 1. Insert account with map entries {A, B} at nonce 0 -/// 2. `update_account_state` at nonce 1 with entries {A, B, C, D} (C and D are new) -/// 3. Undo nonce 1 +/// 1. Insert account with map entries {A, B} at nonce 1 +/// 2. `update_account_state` at nonce 2 with entries {A, B, C, D} (C and D are new) +/// 3. Undo nonce 2 /// 4. Verify C and D are gone from latest, only A and B remain #[tokio::test] #[allow(clippy::too_many_lines)] @@ -1904,40 +1962,49 @@ async fn undo_after_update_removes_genuinely_new_entries() -> anyhow::Result<()> AuthSchemeId::Falcon512Poseidon2, )) .with_component(component) - .build_with_schema_commitment()?; + .build_existing()?; let account_id = account.id(); store .insert_account(&account, Address::new(account_id), ClientAccountType::Native) .await?; - // Verify nonce 0 state + // Verify nonce 1 state let m = get_storage_metrics(&store).await; assert_eq!(m.latest_storage_map_entries, 2, "Initial: 2 map entries"); - // Build in-memory state at nonce 1 with {A, B, C, D}: C and D are genuinely new - let mut storage_delta_add = AccountStorageDelta::new(); - storage_delta_add.set_map_item( + // Build in-memory state at nonce 2 with {A, B, C, D}: C and D are genuinely new + let mut storage_patch_add = AccountStoragePatch::new(); + storage_patch_add.set_map_item( map_slot_name.clone(), key_c, [Felt::from(300u32), ZERO, ZERO, ZERO].into(), )?; - storage_delta_add.set_map_item( + storage_patch_add.set_map_item( map_slot_name.clone(), key_d, [Felt::from(400u32), ZERO, ZERO, ZERO].into(), )?; - // Also add an asset so the vault root changes (avoids SMT root collision on undo) + // Also add an asset so the vault root changes (avoids SMT root collision on undo). + // The account starts with an empty vault, so the absolute value of the added asset is the + // asset itself. let faucet_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?; let asset = FungibleAsset::new(faucet_id, 100)?; - let vault_delta_add = AccountVaultDelta::from_iters(vec![asset.into()], []); - let delta_add = AccountDelta::new(account_id, storage_delta_add, vault_delta_add, ONE)?; + let mut vault_patch_add = AccountVaultPatch::default(); + vault_patch_add.insert_asset(asset.into()); + let patch_add = AccountPatch::new( + account_id, + storage_patch_add, + vault_patch_add, + None, + Some(Felt::from(2u32)), + )?; let mut account_updated = account.clone(); - account_updated.apply_delta(&delta_add)?; + account_updated.apply_patch(&patch_add)?; - // Call update_account_state with the updated state at nonce 1 + // Call update_account_state with the updated state at nonce 2 let smt_forest = store.smt_forest.clone(); let account_updated_clone = account_updated.clone(); store @@ -1969,7 +2036,7 @@ async fn undo_after_update_removes_genuinely_new_entries() -> anyhow::Result<()> .await?; assert_eq!(null_count, 2, "Should have 2 NULL old_value entries (C and D) in historical"); - // Undo nonce 1 + // Undo nonce 2 let commitment = account_updated.to_commitment(); let smt_forest = store.smt_forest.clone(); store @@ -1990,19 +2057,19 @@ async fn undo_after_update_removes_genuinely_new_entries() -> anyhow::Result<()> ); assert_eq!( m.latest_account_assets, 0, - "Asset should be gone after undo (didn't exist at nonce 0)" + "Asset should be gone after undo (didn't exist at nonce 1)" ); assert_eq!( m.historical_storage_map_entries, 0, "Historical map entries should be cleaned up after undo" ); - // Verify the header is at nonce 0 + // Verify the header is at nonce 1 let (header, _) = store .interact_with_connection(move |conn| SqliteStore::get_account_header(conn, account_id)) .await? .expect("account should exist after undo"); - assert_eq!(header.nonce().as_canonical_u64(), 0); + assert_eq!(header.nonce().as_canonical_u64(), 1); Ok(()) } @@ -2010,35 +2077,35 @@ async fn undo_after_update_removes_genuinely_new_entries() -> anyhow::Result<()> // SMT FOREST SNAPSHOT ROLLBACK // ================================================================================================ -/// Builds a non-trivial delta over a freshly inserted account: a value-slot write, a map-slot +/// Builds a non-trivial patch over a freshly inserted account: a value-slot write, a map-slot /// write, and a vault addition. Sufficient to drive `stage_roots` + multiple SMT mutations in -/// `apply_account_delta`. -fn build_delta_for_snapshot_test( +/// `apply_account_patch`. +fn build_patch_for_snapshot_test( account: &Account, value_slot_name: StorageSlotName, map_slot_name: StorageSlotName, -) -> anyhow::Result<(AccountDelta, Account)> { - let mut storage_delta = AccountStorageDelta::new(); - storage_delta.set_item(value_slot_name, [ZERO, ZERO, ZERO, ONE].into())?; - storage_delta.set_map_item( +) -> anyhow::Result<(AccountPatch, Account)> { + let mut storage_patch = AccountStoragePatch::new(); + storage_patch.set_item(value_slot_name, [ZERO, ZERO, ZERO, ONE].into())?; + storage_patch.set_map_item( map_slot_name, StorageMapKey::new([ONE, ZERO, ZERO, ZERO].into()), [ONE, ONE, ONE, ONE].into(), )?; - let vault_delta = AccountVaultDelta::from_iters( - vec![ - FungibleAsset::new(AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?, 100)? - .into(), - ], - [], + // The account starts with an empty vault, so the absolute value of the added asset is the + // asset itself. + let mut vault_patch = AccountVaultPatch::default(); + vault_patch.insert_asset( + FungibleAsset::new(AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?, 100)?.into(), ); - let delta = AccountDelta::new(account.id(), storage_delta, vault_delta, ONE)?; + let patch = + AccountPatch::new(account.id(), storage_patch, vault_patch, None, Some(Felt::from(2u32)))?; - let mut account_after_delta = account.clone(); - account_after_delta.apply_delta(&delta)?; - Ok((delta, account_after_delta)) + let mut account_after_patch = account.clone(); + account_after_patch.apply_patch(&patch)?; + Ok((patch, account_after_patch)) } async fn insert_account_with_storage_for_snapshot_test() @@ -2066,7 +2133,7 @@ async fn insert_account_with_storage_for_snapshot_test() AuthSchemeId::Falcon512Poseidon2, )) .with_component(dummy_component) - .build_with_schema_commitment()?; + .build_existing()?; let default_address = Address::new(account.id()); store @@ -2077,16 +2144,16 @@ async fn insert_account_with_storage_for_snapshot_test() } /// `with_forest_snapshot` must leave the in-memory `AccountSmtForest` unchanged when the -/// closure returns an error, even after `apply_account_delta` has already mutated the +/// closure returns an error, even after `apply_account_patch` has already mutated the /// working clone (vault tree, storage map tree, and staged roots). #[tokio::test] async fn with_forest_snapshot_leaves_forest_unchanged_on_error() -> anyhow::Result<()> { let (store, account, value_slot_name, map_slot_name) = insert_account_with_storage_for_snapshot_test().await?; - let (delta, account_after_delta) = - build_delta_for_snapshot_test(&account, value_slot_name, map_slot_name)?; - let final_state: AccountHeader = (&account_after_delta).into(); + let (patch, account_after_patch) = + build_patch_for_snapshot_test(&account, value_slot_name, map_slot_name)?; + let final_state: AccountHeader = (&account_after_patch).into(); let forest_arc = store.smt_forest.clone(); let forest_before = forest_arc.read().expect("read lock").clone(); @@ -2096,14 +2163,13 @@ async fn with_forest_snapshot_leaves_forest_unchanged_on_error() -> anyhow::Resu let outcome = store .interact_with_connection(move |conn| { with_forest_snapshot(conn, &smt_forest, |tx, forest| { - SqliteStore::apply_account_delta( + SqliteStore::apply_account_patch( tx, forest, &init_header, &final_state, - BTreeMap::default(), &BTreeMap::new(), - &delta, + &patch, )?; Err::<(), _>(StoreError::DatabaseError("forced rollback".to_string())) }) @@ -2115,12 +2181,12 @@ async fn with_forest_snapshot_leaves_forest_unchanged_on_error() -> anyhow::Resu let forest_after = forest_arc.read().expect("read lock").clone(); assert_eq!(forest_after, forest_before, "forest must be unchanged after a failed closure"); - // The DB transaction was rolled back too; account state is still at nonce 0. + // The DB transaction was rolled back too; account state is still at nonce 1. let (header, _) = store .interact_with_connection(move |conn| SqliteStore::get_account_header(conn, account.id())) .await? .expect("account header present"); - assert_eq!(header.nonce().as_canonical_u64(), 0); + assert_eq!(header.nonce().as_canonical_u64(), 1); Ok(()) } @@ -2132,9 +2198,9 @@ async fn with_forest_snapshot_persists_forest_on_success() -> anyhow::Result<()> let (store, account, value_slot_name, map_slot_name) = insert_account_with_storage_for_snapshot_test().await?; - let (delta, account_after_delta) = - build_delta_for_snapshot_test(&account, value_slot_name, map_slot_name)?; - let final_state: AccountHeader = (&account_after_delta).into(); + let (patch, account_after_patch) = + build_patch_for_snapshot_test(&account, value_slot_name, map_slot_name)?; + let final_state: AccountHeader = (&account_after_patch).into(); let forest_arc = store.smt_forest.clone(); let forest_before = forest_arc.read().expect("read lock").clone(); @@ -2145,14 +2211,13 @@ async fn with_forest_snapshot_persists_forest_on_success() -> anyhow::Result<()> store .interact_with_connection(move |conn| { with_forest_snapshot(conn, &smt_forest, |tx, forest| { - SqliteStore::apply_account_delta( + SqliteStore::apply_account_patch( tx, forest, &init_header, &final_state, - BTreeMap::default(), &BTreeMap::new(), - &delta, + &patch, ) }) }) @@ -2161,14 +2226,14 @@ async fn with_forest_snapshot_persists_forest_on_success() -> anyhow::Result<()> let forest_after = forest_arc.read().expect("read lock").clone(); assert_ne!( forest_after, forest_before, - "forest must reflect the staged delta after a successful closure" + "forest must reflect the staged patch after a successful closure" ); let (header, _) = store .interact_with_connection(move |conn| SqliteStore::get_account_header(conn, account_id)) .await? .expect("account header present"); - assert_eq!(header.nonce().as_canonical_u64(), 1); + assert_eq!(header.nonce().as_canonical_u64(), 2); Ok(()) } @@ -2196,10 +2261,14 @@ async fn watched_status_survives_state_replacement() -> anyhow::Result<()> { // Bump the account's nonce and run it through update_account. let mut updated = account.clone(); - let storage_delta = AccountStorageDelta::new(); - let vault_delta = AccountVaultDelta::from_iters([], []); - let delta = AccountDelta::new(account_id, storage_delta, vault_delta, ONE)?; - updated.apply_delta(&delta)?; + let patch = AccountPatch::new( + account_id, + AccountStoragePatch::new(), + AccountVaultPatch::default(), + None, + Some(Felt::from(2u32)), + )?; + updated.apply_patch(&patch)?; store.update_account(&updated).await?; diff --git a/crates/sqlite-store/src/account/vault.rs b/crates/sqlite-store/src/account/vault.rs index 9167528d01..818fbf517c 100644 --- a/crates/sqlite-store/src/account/vault.rs +++ b/crates/sqlite-store/src/account/vault.rs @@ -1,17 +1,15 @@ //! Vault/asset-related database operations for accounts. -use std::collections::BTreeMap; use std::rc::Rc; use std::vec::Vec; -use miden_client::Word; -use miden_client::account::{AccountDelta, AccountHeader, AccountId}; -use miden_client::asset::{Asset, FungibleAsset, NonFungibleDeltaAction}; +use miden_client::account::{AccountHeader, AccountId, AccountVaultPatch}; +use miden_client::asset::Asset; use miden_client::store::{AccountSmtForest, StoreError}; use miden_protocol::asset::AssetVaultKey; use miden_protocol::crypto::merkle::MerkleError; use rusqlite::types::Value; -use rusqlite::{Connection, OptionalExtension, Transaction, params}; +use rusqlite::{OptionalExtension, Transaction, params}; use crate::sql_error::SqlResultExt; use crate::{SqliteStore, insert_sql, subst, u64_to_value}; @@ -20,44 +18,6 @@ impl SqliteStore { // READER METHODS // -------------------------------------------------------------------------------------------- - /// Fetches the relevant fungible assets of an account that will be updated by the account - /// delta. - pub(crate) fn get_account_fungible_assets_for_delta( - conn: &Connection, - account_id: AccountId, - delta: &AccountDelta, - ) -> Result, StoreError> { - let vault_keys = delta - .vault() - .fungible() - .iter() - .map(|(vault_key, _)| Value::Text(vault_key.to_string())) - .collect::>(); - - const QUERY: &str = "SELECT vault_key, asset FROM latest_account_assets WHERE account_id = ? AND vault_key IN rarray(?)"; - - Ok(conn - .prepare(QUERY) - .into_store_error()? - .query_map(params![account_id.to_hex(), Rc::new(vault_keys)], |row| { - let vault_key: String = row.get(0)?; - let asset: String = row.get(1)?; - Ok((vault_key, asset)) - }) - .into_store_error()? - .map(|result| { - let (vault_key_str, asset_str): (String, String) = result.into_store_error()?; - let key_word = Word::try_from(vault_key_str)?; - let value_word = Word::try_from(asset_str)?; - Ok(Asset::from_key_value_words(key_word, value_word)?) - }) - .collect::, StoreError>>()? - .into_iter() - // SAFETY: all retrieved assets should be fungible - .map(|asset| (asset.vault_key(), asset.unwrap_fungible())) - .collect()) - } - // MUTATOR/WRITER METHODS // -------------------------------------------------------------------------------------------- @@ -92,69 +52,26 @@ impl SqliteStore { /// The function updates the SMT forest with all asset changes and verifies that the resulting /// vault root matches the expected final state. It archives old values from latest to /// historical, deletes removed assets from latest, then inserts updated assets. - pub(crate) fn apply_account_vault_delta( + pub(crate) fn apply_account_vault_patch( tx: &Transaction<'_>, smt_forest: &mut AccountSmtForest, account_id: AccountId, init_account_state: &AccountHeader, final_account_state: &AccountHeader, - mut updated_fungible_assets: BTreeMap, - delta: &AccountDelta, + vault_patch: &AccountVaultPatch, ) -> Result<(), StoreError> { let nonce = final_account_state.nonce().as_canonical_u64(); let account_id_hex = account_id.to_hex(); let nonce_val = u64_to_value(nonce); - // Apply vault delta. This map will contain all updated assets (indexed by vault key), both - // fungible and non-fungible. - let mut updated_assets: BTreeMap = BTreeMap::new(); - let mut removed_vault_keys: Vec = Vec::new(); - - // We first process the fungible assets. Adding or subtracting them from the vault as - // requested. - for (vault_key, delta) in delta.vault().fungible().iter() { - let delta_asset = FungibleAsset::new(vault_key.faucet_id(), delta.unsigned_abs())? - .with_callbacks(vault_key.callback_flag()); - - let asset = match updated_fungible_assets.remove(vault_key) { - Some(asset) => { - // If the asset exists, update it accordingly. - if *delta >= 0 { - asset.add(delta_asset)? - } else { - asset.sub(delta_asset)? - } - }, - None => { - // If the asset doesn't exist, we add it to the map to be inserted. - delta_asset - }, - }; - - if asset.amount().as_u64() > 0 { - updated_assets.insert(asset.vault_key(), Asset::Fungible(asset)); - } else { - removed_vault_keys.push(asset.vault_key()); - } - } - - // Process non-fungible assets. Here additions or removals don't depend on previous state as - // each asset is unique. - let (added_nonfungible_assets, removed_nonfungible_assets) = - delta.vault().non_fungible().iter().partition::, _>(|(_, action)| { - matches!(action, NonFungibleDeltaAction::Add) - }); - - updated_assets.extend( - added_nonfungible_assets - .into_iter() - .map(|(asset, _)| (asset.vault_key(), Asset::NonFungible(*asset))), - ); - - removed_vault_keys - .extend(removed_nonfungible_assets.iter().map(|(asset, _)| asset.vault_key())); + // The patch carries the absolute final value of every changed entry, so updated assets are + // inserted verbatim and removed entries (empty value) are deleted. No prior balance lookup + // or signed-amount arithmetic is needed, and the asset value word already encodes the + // callback flag for both fungible and non-fungible assets. + let updated_assets_values: Vec = vault_patch.updated_assets().collect(); + let removed_vault_keys: Vec = + vault_patch.removed_asset_keys().copied().collect(); - let updated_assets_values: Vec = updated_assets.values().copied().collect(); Self::persist_vault_delta( tx, &account_id_hex, diff --git a/crates/sqlite-store/src/sync.rs b/crates/sqlite-store/src/sync.rs index 6a9d946acb..9b7ea9b08e 100644 --- a/crates/sqlite-store/src/sync.rs +++ b/crates/sqlite-store/src/sync.rs @@ -7,11 +7,11 @@ use std::vec::Vec; use miden_client::Word; use miden_client::account::AccountId; use miden_client::note::{BlockNumber, NoteTag}; -use miden_client::store::{AccountSmtForest, AccountStorageFilter, StoreError}; +use miden_client::store::{AccountSmtForest, StoreError}; use miden_client::sync::{ NoteTagRecord, NoteTagSource, - PublicAccountDelta, + PublicAccountPatch, PublicAccountUpdate, StateSyncUpdate, }; @@ -19,6 +19,7 @@ use miden_client::utils::{Deserializable, Serializable}; use rusqlite::{Connection, Transaction, params}; use super::SqliteStore; +use crate::account::helpers::query_account_code; use crate::note::apply_note_updates_tx; use crate::sql_error::SqlResultExt; use crate::transaction::{upsert_transaction_record, with_forest_snapshot}; @@ -194,8 +195,8 @@ impl SqliteStore { PublicAccountUpdate::Full(account) => { Self::update_account_state(tx, smt_forest, account)?; }, - PublicAccountUpdate::Delta(delta) => { - Self::apply_public_account_delta(tx, smt_forest, delta)?; + PublicAccountUpdate::Patch(delta) => { + Self::apply_public_account_patch(tx, smt_forest, delta)?; }, } } @@ -208,27 +209,29 @@ impl SqliteStore { }) } - /// Reads the local account state, derives the [`AccountDelta`] from `delta`'s incremental - /// payload, and applies it. - fn apply_public_account_delta( + /// Builds the absolute [`AccountPatch`] from `patch`'s incremental payload and applies it. + /// + /// The patch carries the new absolute value of every changed storage slot, map entry, and + /// vault asset, so no prior account storage or vault state needs to be loaded here. + fn apply_public_account_patch( tx: &Transaction<'_>, smt_forest: &mut AccountSmtForest, - delta: &PublicAccountDelta, + patch: &PublicAccountPatch, ) -> Result<(), StoreError> { - let account_id = delta.id(); - let local_header = Self::get_account_header(tx, account_id)? - .map(|(header, _)| header) - .ok_or(StoreError::AccountDataNotFound(account_id))?; - let local_storage = Self::get_account_storage( - tx, - account_id, - &AccountStorageFilter::SlotNames(delta.value_slot_names()), - )?; - let local_vault = Self::get_account_vault(tx, account_id)?; - - let account_delta = - delta.compute_account_delta(&local_header, &local_storage, &local_vault)?; - Self::apply_sync_account_delta(tx, smt_forest, delta.new_header(), &account_delta) + // A newly created account (final nonce 1) must be applied as a full-state patch carrying + // its code. The code is fixed at creation, so the locally-tracked code (persisted + // when the account was added) matches the new header's code commitment. + let account_id = patch.id(); + let code = if patch.new_header().nonce().as_canonical_u64() == 1 { + let code = query_account_code(tx, patch.new_header().code_commitment())? + .ok_or(StoreError::AccountDataNotFound(account_id))?; + Some(code) + } else { + None + }; + + let account_patch = patch.compute_account_patch(code)?; + Self::apply_sync_account_patch(tx, smt_forest, patch.new_header(), &account_patch) } } diff --git a/crates/sqlite-store/src/transaction.rs b/crates/sqlite-store/src/transaction.rs index 7a1dddc51b..ce28bebd15 100644 --- a/crates/sqlite-store/src/transaction.rs +++ b/crates/sqlite-store/src/transaction.rs @@ -143,25 +143,20 @@ impl SqliteStore { /// and provides no rollback of its own, relying on the caller to discard the snapshot on /// error. /// - /// Pre-reads (fungible assets and storage map roots) are performed via the transaction so - /// that each call sees writes made by prior calls within the same outer transaction. + /// The storage-map-root pre-read is performed via the transaction so that each call sees + /// writes made by prior calls within the same outer transaction. pub(crate) fn apply_transaction_in_txn( db_tx: &mut Transaction<'_>, smt_forest: &mut AccountSmtForest, tx_update: &TransactionStoreUpdate, ) -> Result<(), StoreError> { let executed_transaction = tx_update.executed_transaction(); + let account_patch = executed_transaction.account_patch(); - let updated_fungible_assets = Self::get_account_fungible_assets_for_delta( + let old_map_roots = Self::get_storage_map_roots_for_patch( db_tx, executed_transaction.account_id(), - executed_transaction.account_delta(), - )?; - - let old_map_roots = Self::get_storage_map_roots_for_delta( - db_tx, - executed_transaction.account_id(), - executed_transaction.account_delta(), + account_patch.storage(), )?; // Build transaction record @@ -196,14 +191,13 @@ impl SqliteStore { upsert_transaction_record(db_tx, &transaction_record)?; // Account Data - Self::apply_account_delta( + Self::apply_account_patch( db_tx, smt_forest, &executed_transaction.initial_account().into(), executed_transaction.final_account(), - updated_fungible_assets, &old_map_roots, - executed_transaction.account_delta(), + account_patch, )?; // Note Updates diff --git a/crates/testing/miden-client-tests/src/tests.rs b/crates/testing/miden-client-tests/src/tests.rs index 0297451331..a116a135cd 100644 --- a/crates/testing/miden-client-tests/src/tests.rs +++ b/crates/testing/miden-client-tests/src/tests.rs @@ -113,13 +113,7 @@ use miden_protocol::vm::AdviceInputs; use miden_protocol::{EMPTY_WORD, Felt, ONE, Word}; use miden_standards::account::AccountBuilderSchemaCommitmentExt; use miden_standards::account::faucets::{FungibleFaucet, TokenName}; -use miden_standards::account::interface::AccountInterfaceError; -use miden_standards::account::policies::{ - BurnPolicyConfig, - MintPolicyConfig, - PolicyRegistration, - TokenPolicyManager, -}; +use miden_standards::account::policies::{BurnPolicy, MintPolicy, TokenPolicyManager}; use miden_standards::account::wallets::BasicWallet; use miden_standards::note::{ NoteConsumptionStatus, @@ -131,6 +125,7 @@ use miden_standards::note::{ }; use miden_standards::testing::mock_account::MockAccountExt; use miden_standards::testing::note::NoteBuilder; +use miden_standards::tx_script::SendNotesTransactionScriptError; use miden_testing::{MockChain, MockChainBuilder, TxContextInput}; use rand::rngs::StdRng; use rand::{Rng, RngCore, SeedableRng}; @@ -847,7 +842,7 @@ async fn mint_transaction() { .unwrap(); let executed_tx = transaction_result.executed_transaction().clone(); - assert_eq!(executed_tx.account_delta().nonce_delta(), ONE); + assert_eq!(executed_tx.account_patch().final_nonce(), Some(ONE)); } #[tokio::test] @@ -1048,9 +1043,11 @@ async fn note_without_asset() { assert!(matches!( error, - ClientError::TransactionRequestError(TransactionRequestError::AccountInterfaceError( - AccountInterfaceError::FaucetNoteWithoutAsset - )) + ClientError::TransactionRequestError( + TransactionRequestError::SendNotesTransactionScriptError( + SendNotesTransactionScriptError::FaucetNoteWithoutAsset + ) + ) )); let error = TransactionRequestBuilder::new() @@ -3781,7 +3778,8 @@ async fn storage_and_vault_proofs() { .unwrap() .unwrap(); - let expected_witness = AssetWitness::new(vault.open(asset.vault_key()).into()).unwrap(); + let expected_witness = + AssetWitness::new(vault.open(asset.vault_key()).into(), [asset.vault_key()]).unwrap(); assert_eq!(witness, expected_witness); // Check that specific map item proof matches the one in the storage @@ -4159,15 +4157,15 @@ async fn sync_stores_private_note_attachments() { let ntx_target = NetworkAccountTarget::new(target.id(), NoteExecutionHint::Always).unwrap(); let attachments = NoteAttachments::new(vec![ntx_target.into()]).unwrap(); let mut note_rng = RandomCoin::new([1, 2, 3, 4].map(Felt::new_unchecked).into()); - let private_note = P2idNote::create( - sender.id(), - target.id(), - vec![], - NoteType::Private, - attachments.clone(), - &mut note_rng, - ) - .unwrap(); + let private_note = P2idNote::builder() + .sender(sender.id()) + .target(target.id()) + .note_type(NoteType::Private) + .attachments(attachments.clone().into_vec()) + .generate_serial_number(&mut note_rng) + .build() + .unwrap() + .into(); // Declare the note as a spawn note (not yet committed) and build the chain at genesis. let spawn_note = @@ -4646,11 +4644,10 @@ async fn insert_new_fungible_faucet( .unwrap(); // Only mint/burn policies — see test_utils/common.rs::insert_new_fungible_faucet for the // reason transfer policies are intentionally omitted. - let policy_manager = TokenPolicyManager::new() - .with_mint_policy(MintPolicyConfig::AllowAll, PolicyRegistration::Active) - .unwrap() - .with_burn_policy(BurnPolicyConfig::AllowAll, PolicyRegistration::Active) - .unwrap(); + let policy_manager = TokenPolicyManager::builder() + .active_mint_policy(MintPolicy::allow_all()) + .active_burn_policy(BurnPolicy::allow_all()) + .build(); let account = AccountBuilder::new(init_seed) .account_type(visibility) @@ -4696,11 +4693,10 @@ async fn insert_new_ecdsa_fungible_faucet( .unwrap(); // Only mint/burn policies — see test_utils/common.rs::insert_new_fungible_faucet for the // reason transfer policies are intentionally omitted. - let policy_manager = TokenPolicyManager::new() - .with_mint_policy(MintPolicyConfig::AllowAll, PolicyRegistration::Active) - .unwrap() - .with_burn_policy(BurnPolicyConfig::AllowAll, PolicyRegistration::Active) - .unwrap(); + let policy_manager = TokenPolicyManager::builder() + .active_mint_policy(MintPolicy::allow_all()) + .active_burn_policy(BurnPolicy::allow_all()) + .build(); let account = AccountBuilder::new(init_seed) .account_type(visibility) @@ -4835,7 +4831,8 @@ async fn storage_and_vault_proofs_ecdsa() { .unwrap() .unwrap(); - let expected_witness = AssetWitness::new(vault.open(asset.vault_key()).into()).unwrap(); + let expected_witness = + AssetWitness::new(vault.open(asset.vault_key()).into(), [asset.vault_key()]).unwrap(); assert_eq!(witness, expected_witness); // Check that specific map item proof matches the one in the storage @@ -4887,11 +4884,10 @@ async fn execute_transaction_fails_for_watched_account() { .max_supply(AssetAmount::new(max_supply).unwrap()) .build() .unwrap(); - let policy_manager = TokenPolicyManager::new() - .with_mint_policy(MintPolicyConfig::AllowAll, PolicyRegistration::Active) - .unwrap() - .with_burn_policy(BurnPolicyConfig::AllowAll, PolicyRegistration::Active) - .unwrap(); + let policy_manager = TokenPolicyManager::builder() + .active_mint_policy(MintPolicy::allow_all()) + .active_burn_policy(BurnPolicy::allow_all()) + .build(); let faucet = AccountBuilder::new(init_seed) .account_type(AccountType::Public) .with_auth_component(auth_component) diff --git a/crates/testing/miden-client-tests/src/tests/transaction.rs b/crates/testing/miden-client-tests/src/tests/transaction.rs index bacd18f696..da5b1758b7 100644 --- a/crates/testing/miden-client-tests/src/tests/transaction.rs +++ b/crates/testing/miden-client-tests/src/tests/transaction.rs @@ -4,7 +4,7 @@ use alloc::sync::Arc; use miden_client::assembly::CodeBuilder; use miden_client::auth::{AuthSchemeId, AuthSecretKey, AuthSingleSig, RPO_FALCON_SCHEME_ID}; use miden_client::keystore::Keystore; -use miden_client::note::{NoteAttachments, P2idNote}; +use miden_client::note::{Note, P2idNote}; use miden_client::store::NoteFilter; use miden_client::transaction::{ ProvenTransaction, @@ -148,15 +148,15 @@ async fn execute_transaction_failure_leaves_store_unchanged() { // A note targeting the wallet that is not tracked by the store. Passing it as a request // input note is what would trigger an input-note write during preparation. let asset = FungibleAsset::new(faucet.id(), 100).unwrap(); - let unauthenticated_note = P2idNote::create( - faucet.id(), - wallet.id(), - vec![asset.into()], - NoteType::Private, - NoteAttachments::empty(), - client.rng(), - ) - .unwrap(); + let unauthenticated_note: Note = P2idNote::builder() + .sender(faucet.id()) + .target(wallet.id()) + .asset(asset) + .note_type(NoteType::Private) + .generate_serial_number(client.rng()) + .build() + .unwrap() + .into(); let note_id = unauthenticated_note.id(); // An expected output recipient with a non-standard script. Declaring it in the request is diff --git a/crates/testing/miden-client-tests/src/tests/transport.rs b/crates/testing/miden-client-tests/src/tests/transport.rs index 7daef7552d..1d7441742a 100644 --- a/crates/testing/miden-client-tests/src/tests/transport.rs +++ b/crates/testing/miden-client-tests/src/tests/transport.rs @@ -6,7 +6,7 @@ use miden_client::account::{Account, AccountType}; use miden_client::address::{Address, AddressInterface, RoutingParameters}; use miden_client::builder::ClientBuilder; use miden_client::keystore::FilesystemKeyStore; -use miden_client::note::{Note, NoteAttachments, NoteDetails, NoteTag, NoteType}; +use miden_client::note::{Note, NoteDetails, NoteTag, NoteType}; use miden_client::note_transport::NoteTransportClient; use miden_client::store::NoteFilter; use miden_client::testing::common::create_test_store_path; @@ -42,15 +42,14 @@ async fn transport_basic() { let (mut observer, _observer_account) = create_test_user_transport(mock_node.clone()).await; // Create note - let note = P2idNote::create( - sender_account.id(), - recipient_account.id(), - vec![], - NoteType::Private, - NoteAttachments::empty(), - sender.rng(), - ) - .unwrap(); + let note: Note = P2idNote::builder() + .sender(sender_account.id()) + .target(recipient_account.id()) + .note_type(NoteType::Private) + .generate_serial_number(sender.rng()) + .build() + .unwrap() + .into(); // Sync-state / fetch notes // No notes before sending @@ -90,25 +89,23 @@ async fn transport_cursor_pagination() { let recipient_address = Address::new(recipient_account.id()) .with_routing_parameters(RoutingParameters::new(AddressInterface::BasicWallet)); - let note_a = P2idNote::create( - sender_account.id(), - recipient_account.id(), - vec![], - NoteType::Private, - NoteAttachments::empty(), - sender.rng(), - ) - .unwrap(); - - let note_b = P2idNote::create( - sender_account.id(), - recipient_account.id(), - vec![], - NoteType::Private, - NoteAttachments::empty(), - sender.rng(), - ) - .unwrap(); + let note_a: Note = P2idNote::builder() + .sender(sender_account.id()) + .target(recipient_account.id()) + .note_type(NoteType::Private) + .generate_serial_number(sender.rng()) + .build() + .unwrap() + .into(); + + let note_b: Note = P2idNote::builder() + .sender(sender_account.id()) + .target(recipient_account.id()) + .note_type(NoteType::Private) + .generate_serial_number(sender.rng()) + .build() + .unwrap() + .into(); // Send note A, sync → recipient receives 1 note sender @@ -141,15 +138,14 @@ async fn transport_duplicate_note_handling() { let recipient_address = Address::new(recipient_account.id()) .with_routing_parameters(RoutingParameters::new(AddressInterface::BasicWallet)); - let note = P2idNote::create( - sender_account.id(), - recipient_account.id(), - vec![], - NoteType::Private, - NoteAttachments::empty(), - sender.rng(), - ) - .unwrap(); + let note: Note = P2idNote::builder() + .sender(sender_account.id()) + .target(recipient_account.id()) + .note_type(NoteType::Private) + .generate_serial_number(sender.rng()) + .build() + .unwrap() + .into(); sender .send_private_note_with_block_hint(note, &recipient_address, BlockNumber::from(0)) @@ -189,15 +185,14 @@ async fn fetch_all_private_notes_drains_across_batches() { // Send TOTAL_NOTES > BATCH_CAP private notes so a single-batch fetch // cannot drain the backlog. for _ in 0..TOTAL_NOTES { - let note = P2idNote::create( - sender_account.id(), - recipient_account.id(), - vec![], - NoteType::Private, - NoteAttachments::empty(), - sender.rng(), - ) - .unwrap(); + let note: Note = P2idNote::builder() + .sender(sender_account.id()) + .target(recipient_account.id()) + .note_type(NoteType::Private) + .generate_serial_number(sender.rng()) + .build() + .unwrap() + .into(); sender .send_private_note_with_block_hint(note, &recipient_address, BlockNumber::from(0)) .await @@ -229,15 +224,14 @@ async fn transport_fetch_no_matching_tags() { .with_routing_parameters(RoutingParameters::new(AddressInterface::BasicWallet)); let (mut observer, _observer_account) = create_test_user_transport(mock_node.clone()).await; - let note = P2idNote::create( - sender_account.id(), - recipient_account.id(), - vec![], - NoteType::Private, - NoteAttachments::empty(), - sender.rng(), - ) - .unwrap(); + let note: Note = P2idNote::builder() + .sender(sender_account.id()) + .target(recipient_account.id()) + .note_type(NoteType::Private) + .generate_serial_number(sender.rng()) + .build() + .unwrap() + .into(); sender .send_private_note_with_block_hint(note, &recipient_address, BlockNumber::from(0)) @@ -381,15 +375,14 @@ async fn private_note_relay_recovers_after_transient_ntl_failure() { let recipient_address = Address::new(recipient_account.id()) .with_routing_parameters(RoutingParameters::new(AddressInterface::BasicWallet)); - let note = P2idNote::create( - sender_account.id(), - recipient_account.id(), - vec![], - NoteType::Private, - NoteAttachments::empty(), - sender.rng(), - ) - .unwrap(); + let note: Note = P2idNote::builder() + .sender(sender_account.id()) + .target(recipient_account.id()) + .note_type(NoteType::Private) + .generate_serial_number(sender.rng()) + .build() + .unwrap() + .into(); // Transport-delivered notes carry no metadata (hence no `NoteId`); match by // details commitment. let note_commitment = note.details_commitment(); @@ -443,15 +436,14 @@ async fn flush_relay_outbox_retries_failed_relay_without_full_sync() { let recipient_address = Address::new(recipient_account.id()) .with_routing_parameters(RoutingParameters::new(AddressInterface::BasicWallet)); - let note = P2idNote::create( - sender_account.id(), - recipient_account.id(), - vec![], - NoteType::Private, - NoteAttachments::empty(), - sender.rng(), - ) - .unwrap(); + let note: Note = P2idNote::builder() + .sender(sender_account.id()) + .target(recipient_account.id()) + .note_type(NoteType::Private) + .generate_serial_number(sender.rng()) + .build() + .unwrap() + .into(); // Transport-delivered notes carry no metadata (hence no `NoteId`); match by // details commitment. let note_commitment = note.details_commitment(); @@ -513,15 +505,14 @@ async fn persistent_relay_failure_does_not_block_sync_state() { let recipient_address = Address::new(recipient_account.id()) .with_routing_parameters(RoutingParameters::new(AddressInterface::BasicWallet)); - let note = P2idNote::create( - sender_account.id(), - recipient_account.id(), - vec![], - NoteType::Private, - NoteAttachments::empty(), - sender.rng(), - ) - .unwrap(); + let note: Note = P2idNote::builder() + .sender(sender_account.id()) + .target(recipient_account.id()) + .note_type(NoteType::Private) + .generate_serial_number(sender.rng()) + .build() + .unwrap() + .into(); // The relay fails and the payload is persisted to the outbox. let _ = sender @@ -553,15 +544,14 @@ async fn send_private_note_with_block_hint_delivers_note() { let recipient_address = Address::new(recipient_account.id()) .with_routing_parameters(RoutingParameters::new(AddressInterface::BasicWallet)); - let note = P2idNote::create( - sender_account.id(), - recipient_account.id(), - vec![], - NoteType::Private, - NoteAttachments::empty(), - sender.rng(), - ) - .unwrap(); + let note: Note = P2idNote::builder() + .sender(sender_account.id()) + .target(recipient_account.id()) + .note_type(NoteType::Private) + .generate_serial_number(sender.rng()) + .build() + .unwrap() + .into(); sender .send_private_note_with_block_hint(note, &recipient_address, BlockNumber::from(0)) diff --git a/crates/testing/test-node-genesis/Cargo.toml b/crates/testing/test-node-genesis/Cargo.toml index fa6a4fdb23..423b0eaacb 100644 --- a/crates/testing/test-node-genesis/Cargo.toml +++ b/crates/testing/test-node-genesis/Cargo.toml @@ -19,7 +19,7 @@ path = "src/bin/gen-genesis.rs" # Miden dependencies miden-agglayer = { workspace = true } miden-protocol = { features = ["std", "testing"], workspace = true } -miden-standards = { features = ["std"], workspace = true } +miden-standards = { features = ["std", "testing"], workspace = true } # External dependencies anyhow = { workspace = true } diff --git a/crates/testing/test-node-genesis/src/agglayer.rs b/crates/testing/test-node-genesis/src/agglayer.rs index 355bb7d7e4..3261b9522d 100644 --- a/crates/testing/test-node-genesis/src/agglayer.rs +++ b/crates/testing/test-node-genesis/src/agglayer.rs @@ -19,11 +19,6 @@ use miden_standards::account::wallets::BasicWallet; use rand_chacha::ChaCha20Rng; use rand_chacha::rand_core::SeedableRng; -/// `AggLayer` network ID assigned to the Miden chain (the protocol's `MIDEN_NETWORK_ID` MASM -/// constant). Claim validation compares the leaf's `destination_network` to this value, so it -/// must match the `MIDEN_NETWORK_ID` used by the foundry-generated test vectors. -pub const MIDEN_AGGLAYER_NETWORK_ID: u32 = 77; - /// File names for agglayer genesis account exports. pub const BRIDGE_ADMIN_ACCOUNT_FILE: &str = "bridge_admin.mac"; pub const GER_MANAGER_ACCOUNT_FILE: &str = "ger_manager.mac"; @@ -66,12 +61,8 @@ pub fn create_agglayer_genesis_accounts() -> Result { // 3. Create and deploy the Bridge account (unconfigured; configured at test time). let bridge_seed: Word = rng.random::<[u32; 4]>().map(Felt::from).into(); - let bridge = create_bridge_account( - bridge_seed, - admin_account.id(), - ger_account.id(), - MIDEN_AGGLAYER_NETWORK_ID, - ); + let bridge = + create_bridge_account(bridge_seed, admin_account.id(), ger_account.id(), ger_account.id()); let bridge = set_nonce_to_one(bridge); // 4. Create and deploy the Faucet. In protocol 0.15 the faucet no longer stores conversion diff --git a/crates/testing/test-node-genesis/src/lib.rs b/crates/testing/test-node-genesis/src/lib.rs index a3dd7a55fd..bc59d1c26b 100644 --- a/crates/testing/test-node-genesis/src/lib.rs +++ b/crates/testing/test-node-genesis/src/lib.rs @@ -23,17 +23,11 @@ use miden_protocol::account::{ }; use miden_protocol::asset::{Asset, AssetAmount, FungibleAsset, TokenSymbol}; use miden_protocol::{ONE, Word}; -use miden_standards::AuthMethod; -use miden_standards::account::access::AccessControl; use miden_standards::account::auth::AuthSingleSig; -use miden_standards::account::faucets::{FungibleFaucet, TokenName, create_fungible_faucet}; -use miden_standards::account::policies::{ - BurnPolicyConfig, - MintPolicyConfig, - PolicyRegistration, - TokenPolicyManager, -}; +use miden_standards::account::faucets::{FungibleFaucet, TokenName, create_user_fungible_faucet}; +use miden_standards::account::policies::{BurnPolicy, MintPolicy, TokenPolicyManager}; use miden_standards::account::wallets::BasicWallet; +use miden_standards::testing::faucet::user_faucet_single_sig_acl; use rand_chacha::ChaCha20Rng; use rand_chacha::rand_core::SeedableRng; @@ -125,9 +119,10 @@ fn generate_genesis_account() -> anyhow::Result { let mut rng = ChaCha20Rng::from_seed(random()); let secret = AuthSecretKey::new_falcon512_poseidon2_with_rng(&mut rng); - let auth_method = AuthMethod::SingleSig { - approver: (secret.public_key().to_commitment(), AuthScheme::Falcon512Poseidon2), - }; + let auth_component = user_faucet_single_sig_acl( + secret.public_key().to_commitment(), + AuthScheme::Falcon512Poseidon2, + ); let symbol = TokenSymbol::try_from("TST").expect("TST should be a valid token symbol"); let name = TokenName::new(&symbol.to_string()).expect("token symbol is a valid token name"); @@ -137,13 +132,12 @@ fn generate_genesis_account() -> anyhow::Result { .decimals(12) .max_supply(AssetAmount::new(1_000_000_000_000).unwrap()) .build()?; - let account = create_fungible_faucet( + let account = create_user_fungible_faucet( rng.random(), faucet, - AccountType::Public, - auth_method, - AccessControl::AuthControlled, + auth_component, allow_all_policy_manager(), + AccountType::Public, )?; // Force the account nonce to 1. @@ -217,9 +211,10 @@ fn create_single_test_faucet(index: u128, secret: &AuthSecretKey) -> anyhow::Res .try_into() .expect("concatenating two 16-byte arrays yields exactly 32 bytes"); - let auth_scheme = AuthMethod::SingleSig { - approver: (secret.public_key().to_commitment(), AuthScheme::Falcon512Poseidon2), - }; + let auth_component = user_faucet_single_sig_acl( + secret.public_key().to_commitment(), + AuthScheme::Falcon512Poseidon2, + ); let symbol = TokenSymbol::new("TKN")?; let name = TokenName::new(&symbol.to_string()).expect("token symbol is a valid token name"); @@ -229,13 +224,12 @@ fn create_single_test_faucet(index: u128, secret: &AuthSecretKey) -> anyhow::Res .decimals(FAUCET_DECIMALS) .max_supply(AssetAmount::new(u64::from(FAUCET_MAX_SUPPLY)).unwrap()) .build()?; - let faucet = create_fungible_faucet( + let faucet = create_user_fungible_faucet( init_seed, faucet_component, - AccountType::Public, - auth_scheme, - AccessControl::AuthControlled, + auth_component, allow_all_policy_manager(), + AccountType::Public, )?; // Set nonce to ONE to indicate the account is deployed (see generate_genesis_account) @@ -284,11 +278,10 @@ fn allow_all_policy_manager() -> TokenPolicyManager { // assets via `FungibleAsset::new`, which defaults to `Disabled`, so adding transfer // policies makes `mint_and_send` reject the mint with // `ERR_FUNGIBLE_MINT_NOTE_ASSET_NOT_FROM_THIS_FAUCET`. - TokenPolicyManager::new() - .with_mint_policy(MintPolicyConfig::AllowAll, PolicyRegistration::Active) - .expect("allow-all mint policy should register") - .with_burn_policy(BurnPolicyConfig::AllowAll, PolicyRegistration::Active) - .expect("allow-all burn policy should register") + TokenPolicyManager::builder() + .active_mint_policy(MintPolicy::allow_all()) + .active_burn_policy(BurnPolicy::allow_all()) + .build() } /// Creates a storage map with many entries for stress-testing storage handling. diff --git a/scripts/start-test-node.sh b/scripts/start-test-node.sh index 9b66724b32..f8ce8e4055 100755 --- a/scripts/start-test-node.sh +++ b/scripts/start-test-node.sh @@ -43,7 +43,7 @@ NODE_BINS=(miden-validator miden-node miden-ntx-builder miden-remote-prover) # Resolve the pinned node source from Cargo.lock: a git pin takes precedence, otherwise use the # crates.io version locked for `miden-node-proto-build`. -SRC_LINE="$(grep -m1 'source = "git+https://github.com/0xMiden/node' "$ROOT/Cargo.lock" || true)" +SRC_LINE="$(grep -m1 'source = "git+https://github.com/0xMiden/miden-node' "$ROOT/Cargo.lock" || true)" if [ -n "$SRC_LINE" ]; then NODE_SOURCE="git" SRC="${SRC_LINE#*\"git+}"; SRC="${SRC%\"}" @@ -54,7 +54,7 @@ else NODE_SOURCE="registry" NODE_VERSION="$(awk -F'"' '/^name = "miden-node-proto-build"$/ { getline; print $2; exit }' "$ROOT/Cargo.lock")" [ -n "$NODE_VERSION" ] || { - echo "error: no 0xMiden/node git source and no miden-node-proto-build version in Cargo.lock" >&2 + echo "error: no 0xMiden/miden-node git source and no miden-node-proto-build version in Cargo.lock" >&2 exit 1 } NODE_REV="v$NODE_VERSION" From 88772f9cc5d677a81e90a2a1c2e63f9ca93e2f89 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Mon, 29 Jun 2026 15:41:11 -0300 Subject: [PATCH 02/20] fix: failing tests --- .../testing/miden-client-tests/src/tests.rs | 6 +++++- .../miden-client-tests/src/tests/transport.rs | 20 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/crates/testing/miden-client-tests/src/tests.rs b/crates/testing/miden-client-tests/src/tests.rs index a116a135cd..ccfeeb3909 100644 --- a/crates/testing/miden-client-tests/src/tests.rs +++ b/crates/testing/miden-client-tests/src/tests.rs @@ -70,6 +70,7 @@ use miden_protocol::account::{ AccountComponentMetadata, AccountHeader, AccountId, + AccountIdVersion, AccountType, StorageMap, StorageMapKey, @@ -4148,8 +4149,10 @@ async fn sync_stores_private_note_attachments() { // 1. Build a mock chain with a sender and a public target account (the attachment target must // be public). let mut mock_chain_builder = MockChainBuilder::new(); + let faucet_id = AccountId::dummy([7u8; 15], AccountIdVersion::Version1, AccountType::Public); + let note_asset = FungibleAsset::new(faucet_id, 100).unwrap(); let sender = mock_chain_builder - .add_existing_mock_account(miden_testing::Auth::IncrNonce) + .add_existing_mock_account_with_assets(miden_testing::Auth::IncrNonce, [note_asset.into()]) .unwrap(); let target = mock_chain_builder.add_existing_wallet(miden_testing::Auth::IncrNonce).unwrap(); @@ -4160,6 +4163,7 @@ async fn sync_stores_private_note_attachments() { let private_note = P2idNote::builder() .sender(sender.id()) .target(target.id()) + .asset(note_asset) .note_type(NoteType::Private) .attachments(attachments.clone().into_vec()) .generate_serial_number(&mut note_rng) diff --git a/crates/testing/miden-client-tests/src/tests/transport.rs b/crates/testing/miden-client-tests/src/tests/transport.rs index 1d7441742a..146ea84946 100644 --- a/crates/testing/miden-client-tests/src/tests/transport.rs +++ b/crates/testing/miden-client-tests/src/tests/transport.rs @@ -19,6 +19,8 @@ use miden_client::testing::note_transport::{ use miden_client::utils::RwLock; use miden_client_sqlite_store::ClientBuilderSqliteExt; use miden_protocol::Felt; +use miden_protocol::account::{AccountId, AccountIdVersion, AccountType as ProtocolAccountType}; +use miden_protocol::asset::{Asset, FungibleAsset}; use miden_protocol::block::BlockNumber; use miden_protocol::crypto::rand::RandomCoin; use miden_protocol::note::NoteType as ProtocolNoteType; @@ -45,6 +47,7 @@ async fn transport_basic() { let note: Note = P2idNote::builder() .sender(sender_account.id()) .target(recipient_account.id()) + .asset(dummy_asset()) .note_type(NoteType::Private) .generate_serial_number(sender.rng()) .build() @@ -92,6 +95,7 @@ async fn transport_cursor_pagination() { let note_a: Note = P2idNote::builder() .sender(sender_account.id()) .target(recipient_account.id()) + .asset(dummy_asset()) .note_type(NoteType::Private) .generate_serial_number(sender.rng()) .build() @@ -101,6 +105,7 @@ async fn transport_cursor_pagination() { let note_b: Note = P2idNote::builder() .sender(sender_account.id()) .target(recipient_account.id()) + .asset(dummy_asset()) .note_type(NoteType::Private) .generate_serial_number(sender.rng()) .build() @@ -141,6 +146,7 @@ async fn transport_duplicate_note_handling() { let note: Note = P2idNote::builder() .sender(sender_account.id()) .target(recipient_account.id()) + .asset(dummy_asset()) .note_type(NoteType::Private) .generate_serial_number(sender.rng()) .build() @@ -188,6 +194,7 @@ async fn fetch_all_private_notes_drains_across_batches() { let note: Note = P2idNote::builder() .sender(sender_account.id()) .target(recipient_account.id()) + .asset(dummy_asset()) .note_type(NoteType::Private) .generate_serial_number(sender.rng()) .build() @@ -227,6 +234,7 @@ async fn transport_fetch_no_matching_tags() { let note: Note = P2idNote::builder() .sender(sender_account.id()) .target(recipient_account.id()) + .asset(dummy_asset()) .note_type(NoteType::Private) .generate_serial_number(sender.rng()) .build() @@ -378,6 +386,7 @@ async fn private_note_relay_recovers_after_transient_ntl_failure() { let note: Note = P2idNote::builder() .sender(sender_account.id()) .target(recipient_account.id()) + .asset(dummy_asset()) .note_type(NoteType::Private) .generate_serial_number(sender.rng()) .build() @@ -439,6 +448,7 @@ async fn flush_relay_outbox_retries_failed_relay_without_full_sync() { let note: Note = P2idNote::builder() .sender(sender_account.id()) .target(recipient_account.id()) + .asset(dummy_asset()) .note_type(NoteType::Private) .generate_serial_number(sender.rng()) .build() @@ -508,6 +518,7 @@ async fn persistent_relay_failure_does_not_block_sync_state() { let note: Note = P2idNote::builder() .sender(sender_account.id()) .target(recipient_account.id()) + .asset(dummy_asset()) .note_type(NoteType::Private) .generate_serial_number(sender.rng()) .build() @@ -547,6 +558,7 @@ async fn send_private_note_with_block_hint_delivers_note() { let note: Note = P2idNote::builder() .sender(sender_account.id()) .target(recipient_account.id()) + .asset(dummy_asset()) .note_type(NoteType::Private) .generate_serial_number(sender.rng()) .build() @@ -630,6 +642,14 @@ async fn fetch_private_notes_without_floor_falls_back_to_lookback_window() { // HELPERS // ================================================================================================ +/// A dummy fungible asset for transport-layer notes. P2ID notes require at least one asset, and +/// these notes are never consumed on-chain, so the issuing faucet only needs to be a valid ID. +fn dummy_asset() -> Asset { + let faucet_id = + AccountId::dummy([7u8; 15], AccountIdVersion::Version1, ProtocolAccountType::Public); + FungibleAsset::new(faucet_id, 100).unwrap().into() +} + pub async fn create_test_client_transport( mock_node: Arc>, ) -> (MockClient, FilesystemKeyStore) { From 3da610d75a354423358b7df760ddbdbb1372279c Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 30 Jun 2026 17:52:58 -0300 Subject: [PATCH 03/20] chore: bump miden versions --- Cargo.lock | 26 +- bin/integration-tests/src/tests/client.rs | 52 ++-- bin/integration-tests/src/tests/fpi.rs | 16 +- .../src/tests/network_transaction.rs | 25 +- bin/integration-tests/src/tests/onchain.rs | 6 +- .../src/tests/pass_through.rs | 7 +- .../src/tests/swap_transaction.rs | 6 +- bin/miden-bench/src/deploy.rs | 6 +- .../src/generators/large_account.rs | 6 +- bin/miden-cli/src/commands/new_account.rs | 6 +- bin/miden-cli/src/utils.rs | 8 +- bin/miden-cli/tests/cli.rs | 6 +- crates/rust-client/src/account/mod.rs | 11 +- crates/rust-client/src/lib.rs | 3 +- crates/rust-client/src/note/import.rs | 27 +- crates/rust-client/src/note/mod.rs | 2 +- crates/rust-client/src/note_transport/mod.rs | 16 +- .../note_record/output_note_record/mod.rs | 11 +- .../rust-client/src/sync/state_sync_update.rs | 42 ++- crates/rust-client/src/test_utils/common.rs | 12 +- crates/rust-client/src/test_utils/mock.rs | 18 +- .../src/transaction/request/mod.rs | 13 +- crates/sqlite-store/src/account/storage.rs | 18 +- crates/sqlite-store/src/account/tests.rs | 242 +++++++++++------- .../testing/miden-client-tests/src/tests.rs | 116 +++++---- .../miden-client-tests/src/tests/store.rs | 25 +- .../src/tests/transaction.rs | 9 +- .../testing/test-node-genesis/src/agglayer.rs | 6 +- crates/testing/test-node-genesis/src/lib.rs | 16 +- 29 files changed, 446 insertions(+), 311 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 021e16b195..915e73ebd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1101,7 +1101,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1989,7 +1989,7 @@ dependencies = [ [[package]] name = "miden-agglayer" version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#2547c0e425f4ab78295850cc97df81be4ce44848" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" dependencies = [ "alloy-sol-types", "fs-err", @@ -2069,7 +2069,7 @@ dependencies = [ [[package]] name = "miden-block-prover" version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#2547c0e425f4ab78295850cc97df81be4ce44848" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" dependencies = [ "miden-protocol", "thiserror", @@ -2500,7 +2500,7 @@ dependencies = [ [[package]] name = "miden-node-proto-build" version = "0.15.0" -source = "git+https://github.com/0xMiden/miden-node.git?branch=jmunoz-update-miden-protocol#1c67505215a61b4acf1a542f711e4500910c89a1" +source = "git+https://github.com/0xMiden/miden-node.git?branch=jmunoz-update-miden-protocol#b77301f76d59d3600a8a7f194d9454f1bdffe560" dependencies = [ "build-rs", "codegen", @@ -2576,7 +2576,7 @@ dependencies = [ [[package]] name = "miden-protocol" version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#2547c0e425f4ab78295850cc97df81be4ce44848" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" dependencies = [ "bech32", "fs-err", @@ -2620,7 +2620,7 @@ dependencies = [ [[package]] name = "miden-remote-prover-client" version = "0.15.0" -source = "git+https://github.com/0xMiden/miden-node.git?branch=jmunoz-update-miden-protocol#1c67505215a61b4acf1a542f711e4500910c89a1" +source = "git+https://github.com/0xMiden/miden-node.git?branch=jmunoz-update-miden-protocol#b77301f76d59d3600a8a7f194d9454f1bdffe560" dependencies = [ "build-rs", "fs-err", @@ -2651,7 +2651,7 @@ dependencies = [ [[package]] name = "miden-standards" version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#2547c0e425f4ab78295850cc97df81be4ce44848" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" dependencies = [ "bon", "fs-err", @@ -2689,7 +2689,7 @@ dependencies = [ [[package]] name = "miden-testing" version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#2547c0e425f4ab78295850cc97df81be4ce44848" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" dependencies = [ "anyhow", "itertools", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "miden-tx" version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#2547c0e425f4ab78295850cc97df81be4ce44848" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" dependencies = [ "miden-processor", "miden-protocol", @@ -2721,7 +2721,7 @@ dependencies = [ [[package]] name = "miden-tx-batch" version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#2547c0e425f4ab78295850cc97df81be4ce44848" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" dependencies = [ "miden-processor", "miden-protocol", @@ -4003,7 +4003,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4510,7 +4510,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5323,7 +5323,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/bin/integration-tests/src/tests/client.rs b/bin/integration-tests/src/tests/client.rs index 647585a506..44a938d8a2 100644 --- a/bin/integration-tests/src/tests/client.rs +++ b/bin/integration-tests/src/tests/client.rs @@ -4,7 +4,7 @@ use std::time::Duration; use anyhow::{Context, Result}; use assert_matches::assert_matches; -use miden_client::account::component::{AccountComponent, AccountComponentMetadata}; +use miden_client::account::component::{AccountComponent, AccountComponentMetadata, Approver}; use miden_client::account::{ Account, AccountBuilder, @@ -21,6 +21,7 @@ use miden_client::asset::{Asset, FungibleAsset}; use miden_client::auth::{AuthSchemeId, AuthSecretKey, AuthSingleSig, RPO_FALCON_SCHEME_ID}; use miden_client::builder::ClientBuilder; use miden_client::keystore::FilesystemKeyStore; +use miden_client::note::standards::NoteSyncHint; use miden_client::note::{BlockNumber, NoteFile, NoteTag, NoteType}; use miden_client::rpc::domain::account::{ AccountStorageRequirements, @@ -284,10 +285,12 @@ pub async fn test_import_expected_notes(client_config: ClientConfig) -> Result<( // Import the node before it's committed onchain works if we have full `NoteDetails` client_2.add_note_tag(note.metadata().unwrap().tag()).await.unwrap(); client_2 - .import_notes(&[NoteFile::NoteDetails { + .import_notes(&[NoteFile::ExpectedNote { details: note.clone().into(), - after_block_num: client_1.get_sync_height().await.unwrap(), - tag: Some(note.metadata().unwrap().tag()), + sync_hint: NoteSyncHint::new( + client_1.get_sync_height().await.unwrap(), + note.metadata().unwrap().tag(), + ), }]) .await .unwrap(); @@ -361,10 +364,9 @@ pub async fn test_import_expected_note_uncommitted(client_config: ClientConfig) // If the verification is requested before execution then the import should fail let imported_commitment = client_2 - .import_notes(&[NoteFile::NoteDetails { - details: note.into(), - after_block_num: 0.into(), - tag: None, + .import_notes(&[NoteFile::ExpectedNote { + details: note.clone().into(), + sync_hint: NoteSyncHint::new(0.into(), note.metadata().unwrap().tag()), }]) .await?[0]; @@ -412,10 +414,9 @@ pub async fn test_import_expected_notes_from_the_past_as_committed( // importing the note before client_2 is synced will result in a note with `Expected` state let commitment = client_2 - .import_notes(&[NoteFile::NoteDetails { + .import_notes(&[NoteFile::ExpectedNote { details: note.clone().into(), - after_block_num: block_height_before, - tag: Some(note.metadata().unwrap().tag()), + sync_hint: NoteSyncHint::new(block_height_before, note.metadata().unwrap().tag()), }]) .await?[0]; @@ -432,10 +433,9 @@ pub async fn test_import_expected_notes_from_the_past_as_committed( // Note already imported assert!( client_2 - .import_notes(&[NoteFile::NoteDetails { + .import_notes(&[NoteFile::ExpectedNote { details: note.clone().into(), - after_block_num: block_height_before, - tag: Some(note.metadata().unwrap().tag()), + sync_hint: NoteSyncHint::new(block_height_before, note.metadata().unwrap().tag()), }]) .await? .is_empty() @@ -982,10 +982,10 @@ pub async fn test_import_consumed_note_with_proof(client_config: ClientConfig) - // Import the consumed note client_2 - .import_notes(&[NoteFile::NoteWithProof( - note.clone().try_into().unwrap(), - note.inclusion_proof().unwrap().clone(), - )]) + .import_notes(&[NoteFile::Committed { + note: note.clone().try_into().unwrap(), + proof: note.inclusion_proof().unwrap().clone(), + }]) .await?; // A `ConsumedExternal` note has no metadata, so look it up by its details commitment. @@ -1115,10 +1115,10 @@ pub async fn test_import_note_with_proof(client_config: ClientConfig) -> Result< // Import the consumed note client_2 - .import_notes(&[NoteFile::NoteWithProof( - note.clone().try_into().unwrap(), - note.inclusion_proof().unwrap().clone(), - )]) + .import_notes(&[NoteFile::Committed { + note: note.clone().try_into().unwrap(), + proof: note.inclusion_proof().unwrap().clone(), + }]) .await?; let imported_note = client_2.get_input_note(note.id().unwrap()).await?.unwrap(); @@ -1739,9 +1739,11 @@ pub async fn test_get_account_storage_map_key_filtering(client_config: ClientCon .map_err(|err| anyhow::anyhow!(err))?; let key_pair = AuthSecretKey::new_falcon512_poseidon2(); - let auth_component: AccountComponent = - AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2) - .into(); + let auth_component: AccountComponent = AuthSingleSig::new(Approver::new( + key_pair.public_key().to_commitment(), + AuthSchemeId::Falcon512Poseidon2, + )) + .into(); let account = AccountBuilder::new(Default::default()) .with_component(component) diff --git a/bin/integration-tests/src/tests/fpi.rs b/bin/integration-tests/src/tests/fpi.rs index f9a6194206..93aceb9f2b 100644 --- a/bin/integration-tests/src/tests/fpi.rs +++ b/bin/integration-tests/src/tests/fpi.rs @@ -15,7 +15,13 @@ use miden_client::account::{ StorageSlotName, }; use miden_client::assembly::CodeBuilder; -use miden_client::auth::{AuthSchemeId, AuthSecretKey, AuthSingleSig, RPO_FALCON_SCHEME_ID}; +use miden_client::auth::{ + Approver, + AuthSchemeId, + AuthSecretKey, + AuthSingleSig, + RPO_FALCON_SCHEME_ID, +}; use miden_client::keystore::{FilesystemKeyStore, Keystore}; use miden_client::rpc::domain::account::AccountStorageRequirements; use miden_client::testing::common::*; @@ -609,19 +615,19 @@ fn foreign_account_with_code( let (key_pair, auth_component) = match auth_scheme { AuthSchemeId::Falcon512Poseidon2 => { let key_pair = AuthSecretKey::new_falcon512_poseidon2(); - let auth_component: AccountComponent = AuthSingleSig::new( + let auth_component: AccountComponent = AuthSingleSig::new(Approver::new( key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2, - ) + )) .into(); (key_pair, auth_component) }, AuthSchemeId::EcdsaK256Keccak => { let key_pair = AuthSecretKey::new_ecdsa_k256_keccak(); - let auth_component: AccountComponent = AuthSingleSig::new( + let auth_component: AccountComponent = AuthSingleSig::new(Approver::new( key_pair.public_key().to_commitment(), AuthSchemeId::EcdsaK256Keccak, - ) + )) .into(); (key_pair, auth_component) }, diff --git a/bin/integration-tests/src/tests/network_transaction.rs b/bin/integration-tests/src/tests/network_transaction.rs index 0f3550e6a9..d37b01ddf0 100644 --- a/bin/integration-tests/src/tests/network_transaction.rs +++ b/bin/integration-tests/src/tests/network_transaction.rs @@ -372,14 +372,13 @@ fn build_non_standard_mint( NoteTag::with_account_target(target).into(), )?; let target_ntx = NetworkAccountTarget::new(faucet.id(), NoteExecutionHint::Always)?; - let attachments = NoteAttachments::new(vec![target_ntx.into()])?; - let mint_note = MintNote::create( - faucet.id(), - faucet_owner, // must equal the faucet owner, checked by mint_and_send - mint_storage, - attachments, - client.rng(), - )?; + let mint_note: Note = MintNote::builder() + .sender(faucet_owner) // must equal the faucet owner, checked by mint_and_send + .mint_storage(mint_storage) + .attachment(target_ntx) + .generate_serial_number(client.rng()) + .build()? + .into(); Ok((custom_script, mint_note, expected_output_commitment)) } @@ -702,9 +701,13 @@ pub async fn test_ntx_mint_produces_public_p2id(client_config: ClientConfig) -> )?; let target_ntx = NetworkAccountTarget::new(faucet.id(), NoteExecutionHint::Always)?; - let attachments = NoteAttachments::new(vec![target_ntx.into()])?; - let mint_note = - MintNote::create(faucet.id(), alice.id(), mint_storage, attachments, client.rng())?; + let mint_note: Note = MintNote::builder() + .sender(alice.id()) + .mint_storage(mint_storage) + .attachment(target_ntx) + .generate_serial_number(client.rng()) + .build()? + .into(); let mint_tx = TransactionRequestBuilder::new().own_output_notes(vec![mint_note]).build()?; execute_tx_and_sync(&mut client, alice.id(), mint_tx).await?; diff --git a/bin/integration-tests/src/tests/onchain.rs b/bin/integration-tests/src/tests/onchain.rs index 11f1ea5238..c05e583b54 100644 --- a/bin/integration-tests/src/tests/onchain.rs +++ b/bin/integration-tests/src/tests/onchain.rs @@ -5,6 +5,7 @@ use miden_client::account::{AccountType, build_wallet_id}; use miden_client::asset::{Asset, FungibleAsset}; use miden_client::auth::RPO_FALCON_SCHEME_ID; use miden_client::keystore::Keystore; +use miden_client::note::standards::NoteSyncHint; use miden_client::note::{ BlockNumber, Note, @@ -787,10 +788,9 @@ pub async fn test_sync_note_with_attachment(client_config: ClientConfig) -> Resu // A private note's details never appear on-chain, so client 2 must receive the details. client_2.add_note_tag(private_note.metadata().tag()).await?; client_2 - .import_notes(&[NoteFile::NoteDetails { + .import_notes(&[NoteFile::ExpectedNote { details: private_note.clone().into(), - after_block_num: 0u32.into(), - tag: Some(private_note.metadata().tag()), + sync_hint: NoteSyncHint::new(0u32.into(), private_note.metadata().tag()), }]) .await?; diff --git a/bin/integration-tests/src/tests/pass_through.rs b/bin/integration-tests/src/tests/pass_through.rs index 5e34dfa759..42facd314e 100644 --- a/bin/integration-tests/src/tests/pass_through.rs +++ b/bin/integration-tests/src/tests/pass_through.rs @@ -12,6 +12,7 @@ use miden_client::account::{ use miden_client::assembly::CodeBuilder; use miden_client::asset::{Asset, FungibleAsset}; use miden_client::auth::{ + Approver, AuthSchemeId, AuthSingleSigAcl, AuthSingleSigAclConfig, @@ -209,8 +210,10 @@ async fn create_pass_through_account( let acl_config = AuthSingleSigAclConfig::new(BTreeSet::new()).unwrap(); - let auth_component = - AuthSingleSigAcl::new(pub_key.into(), AuthSchemeId::Falcon512Poseidon2, acl_config); + let auth_component = AuthSingleSigAcl::new( + Approver::new(pub_key.into(), AuthSchemeId::Falcon512Poseidon2), + acl_config, + ); let account = AccountBuilder::new(init_seed) .account_type(AccountType::Private) .with_auth_component(auth_component) diff --git a/bin/integration-tests/src/tests/swap_transaction.rs b/bin/integration-tests/src/tests/swap_transaction.rs index 844b18006c..ddcc4af4ba 100644 --- a/bin/integration-tests/src/tests/swap_transaction.rs +++ b/bin/integration-tests/src/tests/swap_transaction.rs @@ -2,6 +2,7 @@ use anyhow::{Context, Result}; use miden_client::account::AccountType; use miden_client::asset::{Asset, FungibleAsset}; use miden_client::auth::RPO_FALCON_SCHEME_ID; +use miden_client::note::standards::NoteSyncHint; use miden_client::note::{Note, NoteDetails, NoteFile, NoteType, SwapNote}; use miden_client::store::NoteFilter; use miden_client::testing::common::*; @@ -265,10 +266,9 @@ pub async fn test_swap_private(client_config: ClientConfig) -> Result<()> { ); client2.add_note_tag(tag).await?; client2 - .import_notes(&[NoteFile::NoteDetails { + .import_notes(&[NoteFile::ExpectedNote { details: output_note.try_into()?, - after_block_num: client1.get_sync_height().await?, - tag: Some(tag), + sync_hint: NoteSyncHint::new(client1.get_sync_height().await?, tag), }]) .await?; diff --git a/bin/miden-bench/src/deploy.rs b/bin/miden-bench/src/deploy.rs index 56ac8d9ac3..12e9d69e97 100644 --- a/bin/miden-bench/src/deploy.rs +++ b/bin/miden-bench/src/deploy.rs @@ -15,7 +15,7 @@ use miden_client::account::{ StorageSlotName, }; use miden_client::assembly::CodeBuilder; -use miden_client::auth::{AuthSchemeId, AuthSecretKey, AuthSingleSig}; +use miden_client::auth::{Approver, AuthSchemeId, AuthSecretKey, AuthSingleSig}; use miden_client::keystore::{FilesystemKeyStore, Keystore}; use miden_client::transaction::TransactionRequestBuilder; use miden_client::{Client, Serializable}; @@ -103,10 +103,10 @@ fn create_account_with_empty_maps( .expect("basic wallet component should satisfy account component requirements"); let account = AccountBuilder::new(seed) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( sk.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .account_type(AccountType::Public) .with_component(wallet_component) .with_component(expansion_component) diff --git a/bin/miden-bench/src/generators/large_account.rs b/bin/miden-bench/src/generators/large_account.rs index 4b11eaa5cd..4a181313fd 100644 --- a/bin/miden-bench/src/generators/large_account.rs +++ b/bin/miden-bench/src/generators/large_account.rs @@ -35,6 +35,8 @@ use miden_client::account::{ #[cfg(test)] use miden_client::assembly::CodeBuilder; #[cfg(test)] +use miden_client::auth::Approver; +#[cfg(test)] use miden_client::auth::{AuthSchemeId, AuthSecretKey, AuthSingleSig}; /// Configuration for generating large accounts (used in tests) @@ -153,10 +155,10 @@ fn create_large_account(config: &LargeAccountConfig) -> anyhow::Result<(Account, .expect("basic wallet component should satisfy account component requirements"); let account = AccountBuilder::new(config.seed) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( sk.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .account_type(AccountType::Public) .with_component(wallet_component) .with_component(reader_component) diff --git a/bin/miden-cli/src/commands/new_account.rs b/bin/miden-cli/src/commands/new_account.rs index 0bb7ea0ee8..166da8c2a8 100644 --- a/bin/miden-cli/src/commands/new_account.rs +++ b/bin/miden-cli/src/commands/new_account.rs @@ -25,7 +25,7 @@ use miden_client::account::{ AccountType, }; use miden_client::asset::{AssetAmount, TokenSymbol}; -use miden_client::auth::{AuthSchemeId, AuthSecretKey, AuthSingleSig}; +use miden_client::auth::{Approver, AuthSchemeId, AuthSecretKey, AuthSingleSig}; use miden_client::keystore::Keystore; use miden_client::transaction::TransactionRequestBuilder; use miden_client::utils::Deserializable; @@ -542,10 +542,10 @@ async fn create_client_account( } else { debug!("Adding default Falcon auth component"); let kp = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng()); - builder = builder.with_auth_component(AuthSingleSig::new( + builder = builder.with_auth_component(AuthSingleSig::new(Approver::new( kp.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2, - )); + ))); Some(kp) }; diff --git a/bin/miden-cli/src/utils.rs b/bin/miden-cli/src/utils.rs index 29b499479a..df86925cd5 100644 --- a/bin/miden-cli/src/utils.rs +++ b/bin/miden-cli/src/utils.rs @@ -137,8 +137,10 @@ pub async fn print_executed_transaction( // STORAGE VALUES if patch.storage().values().next().is_some() { let mut table = create_dynamic_table(&["Storage Slot", "New Value"]); - for (slot, new_value) in patch.storage().values() { - table.add_row(vec![slot.to_string(), new_value.to_hex()]); + for (slot, value_patch) in patch.storage().values() { + let new_value = + value_patch.value().map_or_else(|| "removed".to_string(), |v| v.to_hex()); + table.add_row(vec![slot.to_string(), new_value]); } println!("Storage changes:"); println!("{table}"); @@ -150,7 +152,7 @@ pub async fn print_executed_transaction( if patch.storage().maps().next().is_some() { let mut table = create_dynamic_table(&["Storage Slot", "Map Key", "New Value"]); for (slot, map_patch) in patch.storage().maps() { - for (key, value) in map_patch.entries() { + for (key, value) in map_patch.entries().into_iter().flat_map(|e| e.as_map().iter()) { table.add_row(vec![slot.to_string(), Word::from(*key).to_hex(), value.to_hex()]); } } diff --git a/bin/miden-cli/tests/cli.rs b/bin/miden-cli/tests/cli.rs index 75a3c33cc7..e96c9150b5 100644 --- a/bin/miden-cli/tests/cli.rs +++ b/bin/miden-cli/tests/cli.rs @@ -14,6 +14,7 @@ use miden_client::auth::{RPO_FALCON_SCHEME_ID, TransactionAuthenticator}; use miden_client::builder::ClientBuilder; use miden_client::crypto::{FeltRng, RandomCoin}; use miden_client::keystore::Keystore; +use miden_client::note::standards::NoteSyncHint; use miden_client::note::{ Note, NoteAssets, @@ -988,10 +989,9 @@ async fn debug_mode_outputs_logs() -> Result<()> { execute_tx_and_sync(&mut client, account.id(), transaction_request).await?; // Export the note - let note_file: NoteFile = NoteFile::NoteDetails { + let note_file: NoteFile = NoteFile::ExpectedNote { details: note.clone().into(), - after_block_num: 0.into(), - tag: Some(note.metadata().tag()), + sync_hint: NoteSyncHint::new(0.into(), note.metadata().tag()), }; // Import the note into the CLI diff --git a/crates/rust-client/src/account/mod.rs b/crates/rust-client/src/account/mod.rs index 61816268b2..ed233e72c0 100644 --- a/crates/rust-client/src/account/mod.rs +++ b/crates/rust-client/src/account/mod.rs @@ -115,7 +115,7 @@ mod account_reader; pub use account_reader::AccountReader; /// Raw access to `miden-standards` account modules for items not curated by `miden-client`. pub use miden_standards::account as standards; -use miden_standards::account::auth::AuthSingleSig; +use miden_standards::account::auth::{Approver, AuthSingleSig}; use miden_standards::account::faucets::FungibleFaucet; // RE-EXPORTS // ================================================================================================ @@ -190,7 +190,7 @@ pub mod component { TokenMetadataError, TokenName, create_network_fungible_faucet, - create_user_fungible_faucet, + create_singlesig_user_fungible_faucet, }; pub use miden_standards::account::policies::{ AllowlistOwnerControlled, @@ -637,7 +637,7 @@ pub fn build_wallet_id( ) -> Result { let auth_scheme = public_key.auth_scheme(); let auth_component: AccountComponent = - AuthSingleSig::new(public_key.to_commitment(), auth_scheme).into(); + AuthSingleSig::new(Approver::new(public_key.to_commitment(), auth_scheme)).into(); let account = AccountBuilder::new(init_seed) .account_type(account_visibility) @@ -658,6 +658,7 @@ mod schema_commitment_tests { AccountBuilder, AccountBuilderSchemaCommitmentExt, AccountType, + Approver, AuthSingleSig, BasicWallet, }; @@ -668,10 +669,10 @@ mod schema_commitment_tests { let key = AuthSecretKey::new_falcon512_poseidon2(); let account = AccountBuilder::new([2u8; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( key.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(BasicWallet) .build_with_schema_commitment() .expect("build_with_schema_commitment"); diff --git a/crates/rust-client/src/lib.rs b/crates/rust-client/src/lib.rs index fa3082a88c..09d137dda7 100644 --- a/crates/rust-client/src/lib.rs +++ b/crates/rust-client/src/lib.rs @@ -144,7 +144,7 @@ pub use miden_protocol::utils::serde::{Deserializable, Serializable, SliceReader // ================================================================================================ pub mod notes { - pub use miden_protocol::note::NoteFile; + pub use miden_standards::note::NoteFile; } /// Provides `AggLayer` bridge components, note constructors, and helper types. @@ -215,6 +215,7 @@ pub mod auth { Signature, }; pub use miden_standards::account::auth::{ + Approver, AuthMultisig, AuthMultisigConfig, AuthSingleSig, diff --git a/crates/rust-client/src/note/import.rs b/crates/rust-client/src/note/import.rs index ea2c1dd2e2..1ab0df1adc 100644 --- a/crates/rust-client/src/note/import.rs +++ b/crates/rust-client/src/note/import.rs @@ -18,12 +18,12 @@ use miden_protocol::note::{ NoteAttachments, NoteDetails, NoteDetailsCommitment, - NoteFile, NoteId, NoteInclusionProof, NoteMetadata, NoteTag, }; +use miden_standards::note::NoteFile; use miden_tx::auth::TransactionAuthenticator; use crate::rpc::RpcError; @@ -46,14 +46,14 @@ where /// with the new information. The tags specified by the `NoteFile`s will start being /// tracked. Returns the details commitments of notes that were successfully imported or /// updated. The details commitment is used (rather than the note ID) because notes imported - /// without metadata — e.g. from [`NoteFile::NoteDetails`] in an `Expected` state — have no + /// without metadata — e.g. from [`NoteFile::ExpectedNote`] in an `Expected` state — have no /// note ID yet, whereas the details commitment is always available. /// /// - If the note files are [`NoteFile::NoteId`], the notes are fetched from the node and stored /// in the client's store. If the note is private or doesn't exist, an error is returned. - /// - If the note files are [`NoteFile::NoteDetails`], new notes are created with the provided + /// - If the note files are [`NoteFile::ExpectedNote`], new notes are created with the provided /// details and tags. - /// - If the note files are [`NoteFile::NoteWithProof`], the notes are stored with the provided + /// - If the note files are [`NoteFile::Committed`], the notes are stored with the provided /// inclusion proof and metadata. The block header data is only fetched from the node if the /// note is committed in the past relative to the client. /// @@ -69,7 +69,7 @@ where ) -> Result, ClientError> { // Deduplicate the incoming files, keeping note IDs and details commitments in separate // collections. `NoteFile::NoteId` entries are keyed by their note ID; detail-carrying - // entries (`NoteDetails`/`NoteWithProof`) are keyed by their details commitment, since + // entries (`ExpectedNote`/`Committed`) are keyed by their details commitment, since // they may have no note ID of their own. let mut ids = BTreeSet::new(); let mut files_by_commitment = BTreeMap::new(); @@ -78,10 +78,10 @@ where NoteFile::NoteId(id) => { ids.insert(*id); }, - NoteFile::NoteDetails { details, .. } => { + NoteFile::ExpectedNote { details, .. } => { files_by_commitment.insert(details.commitment(), note_file.clone()); }, - NoteFile::NoteWithProof(note, _) => { + NoteFile::Committed { note, .. } => { files_by_commitment.insert(note.details_commitment(), note_file.clone()); }, } @@ -120,11 +120,16 @@ where let previous_note = previous_by_commitment.get(&commitment).cloned(); ensure_not_processing(previous_note.as_ref())?; match note_file { - NoteFile::NoteDetails { details, after_block_num, tag } => { - requests_by_details.push((previous_note, details, after_block_num, tag)); + NoteFile::ExpectedNote { details, sync_hint } => { + requests_by_details.push(( + previous_note, + details, + sync_hint.after_block_num(), + Some(sync_hint.tag()), + )); }, - NoteFile::NoteWithProof(note, inclusion_proof) => { - requests_by_proof.push((previous_note, note, inclusion_proof)); + NoteFile::Committed { note, proof } => { + requests_by_proof.push((previous_note, note, proof)); }, NoteFile::NoteId(_) => { unreachable!("files_by_commitment only holds detail-carrying note files") diff --git a/crates/rust-client/src/note/mod.rs b/crates/rust-client/src/note/mod.rs index 3c541df4f1..96a361cf88 100644 --- a/crates/rust-client/src/note/mod.rs +++ b/crates/rust-client/src/note/mod.rs @@ -88,7 +88,6 @@ pub use miden_protocol::note::{ NoteAttachments, NoteDetails, NoteDetailsCommitment, - NoteFile, NoteHeader, NoteId, NoteInclusionProof, @@ -113,6 +112,7 @@ pub use miden_standards::note::{ NetworkAccountTarget, NoteConsumptionStatus, NoteExecutionHint, + NoteFile, P2idNote, P2idNoteStorage, P2ideNote, diff --git a/crates/rust-client/src/note_transport/mod.rs b/crates/rust-client/src/note_transport/mod.rs index 01f8724713..abd6e3b46b 100644 --- a/crates/rust-client/src/note_transport/mod.rs +++ b/crates/rust-client/src/note_transport/mod.rs @@ -12,16 +12,9 @@ use alloc::vec::Vec; use futures::Stream; use miden_protocol::address::Address; use miden_protocol::block::BlockNumber; -use miden_protocol::note::{ - Note, - NoteDetails, - NoteDetailsCommitment, - NoteFile, - NoteHeader, - NoteId, - NoteTag, -}; +use miden_protocol::note::{Note, NoteDetails, NoteDetailsCommitment, NoteHeader, NoteId, NoteTag}; use miden_protocol::utils::serde::Serializable; +use miden_standards::note::{NoteFile, NoteSyncHint}; use miden_tx::auth::TransactionAuthenticator; use miden_tx::utils::serde::{ ByteReader, @@ -376,10 +369,9 @@ where let tag = note.metadata().tag(); // Prefer the sender-provided hint, falling back to the lookback window when absent. let after_block_num = block_hint.unwrap_or(fallback_after_block_num); - let note_file = NoteFile::NoteDetails { + let note_file = NoteFile::ExpectedNote { details: note.into(), - after_block_num, - tag: Some(tag), + sync_hint: NoteSyncHint::new(after_block_num, tag), }; note_requests.push(note_file); } diff --git a/crates/rust-client/src/store/note_record/output_note_record/mod.rs b/crates/rust-client/src/store/note_record/output_note_record/mod.rs index b6187079cc..c4a5933b88 100644 --- a/crates/rust-client/src/store/note_record/output_note_record/mod.rs +++ b/crates/rust-client/src/store/note_record/output_note_record/mod.rs @@ -9,7 +9,6 @@ use miden_protocol::note::{ NoteAttachments, NoteDetails, NoteDetailsCommitment, - NoteFile, NoteId, NoteInclusionProof, NoteMetadata, @@ -18,6 +17,7 @@ use miden_protocol::note::{ PartialNote, }; use miden_protocol::transaction::RawOutputNote; +use miden_standards::note::{NoteFile, NoteSyncHint}; use miden_tx::utils::serde::{ ByteReader, ByteWriter, @@ -279,12 +279,11 @@ impl OutputNoteRecord { NoteExportType::NoteId => Ok(NoteFile::NoteId(self.id())), NoteExportType::NoteDetails => { let after_block_num = self.expected_height(); - let tag = Some(self.metadata().tag()); + let tag = self.metadata().tag(); - Ok(NoteFile::NoteDetails { + Ok(NoteFile::ExpectedNote { details: self.try_into()?, - after_block_num, - tag, + sync_hint: NoteSyncHint::new(after_block_num, tag), }) }, NoteExportType::NoteWithProof => { @@ -295,7 +294,7 @@ impl OutputNoteRecord { ))? .clone(); - Ok(NoteFile::NoteWithProof(self.try_into()?, proof)) + Ok(NoteFile::Committed { note: self.try_into()?, proof }) }, } } diff --git a/crates/rust-client/src/sync/state_sync_update.rs b/crates/rust-client/src/sync/state_sync_update.rs index ae7d49f840..9017b67bb5 100644 --- a/crates/rust-client/src/sync/state_sync_update.rs +++ b/crates/rust-client/src/sync/state_sync_update.rs @@ -9,7 +9,11 @@ use miden_protocol::account::{ AccountPatch, AccountStoragePatch, AccountVaultPatch, + StorageMapPatch, + StorageMapPatchEntries, StorageSlotName, + StorageSlotPatch, + StorageValuePatch, }; use miden_protocol::block::{BlockHeader, BlockNumber}; use miden_protocol::crypto::merkle::mmr::{InOrderIndex, MmrPeaks}; @@ -457,17 +461,39 @@ impl PublicAccountPatch { ) -> Result { let account_id = self.new_header.id(); - let mut storage = AccountStoragePatch::new(); - for (slot_name, new_value) in &self.value_slot_updates { - storage.set_item(slot_name.clone(), *new_value)?; - } + let is_full_state = self.new_header.nonce() == ONE; + + let value_entries = self.value_slot_updates.iter().map(|(slot_name, new_value)| { + let value_patch = if is_full_state { + StorageValuePatch::Create { value: *new_value } + } else { + StorageValuePatch::Update { value: *new_value } + }; + (slot_name.clone(), StorageSlotPatch::Value(value_patch)) + }); + // Map entries are absolute per (slot, key), applying them in block order lets a later block // overwrite an earlier one for the same key, yielding the final value. let mut map_updates: Vec<&StorageMapUpdate> = self.storage_map_updates.iter().collect(); map_updates.sort_by_key(|u| u.block_num); + let mut map_entries_by_slot: BTreeMap = + BTreeMap::new(); for update in map_updates { - storage.set_map_item(update.slot_name.clone(), update.key, update.value)?; + map_entries_by_slot + .entry(update.slot_name.clone()) + .or_default() + .insert(update.key, update.value); } + let map_entries = map_entries_by_slot.into_iter().map(|(slot_name, entries)| { + let map_patch = if is_full_state { + StorageMapPatch::Create { entries } + } else { + StorageMapPatch::Update { entries } + }; + (slot_name, StorageSlotPatch::Map(map_patch)) + }); + + let storage = AccountStoragePatch::from_entries(value_entries.chain(map_entries))?; // Vault entries are absolute per key, applying them in block order yields the final value, // with a `None` asset encoding a removal. @@ -677,8 +703,8 @@ mod tests { let patch = payload(2, vec![], updates, vec![]).compute_account_patch(None).unwrap(); let map = patch.storage().get_map(&map_slot).expect("patch should contain map slot"); - assert_eq!(map.entries().len(), 1); - assert_eq!(*map.entries().values().next().unwrap(), word(300)); + assert_eq!(map.entries().unwrap().as_map().len(), 1); + assert_eq!(*map.entries().unwrap().as_map().values().next().unwrap(), word(300)); } #[test] @@ -691,7 +717,7 @@ mod tests { let patch = payload(2, vec![], updates, vec![]).compute_account_patch(None).unwrap(); let map = patch.storage().get_map(&map_slot).unwrap(); - assert_eq!(map.entries().len(), 2); + assert_eq!(map.entries().unwrap().as_map().len(), 2); } // COMPUTE ACCOUNT PATCH - VAULT diff --git a/crates/rust-client/src/test_utils/common.rs b/crates/rust-client/src/test_utils/common.rs index 164e54b679..35c58939cb 100644 --- a/crates/rust-client/src/test_utils/common.rs +++ b/crates/rust-client/src/test_utils/common.rs @@ -14,7 +14,7 @@ use miden_protocol::asset::{AssetAmount, FungibleAsset, TokenSymbol}; use miden_protocol::note::NoteType; use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE; use miden_protocol::transaction::TransactionId; -use miden_standards::account::auth::AuthSingleSig; +use miden_standards::account::auth::{Approver, AuthSingleSig}; use miden_standards::account::faucets::TokenName; use miden_standards::code_builder::CodeBuilder; use rand::RngCore; @@ -88,7 +88,8 @@ pub async fn insert_new_wallet_with_seed( AuthSchemeId::EcdsaK256Keccak => AuthSecretKey::new_ecdsa_k256_keccak(), other => panic!("unsupported auth scheme: {}", other.as_u8()), }; - let auth_component = AuthSingleSig::new(key_pair.public_key().to_commitment(), auth_scheme); + let auth_component = + AuthSingleSig::new(Approver::new(key_pair.public_key().to_commitment(), auth_scheme)); let account = AccountBuilder::new(init_seed) .account_type(visibility) @@ -118,7 +119,8 @@ pub async fn insert_new_fungible_faucet( AuthSchemeId::EcdsaK256Keccak => AuthSecretKey::new_ecdsa_k256_keccak(), other => panic!("unsupported auth scheme: {}", other.as_u8()), }; - let auth_component = AuthSingleSig::new(key_pair.public_key().to_commitment(), auth_scheme); + let auth_component = + AuthSingleSig::new(Approver::new(key_pair.public_key().to_commitment(), auth_scheme)); // we need to use an initial seed to create the faucet account let mut init_seed = [0u8; 32]; @@ -632,10 +634,10 @@ pub async fn insert_account_with_custom_component( let account = AccountBuilder::new(init_seed) .account_type(visibility) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( pub_key.to_commitment(), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(BasicWallet) .with_component(custom_component) .build_with_schema_commitment() diff --git a/crates/rust-client/src/test_utils/mock.rs b/crates/rust-client/src/test_utils/mock.rs index 90b30dce79..6a3ee3322b 100644 --- a/crates/rust-client/src/test_utils/mock.rs +++ b/crates/rust-client/src/test_utils/mock.rs @@ -271,14 +271,16 @@ impl MockRpcApi { let storage_patch = patch.storage(); for (slot_name, map_patch) in storage_patch.maps() { - for (key, value) in map_patch.entries() { - let storage_map_info = StorageMapUpdate { - block_num: block_number, - slot_name: slot_name.clone(), - key: *key, - value: *value, - }; - updates.push(storage_map_info); + if let Some(entries) = map_patch.entries() { + for (key, value) in entries.as_map() { + let storage_map_info = StorageMapUpdate { + block_num: block_number, + slot_name: slot_name.clone(), + key: *key, + value: *value, + }; + updates.push(storage_map_info); + } } } } diff --git a/crates/rust-client/src/transaction/request/mod.rs b/crates/rust-client/src/transaction/request/mod.rs index 1dd8a4e3d2..bc6b45f30c 100644 --- a/crates/rust-client/src/transaction/request/mod.rs +++ b/crates/rust-client/src/transaction/request/mod.rs @@ -583,7 +583,7 @@ mod tests { ACCOUNT_ID_SENDER, }; use miden_protocol::{EMPTY_WORD, Felt, Word}; - use miden_standards::account::auth::AuthSingleSig; + use miden_standards::account::auth::{Approver, AuthSingleSig}; use miden_standards::note::P2idNote; use miden_standards::testing::account_component::MockAccountComponent; use miden_tx::utils::serde::{Deserializable, Serializable}; @@ -595,10 +595,10 @@ mod tests { #[test] fn transaction_request_serialization() { assert_transaction_request_serialization_with(|| { - AuthSingleSig::new( + AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthScheme::Falcon512Poseidon2, - ) + )) .into() }); } @@ -606,8 +606,11 @@ mod tests { #[test] fn transaction_request_serialization_ecdsa() { assert_transaction_request_serialization_with(|| { - AuthSingleSig::new(PublicKeyCommitment::from(EMPTY_WORD), AuthScheme::EcdsaK256Keccak) - .into() + AuthSingleSig::new(Approver::new( + PublicKeyCommitment::from(EMPTY_WORD), + AuthScheme::EcdsaK256Keccak, + )) + .into() }); } diff --git a/crates/sqlite-store/src/account/storage.rs b/crates/sqlite-store/src/account/storage.rs index 5aa9cbb06c..4630862b53 100644 --- a/crates/sqlite-store/src/account/storage.rs +++ b/crates/sqlite-store/src/account/storage.rs @@ -174,7 +174,8 @@ impl SqliteStore { .map(|(slot_name, map_patch)| { let entries: Vec<(Word, Word)> = map_patch .entries() - .iter() + .into_iter() + .flat_map(|e| e.as_map().iter()) .map(|(key, value)| ((*key).into(), *value)) .collect(); (slot_name, entries) @@ -295,15 +296,24 @@ impl SqliteStore { ) -> Result, StoreError> { let mut updated_slots: BTreeMap = storage_patch .values() - .map(|(slot_name, value)| (slot_name.clone(), (*value, StorageSlotType::Value))) + .map(|(slot_name, value_patch)| { + ( + slot_name.clone(), + (value_patch.value().unwrap_or_default(), StorageSlotType::Value), + ) + }) .collect(); let default_map_root = StorageMap::default().root(); for (slot_name, map_patch) in storage_patch.maps() { let old_root = old_map_roots.get(slot_name).copied().unwrap_or(default_map_root); - let entries: Vec<_> = - map_patch.entries().iter().map(|(key, value)| (*key, *value)).collect(); + let entries: Vec<_> = map_patch + .entries() + .into_iter() + .flat_map(|e| e.as_map().iter()) + .map(|(key, value)| (*key, *value)) + .collect(); let new_root = smt_forest.update_storage_map_nodes(old_root, entries.into_iter())?; updated_slots.insert(slot_name.clone(), (new_root, StorageSlotType::Map)); diff --git a/crates/sqlite-store/src/account/tests.rs b/crates/sqlite-store/src/account/tests.rs index 8a1067d2fe..ba570058f9 100644 --- a/crates/sqlite-store/src/account/tests.rs +++ b/crates/sqlite-store/src/account/tests.rs @@ -27,13 +27,20 @@ use miden_client::auth::{AuthSchemeId, AuthSingleSig, PublicKeyCommitment}; use miden_client::store::{ClientAccountType, Store, StoreError}; use miden_client::testing::common::ACCOUNT_ID_REGULAR; use miden_client::{EMPTY_WORD, Felt, ONE, ZERO}; -use miden_protocol::account::AccountComponentMetadata; +use miden_protocol::account::{ + AccountComponentMetadata, + StorageMapPatch, + StorageMapPatchEntries, + StorageSlotPatch, + StorageValuePatch, +}; use miden_protocol::asset::AssetCallbackFlag; use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET, }; use miden_protocol::testing::constants::NON_FUNGIBLE_ASSET_DATA; +use miden_standards::account::auth::Approver; use rusqlite::params; use crate::SqliteStore; @@ -52,8 +59,11 @@ async fn account_code_insertion_no_duplicates() -> anyhow::Result<()> { AccountComponentMetadata::new("miden::testing::dummy_component"), )?; let account_code = AccountCode::from_components(&[ - AuthSingleSig::new(PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2) - .into(), + AuthSingleSig::new(Approver::new( + PublicKeyCommitment::from(EMPTY_WORD), + AuthSchemeId::Falcon512Poseidon2, + )) + .into(), account_component, ])?; @@ -114,10 +124,10 @@ async fn apply_account_patch_additions() -> anyhow::Result<()> { // Create and insert an account let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(dummy_component) .build_existing()?; @@ -126,13 +136,21 @@ async fn apply_account_patch_additions() -> anyhow::Result<()> { .insert_account(&account, default_address, ClientAccountType::Native) .await?; - let mut storage_patch = AccountStoragePatch::new(); - storage_patch.set_item(value_slot_name.clone(), [ZERO, ZERO, ZERO, ONE].into())?; - storage_patch.set_map_item( - map_slot_name.clone(), - StorageMapKey::new([ONE, ZERO, ZERO, ZERO].into()), - [ONE, ONE, ONE, ONE].into(), - )?; + let mut map_entries = StorageMapPatchEntries::new(); + map_entries + .insert(StorageMapKey::new([ONE, ZERO, ZERO, ZERO].into()), [ONE, ONE, ONE, ONE].into()); + let storage_patch = AccountStoragePatch::from_entries([ + ( + value_slot_name.clone(), + StorageSlotPatch::Value(StorageValuePatch::Update { + value: [ZERO, ZERO, ZERO, ONE].into(), + }), + ), + ( + map_slot_name.clone(), + StorageSlotPatch::Map(StorageMapPatch::Update { entries: map_entries }), + ), + ])?; // The account starts with an empty vault, so the absolute values of the added assets are the // assets themselves. @@ -216,10 +234,10 @@ async fn apply_account_patch_preserves_fungible_callback_flag() -> anyhow::Resul // Create and insert an account with an empty vault. let account = AccountBuilder::new([7; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(BasicWallet) .build_existing()?; store @@ -319,10 +337,10 @@ async fn apply_account_patch_removals() -> anyhow::Result<()> { ]; let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(dummy_component) .with_assets(assets.clone()) .build_existing()?; @@ -331,15 +349,20 @@ async fn apply_account_patch_removals() -> anyhow::Result<()> { .insert_account(&account, default_address, ClientAccountType::Native) .await?; - let mut storage_patch = AccountStoragePatch::new(); - // A cleared value slot is represented by an empty value. - storage_patch.set_item(value_slot_name.clone(), EMPTY_WORD)?; // A removed map entry is represented by an empty value for the key. - storage_patch.set_map_item( - map_slot_name.clone(), - StorageMapKey::new([ONE, ZERO, ZERO, ZERO].into()), - EMPTY_WORD, - )?; + let mut map_entries = StorageMapPatchEntries::new(); + map_entries.insert(StorageMapKey::new([ONE, ZERO, ZERO, ZERO].into()), EMPTY_WORD); + let storage_patch = AccountStoragePatch::from_entries([ + // A cleared value slot is represented by an empty value. + ( + value_slot_name.clone(), + StorageSlotPatch::Value(StorageValuePatch::Update { value: EMPTY_WORD }), + ), + ( + map_slot_name.clone(), + StorageSlotPatch::Map(StorageMapPatch::Update { entries: map_entries }), + ), + ])?; // Both assets are removed: the absolute final state is an empty vault, so each asset's vault // key is marked as removed. @@ -418,10 +441,10 @@ async fn get_account_storage_item_success() -> anyhow::Result<()> { let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(dummy_component) .build_existing()?; @@ -453,10 +476,10 @@ async fn get_account_storage_item_not_found() -> anyhow::Result<()> { let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(dummy_component) .build_existing()?; @@ -496,10 +519,10 @@ async fn get_account_map_item_success() -> anyhow::Result<()> { let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(dummy_component) .build_existing()?; @@ -532,10 +555,10 @@ async fn get_account_map_item_value_slot_error() -> anyhow::Result<()> { let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(dummy_component) .build_existing()?; @@ -565,10 +588,10 @@ async fn get_account_code() -> anyhow::Result<()> { let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(dummy_component) .build_existing()?; @@ -620,10 +643,10 @@ async fn account_reader_nonce_and_status() -> anyhow::Result<()> { let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(dummy_component) .build_with_schema_commitment()?; @@ -698,10 +721,10 @@ async fn account_reader_storage_access() -> anyhow::Result<()> { let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(dummy_component) .build_existing()?; @@ -737,10 +760,10 @@ async fn account_reader_addresses_access() -> anyhow::Result<()> { let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(dummy_component) .build_existing()?; @@ -868,10 +891,10 @@ async fn prune_account_history_multiple_accounts() -> anyhow::Result<()> { )?; let account_b = AccountBuilder::new([1; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(component_b) .build_existing()?; let b_id = account_b.id(); @@ -1075,10 +1098,10 @@ async fn setup_account_with_map( let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(component) .build_existing()?; @@ -1096,12 +1119,15 @@ async fn apply_single_entry_update( map_slot_name: &StorageSlotName, target_nonce: u64, ) -> anyhow::Result<()> { - let mut storage_patch = AccountStoragePatch::new(); - storage_patch.set_map_item( - map_slot_name.clone(), + let mut map_entries = StorageMapPatchEntries::new(); + map_entries.insert( StorageMapKey::new([Felt::from(1u32), ZERO, ZERO, ZERO].into()), [Felt::new_unchecked(target_nonce * 1000), ZERO, ZERO, ZERO].into(), - )?; + ); + let storage_patch = AccountStoragePatch::from_entries([( + map_slot_name.clone(), + StorageSlotPatch::Map(StorageMapPatch::Update { entries: map_entries }), + )])?; let patch = AccountPatch::new( account.id(), @@ -1165,12 +1191,15 @@ async fn undo_account_state_restores_previous_latest() -> anyhow::Result<()> { // Apply a patch (nonce 2) that changes a map entry AND adds a fungible asset. // The vault change ensures the vault root differs between nonce 1 and 2, // which is needed for pop_roots to work correctly. - let mut storage_patch = AccountStoragePatch::new(); - storage_patch.set_map_item( - map_slot_name.clone(), + let mut map_entries = StorageMapPatchEntries::new(); + map_entries.insert( StorageMapKey::new([Felt::from(1u32), ZERO, ZERO, ZERO].into()), [Felt::from(1000u32), ZERO, ZERO, ZERO].into(), - )?; + ); + let storage_patch = AccountStoragePatch::from_entries([( + map_slot_name.clone(), + StorageSlotPatch::Map(StorageMapPatch::Update { entries: map_entries }), + )])?; // The account starts with an empty vault, so the absolute value of the added asset is the // asset itself. let mut vault_patch = AccountVaultPatch::default(); @@ -1277,10 +1306,10 @@ async fn undo_account_state_deletes_account_entirely() -> anyhow::Result<()> { let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(component) .with_assets(vec![ FungibleAsset::new(AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?, 100)? @@ -1345,12 +1374,15 @@ async fn lock_account_affects_latest_and_historical() -> anyhow::Result<()> { let account_id = account.id(); // Apply a patch (nonce 2) with vault change - let mut storage_patch = AccountStoragePatch::new(); - storage_patch.set_map_item( - map_slot_name.clone(), + let mut map_entries = StorageMapPatchEntries::new(); + map_entries.insert( StorageMapKey::new([Felt::from(1u32), ZERO, ZERO, ZERO].into()), [Felt::from(2000u32), ZERO, ZERO, ZERO].into(), - )?; + ); + let storage_patch = AccountStoragePatch::from_entries([( + map_slot_name.clone(), + StorageSlotPatch::Map(StorageMapPatch::Update { entries: map_entries }), + )])?; // The account starts with an empty vault, so the absolute value of the added asset is the // asset itself. let mut vault_patch = AccountVaultPatch::default(); @@ -1474,10 +1506,10 @@ async fn undo_after_update_account_state_does_not_resurrect_removed_entries() -> // Build an existing account at nonce 1: no initial assets let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(component) .build_existing()?; @@ -1534,8 +1566,12 @@ async fn undo_after_update_account_state_does_not_resurrect_removed_entries() -> assert_eq!(m.latest_account_assets, 2, "Should have 2 assets (X + Y)"); // Step 3: Build in-memory state with only {A, B} and {X} (C and Y removed) - let mut storage_patch_remove = AccountStoragePatch::new(); - storage_patch_remove.set_map_item(map_slot_name.clone(), key_c, EMPTY_WORD)?; + let mut map_entries_remove = StorageMapPatchEntries::new(); + map_entries_remove.insert(key_c, EMPTY_WORD); + let storage_patch_remove = AccountStoragePatch::from_entries([( + map_slot_name.clone(), + StorageSlotPatch::Map(StorageMapPatch::Update { entries: map_entries_remove }), + )])?; // Y is removed, so its vault key is marked as removed (absolute final vault is {X}). let mut vault_patch_remove = AccountVaultPatch::default(); vault_patch_remove.remove_asset(asset_y.vault_key()); @@ -1570,12 +1606,12 @@ async fn undo_after_update_account_state_does_not_resurrect_removed_entries() -> assert_eq!(m.latest_account_assets, 1, "Should have 1 asset after update"); // Step 4: Apply a patch that changes entry A and adds asset Z - let mut storage_patch_next = AccountStoragePatch::new(); - storage_patch_next.set_map_item( + let mut map_entries_next = StorageMapPatchEntries::new(); + map_entries_next.insert(key_a, [Felt::from(999u32), ZERO, ZERO, ZERO].into()); + let storage_patch_next = AccountStoragePatch::from_entries([( map_slot_name.clone(), - key_a, - [Felt::from(999u32), ZERO, ZERO, ZERO].into(), - )?; + StorageSlotPatch::Map(StorageMapPatch::Update { entries: map_entries_next }), + )])?; let asset_z = NonFungibleAsset::new(&NonFungibleAssetDetails::new(nf_faucet_id, vec![5, 6, 7, 8])); @@ -1784,12 +1820,15 @@ async fn undo_multiple_nonces_at_once() -> anyhow::Result<()> { assert_eq!(m.latest_account_assets, 0, "Initial: no assets"); // Apply patch at nonce 2: change map entry key=1, add fungible asset - let mut storage_patch_1 = AccountStoragePatch::new(); - storage_patch_1.set_map_item( - map_slot_name.clone(), + let mut map_entries_1 = StorageMapPatchEntries::new(); + map_entries_1.insert( StorageMapKey::new([Felt::from(1u32), ZERO, ZERO, ZERO].into()), [Felt::from(1000u32), ZERO, ZERO, ZERO].into(), - )?; + ); + let storage_patch_1 = AccountStoragePatch::from_entries([( + map_slot_name.clone(), + StorageSlotPatch::Map(StorageMapPatch::Update { entries: map_entries_1 }), + )])?; let asset_1 = FungibleAsset::new(faucet_id, 100)?; // The account starts with an empty vault, so the absolute value of the added asset is the // asset itself. @@ -1834,12 +1873,15 @@ async fn undo_multiple_nonces_at_once() -> anyhow::Result<()> { .await?; // Apply patch at nonce 3: change map entry key=2, add non-fungible asset - let mut storage_patch_2 = AccountStoragePatch::new(); - storage_patch_2.set_map_item( - map_slot_name.clone(), + let mut map_entries_2 = StorageMapPatchEntries::new(); + map_entries_2.insert( StorageMapKey::new([Felt::from(2u32), ZERO, ZERO, ZERO].into()), [Felt::from(2000u32), ZERO, ZERO, ZERO].into(), - )?; + ); + let storage_patch_2 = AccountStoragePatch::from_entries([( + map_slot_name.clone(), + StorageSlotPatch::Map(StorageMapPatch::Update { entries: map_entries_2 }), + )])?; let asset_2 = NonFungibleAsset::new(&NonFungibleAssetDetails::new( nf_faucet_id, NON_FUNGIBLE_ASSET_DATA.into(), @@ -1957,10 +1999,10 @@ async fn undo_after_update_removes_genuinely_new_entries() -> anyhow::Result<()> let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(component) .build_existing()?; @@ -1974,17 +2016,13 @@ async fn undo_after_update_removes_genuinely_new_entries() -> anyhow::Result<()> assert_eq!(m.latest_storage_map_entries, 2, "Initial: 2 map entries"); // Build in-memory state at nonce 2 with {A, B, C, D}: C and D are genuinely new - let mut storage_patch_add = AccountStoragePatch::new(); - storage_patch_add.set_map_item( + let mut map_entries_add = StorageMapPatchEntries::new(); + map_entries_add.insert(key_c, [Felt::from(300u32), ZERO, ZERO, ZERO].into()); + map_entries_add.insert(key_d, [Felt::from(400u32), ZERO, ZERO, ZERO].into()); + let storage_patch_add = AccountStoragePatch::from_entries([( map_slot_name.clone(), - key_c, - [Felt::from(300u32), ZERO, ZERO, ZERO].into(), - )?; - storage_patch_add.set_map_item( - map_slot_name.clone(), - key_d, - [Felt::from(400u32), ZERO, ZERO, ZERO].into(), - )?; + StorageSlotPatch::Map(StorageMapPatch::Update { entries: map_entries_add }), + )])?; // Also add an asset so the vault root changes (avoids SMT root collision on undo). // The account starts with an empty vault, so the absolute value of the added asset is the @@ -2085,13 +2123,21 @@ fn build_patch_for_snapshot_test( value_slot_name: StorageSlotName, map_slot_name: StorageSlotName, ) -> anyhow::Result<(AccountPatch, Account)> { - let mut storage_patch = AccountStoragePatch::new(); - storage_patch.set_item(value_slot_name, [ZERO, ZERO, ZERO, ONE].into())?; - storage_patch.set_map_item( - map_slot_name, - StorageMapKey::new([ONE, ZERO, ZERO, ZERO].into()), - [ONE, ONE, ONE, ONE].into(), - )?; + let mut map_entries = StorageMapPatchEntries::new(); + map_entries + .insert(StorageMapKey::new([ONE, ZERO, ZERO, ZERO].into()), [ONE, ONE, ONE, ONE].into()); + let storage_patch = AccountStoragePatch::from_entries([ + ( + value_slot_name, + StorageSlotPatch::Value(StorageValuePatch::Update { + value: [ZERO, ZERO, ZERO, ONE].into(), + }), + ), + ( + map_slot_name, + StorageSlotPatch::Map(StorageMapPatch::Update { entries: map_entries }), + ), + ])?; // The account starts with an empty vault, so the absolute value of the added asset is the // asset itself. @@ -2128,10 +2174,10 @@ async fn insert_account_with_storage_for_snapshot_test() let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(dummy_component) .build_existing()?; @@ -2244,10 +2290,10 @@ async fn watched_status_survives_state_replacement() -> anyhow::Result<()> { let account = AccountBuilder::new([0; 32]) .account_type(AccountType::Private) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(AccountComponent::new( BasicWallet::code().as_library().clone(), vec![], diff --git a/crates/testing/miden-client-tests/src/tests.rs b/crates/testing/miden-client-tests/src/tests.rs index ccfeeb3909..1e1a436cd5 100644 --- a/crates/testing/miden-client-tests/src/tests.rs +++ b/crates/testing/miden-client-tests/src/tests.rs @@ -92,7 +92,6 @@ use miden_protocol::note::{ Note, NoteAssets, NoteAttachments, - NoteFile, NoteRecipient, NoteStorage, NoteTag, @@ -113,11 +112,14 @@ use miden_protocol::transaction::RawOutputNote; use miden_protocol::vm::AdviceInputs; use miden_protocol::{EMPTY_WORD, Felt, ONE, Word}; use miden_standards::account::AccountBuilderSchemaCommitmentExt; +use miden_standards::account::auth::Approver; use miden_standards::account::faucets::{FungibleFaucet, TokenName}; use miden_standards::account::policies::{BurnPolicy, MintPolicy, TokenPolicyManager}; use miden_standards::account::wallets::BasicWallet; use miden_standards::note::{ NoteConsumptionStatus, + NoteFile, + NoteSyncHint, P2idNote, P2idNoteStorage, PswapNote, @@ -167,10 +169,10 @@ async fn input_notes_round_trip() { // insert notes into database for note in &available_notes { client - .import_notes(&[NoteFile::NoteWithProof( - note.note().unwrap().clone(), - note.inclusion_proof().clone(), - )]) + .import_notes(&[NoteFile::Committed { + note: note.note().unwrap().clone(), + proof: note.inclusion_proof().clone(), + }]) .await .unwrap(); } @@ -203,10 +205,9 @@ async fn get_input_note() { // insert Note into database let note: InputNoteRecord = original_note.clone().into(); client - .import_notes(&[NoteFile::NoteDetails { - details: note.into(), - tag: None, - after_block_num: 0.into(), + .import_notes(&[NoteFile::ExpectedNote { + details: note.clone().into(), + sync_hint: NoteSyncHint::new(0.into(), note.metadata().unwrap().tag()), }]) .await .unwrap(); @@ -327,7 +328,10 @@ async fn insert_same_account_twice_fails() { let account = Account::mock( ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, - AuthSingleSig::new(PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2), + AuthSingleSig::new(Approver::new( + PublicKeyCommitment::from(EMPTY_WORD), + AuthSchemeId::Falcon512Poseidon2, + )), ); assert!(client.add_account(&account, false).await.is_ok()); @@ -341,7 +345,10 @@ async fn account_code() { let account = Account::mock( ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE, - AuthSingleSig::new(PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2), + AuthSingleSig::new(Approver::new( + PublicKeyCommitment::from(EMPTY_WORD), + AuthSchemeId::Falcon512Poseidon2, + )), ); let account_code = account.code(); @@ -363,7 +370,10 @@ async fn get_account_by_id() { let account = Account::mock( ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE, - AuthSingleSig::new(PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2), + AuthSingleSig::new(Approver::new( + PublicKeyCommitment::from(EMPTY_WORD), + AuthSchemeId::Falcon512Poseidon2, + )), ); client.add_account(&account, false).await.unwrap(); @@ -395,10 +405,9 @@ async fn sync_state() { for note in &expected_notes { client - .import_notes(&[NoteFile::NoteDetails { + .import_notes(&[NoteFile::ExpectedNote { details: note.clone().into(), - after_block_num: 0.into(), - tag: Some(note.metadata().tag()), + sync_hint: NoteSyncHint::new(0.into(), note.metadata().tag()), }]) .await .unwrap(); @@ -444,10 +453,9 @@ async fn sync_state_mmr() { for note in ¬es { client - .import_notes(&[NoteFile::NoteDetails { + .import_notes(&[NoteFile::ExpectedNote { details: note.clone().into(), - after_block_num: 0.into(), - tag: Some(note.metadata().tag()), + sync_hint: NoteSyncHint::new(0.into(), note.metadata().tag()), }]) .await .unwrap(); @@ -542,10 +550,9 @@ async fn stale_cached_partial_mmr_is_rebuilt_from_store() { .collect(); for note in ¬es { client - .import_notes(&[NoteFile::NoteDetails { + .import_notes(&[NoteFile::ExpectedNote { details: note.clone().into(), - after_block_num: 0.into(), - tag: Some(note.metadata().tag()), + sync_hint: NoteSyncHint::new(0.into(), note.metadata().tag()), }]) .await .unwrap(); @@ -882,18 +889,17 @@ async fn import_note_validation() { let consumed_note = consumed_note.expect("expected to find at least one consumed note"); client - .import_notes(&[NoteFile::NoteWithProof( - consumed_note.note().unwrap().clone(), - consumed_note.inclusion_proof().clone(), - )]) + .import_notes(&[NoteFile::Committed { + note: consumed_note.note().unwrap().clone(), + proof: consumed_note.inclusion_proof().clone(), + }]) .await .unwrap(); client - .import_notes(&[NoteFile::NoteDetails { + .import_notes(&[NoteFile::ExpectedNote { details: expected_note.note().unwrap().into(), - after_block_num: 0.into(), - tag: None, + sync_hint: NoteSyncHint::new(0.into(), expected_note.note().unwrap().metadata().tag()), }]) .await .unwrap(); @@ -1379,10 +1385,9 @@ async fn input_note_reader_finds_externally_consumed_notes() { // Import the P2ID note as an input note so the client tracks it. The tag lets the import // resolve the note's on-chain commitment (and thus its metadata) so it can later be matched // when the external consumer's nullifier appears during sync. - let note_file = NoteFile::NoteDetails { + let note_file = NoteFile::ExpectedNote { details: p2id_note.into(), - after_block_num: BlockNumber::from(0u32), - tag: Some(p2id_tag), + sync_hint: NoteSyncHint::new(BlockNumber::from(0u32), p2id_tag), }; client.import_notes(&[note_file]).await.unwrap(); @@ -3604,10 +3609,10 @@ async fn empty_storage_map() { let account = AccountBuilder::new(init_seed) .account_type(AccountType::Public) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( pub_key.to_commitment(), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(BasicWallet) .with_component(component) .build_with_schema_commitment() @@ -3719,10 +3724,10 @@ async fn storage_and_vault_proofs() { let account = AccountBuilder::new(init_seed) .account_type(AccountType::Public) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( pub_key.to_commitment(), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(BasicWallet) .with_component(bump_item_component) .build_with_schema_commitment() @@ -3815,7 +3820,10 @@ async fn account_addresses_basic_wallet() { let account = Account::mock( ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, - AuthSingleSig::new(PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2), + AuthSingleSig::new(Approver::new( + PublicKeyCommitment::from(EMPTY_WORD), + AuthSchemeId::Falcon512Poseidon2, + )), ); client.add_account(&account, false).await.unwrap(); @@ -3855,7 +3863,10 @@ async fn account_add_address_after_creation() { let account = Account::mock( ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2, - AuthSingleSig::new(PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2), + AuthSingleSig::new(Approver::new( + PublicKeyCommitment::from(EMPTY_WORD), + AuthSchemeId::Falcon512Poseidon2, + )), ); client.add_account(&account, false).await.unwrap(); @@ -4221,10 +4232,9 @@ async fn sync_stores_private_note_attachments() { let note_tag = private_note.metadata().tag(); client.add_note_tag(note_tag).await.unwrap(); client - .import_notes(&[NoteFile::NoteDetails { + .import_notes(&[NoteFile::ExpectedNote { details: private_note.clone().into(), - after_block_num: BlockNumber::from(0u32), - tag: Some(note_tag), + sync_hint: NoteSyncHint::new(BlockNumber::from(0u32), note_tag), }]) .await .unwrap(); @@ -4581,10 +4591,10 @@ async fn insert_new_wallet( let account = AccountBuilder::new(init_seed) .account_type(visibility) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( pub_key.to_commitment(), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(BasicWallet) .build_with_schema_commitment() .unwrap(); @@ -4609,10 +4619,10 @@ async fn insert_new_ecdsa_wallet( let account = AccountBuilder::new(init_seed) .account_type(visibility) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( pub_key.to_commitment(), AuthSchemeId::EcdsaK256Keccak, - )) + ))) .with_component(BasicWallet) .build_with_schema_commitment() .unwrap(); @@ -4655,10 +4665,10 @@ async fn insert_new_fungible_faucet( let account = AccountBuilder::new(init_seed) .account_type(visibility) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( pub_key.to_commitment(), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(faucet) .with_components(policy_manager) .build_with_schema_commitment() @@ -4704,10 +4714,10 @@ async fn insert_new_ecdsa_fungible_faucet( let account = AccountBuilder::new(init_seed) .account_type(visibility) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( pub_key.to_commitment(), AuthSchemeId::EcdsaK256Keccak, - )) + ))) .with_component(faucet) .with_components(policy_manager) .build_with_schema_commitment() @@ -4775,10 +4785,10 @@ async fn storage_and_vault_proofs_ecdsa() { let account = AccountBuilder::new(init_seed) .account_type(AccountType::Public) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( pub_key.to_commitment(), AuthSchemeId::EcdsaK256Keccak, - )) + ))) .with_component(BasicWallet) .with_component(bump_item_component) .build_with_schema_commitment() @@ -4872,8 +4882,10 @@ async fn execute_transaction_fails_for_watched_account() { // public `add_account`/`import_watched_account_by_id` paths so we don't need a mock RPC // round-trip. let key_pair = AuthSecretKey::new_falcon512_poseidon2(); - let auth_component = - AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2); + let auth_component = AuthSingleSig::new(Approver::new( + key_pair.public_key().to_commitment(), + AuthSchemeId::Falcon512Poseidon2, + )); let mut init_seed = [0u8; 32]; client.rng().fill_bytes(&mut init_seed); diff --git a/crates/testing/miden-client-tests/src/tests/store.rs b/crates/testing/miden-client-tests/src/tests/store.rs index 7d67ceb3c0..621bdfd81f 100644 --- a/crates/testing/miden-client-tests/src/tests/store.rs +++ b/crates/testing/miden-client-tests/src/tests/store.rs @@ -28,6 +28,7 @@ use miden_protocol::testing::account_id::{ use miden_protocol::transaction::TransactionKernel; use miden_protocol::{EMPTY_WORD, Felt, Word, ZERO}; use miden_standards::account::AccountBuilderSchemaCommitmentExt; +use miden_standards::account::auth::Approver; use miden_standards::account::wallets::BasicWallet; use miden_standards::testing::mock_account::MockAccountExt; use rand::RngCore; @@ -37,7 +38,10 @@ use crate::tests::{create_test_client, insert_new_fungible_faucet, insert_new_wa fn create_account_data(account_id: u128) -> AccountFile { let account = Account::mock( account_id, - AuthSingleSig::new(PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2), + AuthSingleSig::new(Approver::new( + PublicKeyCommitment::from(EMPTY_WORD), + AuthSchemeId::Falcon512Poseidon2, + )), ); AccountFile::new(account.clone(), vec![AuthSecretKey::new_falcon512_poseidon2()]) @@ -46,7 +50,10 @@ fn create_account_data(account_id: u128) -> AccountFile { fn create_ecdsa_account_data(account_id: u128) -> AccountFile { let account = Account::mock( account_id, - AuthSingleSig::new(PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::EcdsaK256Keccak), + AuthSingleSig::new(Approver::new( + PublicKeyCommitment::from(EMPTY_WORD), + AuthSchemeId::EcdsaK256Keccak, + )), ); AccountFile::new(account.clone(), vec![AuthSecretKey::new_falcon512_poseidon2()]) @@ -81,7 +88,10 @@ pub async fn try_add_account() { let account = Account::mock( ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, - AuthSingleSig::new(PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::Falcon512Poseidon2), + AuthSingleSig::new(Approver::new( + PublicKeyCommitment::from(EMPTY_WORD), + AuthSchemeId::Falcon512Poseidon2, + )), ); // The mock account has nonce 1, we need it to be 0 for the test. @@ -103,7 +113,10 @@ pub async fn try_add_ecdsa_account() { let account = Account::mock( ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, - AuthSingleSig::new(PublicKeyCommitment::from(EMPTY_WORD), AuthSchemeId::EcdsaK256Keccak), + AuthSingleSig::new(Approver::new( + PublicKeyCommitment::from(EMPTY_WORD), + AuthSchemeId::EcdsaK256Keccak, + )), ); // The mock account has nonce 1, we need it to be 0 for the test. @@ -315,10 +328,10 @@ async fn build_three_slot_account( let account = AccountBuilder::new(init_seed) .account_type(AccountType::Public) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( pub_key.to_commitment(), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_component(BasicWallet) .with_component(component) .build_with_schema_commitment() diff --git a/crates/testing/miden-client-tests/src/tests/transaction.rs b/crates/testing/miden-client-tests/src/tests/transaction.rs index da5b1758b7..26f5a10f6b 100644 --- a/crates/testing/miden-client-tests/src/tests/transaction.rs +++ b/crates/testing/miden-client-tests/src/tests/transaction.rs @@ -36,6 +36,7 @@ use miden_protocol::testing::account_id::{ }; use miden_protocol::{Felt, Word}; use miden_standards::account::AccountBuilderSchemaCommitmentExt; +use miden_standards::account::auth::Approver; use miden_standards::account::wallets::BasicWallet; use super::PaymentNoteDescription; @@ -58,10 +59,10 @@ async fn transaction_creates_two_notes() { let account = AccountBuilder::new(Default::default()) .with_component(BasicWallet) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( pub_key.to_commitment(), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .with_assets([asset_1, asset_2]) .build_existing() .unwrap(); @@ -326,10 +327,10 @@ async fn lazy_foreign_account_loading() { let foreign_account = AccountBuilder::new(Default::default()) .account_type(AccountType::Public) .with_component(fpi_component) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( secret_key.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2, - )) + ))) .build_with_schema_commitment() .unwrap(); let foreign_account_id = foreign_account.id(); diff --git a/crates/testing/test-node-genesis/src/agglayer.rs b/crates/testing/test-node-genesis/src/agglayer.rs index 3261b9522d..e0994f64b0 100644 --- a/crates/testing/test-node-genesis/src/agglayer.rs +++ b/crates/testing/test-node-genesis/src/agglayer.rs @@ -14,7 +14,7 @@ use miden_protocol::account::{ AccountType, }; use miden_protocol::{Felt, ONE, Word}; -use miden_standards::account::auth::AuthSingleSig; +use miden_standards::account::auth::{Approver, AuthSingleSig}; use miden_standards::account::wallets::BasicWallet; use rand_chacha::ChaCha20Rng; use rand_chacha::rand_core::SeedableRng; @@ -102,10 +102,10 @@ fn build_wallet_account(rng: &mut ChaCha20Rng, secret: &AuthSecretKey) -> Result .context("failed to create wallet component")?; let account = AccountBuilder::new(seed) - .with_auth_component(AuthSingleSig::new( + .with_auth_component(AuthSingleSig::new(Approver::new( secret.public_key().to_commitment(), AuthScheme::Falcon512Poseidon2, - )) + ))) .with_component(acc_component) .account_type(AccountType::Public) .build() diff --git a/crates/testing/test-node-genesis/src/lib.rs b/crates/testing/test-node-genesis/src/lib.rs index bc59d1c26b..18ea25b41c 100644 --- a/crates/testing/test-node-genesis/src/lib.rs +++ b/crates/testing/test-node-genesis/src/lib.rs @@ -23,8 +23,12 @@ use miden_protocol::account::{ }; use miden_protocol::asset::{Asset, AssetAmount, FungibleAsset, TokenSymbol}; use miden_protocol::{ONE, Word}; -use miden_standards::account::auth::AuthSingleSig; -use miden_standards::account::faucets::{FungibleFaucet, TokenName, create_user_fungible_faucet}; +use miden_standards::account::auth::{Approver, AuthSingleSig}; +use miden_standards::account::faucets::{ + FungibleFaucet, + TokenName, + create_singlesig_user_fungible_faucet, +}; use miden_standards::account::policies::{BurnPolicy, MintPolicy, TokenPolicyManager}; use miden_standards::account::wallets::BasicWallet; use miden_standards::testing::faucet::user_faucet_single_sig_acl; @@ -132,7 +136,7 @@ fn generate_genesis_account() -> anyhow::Result { .decimals(12) .max_supply(AssetAmount::new(1_000_000_000_000).unwrap()) .build()?; - let account = create_user_fungible_faucet( + let account = create_singlesig_user_fungible_faucet( rng.random(), faucet, auth_component, @@ -224,7 +228,7 @@ fn create_single_test_faucet(index: u128, secret: &AuthSecretKey) -> anyhow::Res .decimals(FAUCET_DECIMALS) .max_supply(AssetAmount::new(u64::from(FAUCET_MAX_SUPPLY)).unwrap()) .build()?; - let faucet = create_user_fungible_faucet( + let faucet = create_singlesig_user_fungible_faucet( init_seed, faucet_component, auth_component, @@ -260,10 +264,10 @@ fn create_test_account_with_many_assets(faucets: &[Account]) -> anyhow::Result Date: Tue, 30 Jun 2026 19:13:43 -0300 Subject: [PATCH 04/20] chore: bump node version --- Cargo.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 915e73ebd4..3219f0245d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1101,7 +1101,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2500,7 +2500,7 @@ dependencies = [ [[package]] name = "miden-node-proto-build" version = "0.15.0" -source = "git+https://github.com/0xMiden/miden-node.git?branch=jmunoz-update-miden-protocol#b77301f76d59d3600a8a7f194d9454f1bdffe560" +source = "git+https://github.com/0xMiden/miden-node.git?branch=jmunoz-update-miden-protocol#1f529567caf175b6990c1aab2e571738c5449ee6" dependencies = [ "build-rs", "codegen", @@ -2576,7 +2576,7 @@ dependencies = [ [[package]] name = "miden-protocol" version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#36a1d092d4e8cc3dc1e276d8518eae7b7baa70c5" dependencies = [ "bech32", "fs-err", @@ -2620,7 +2620,7 @@ dependencies = [ [[package]] name = "miden-remote-prover-client" version = "0.15.0" -source = "git+https://github.com/0xMiden/miden-node.git?branch=jmunoz-update-miden-protocol#b77301f76d59d3600a8a7f194d9454f1bdffe560" +source = "git+https://github.com/0xMiden/miden-node.git?branch=jmunoz-update-miden-protocol#1f529567caf175b6990c1aab2e571738c5449ee6" dependencies = [ "build-rs", "fs-err", @@ -2651,7 +2651,7 @@ dependencies = [ [[package]] name = "miden-standards" version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#36a1d092d4e8cc3dc1e276d8518eae7b7baa70c5" dependencies = [ "bon", "fs-err", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "miden-tx" version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#36a1d092d4e8cc3dc1e276d8518eae7b7baa70c5" dependencies = [ "miden-processor", "miden-protocol", @@ -4003,7 +4003,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4510,7 +4510,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5323,7 +5323,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] From 31e54e89d51a520019dae494342c6c46b4fc4b2d Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Wed, 1 Jul 2026 15:01:58 -0300 Subject: [PATCH 05/20] chore: bump miden deps --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3219f0245d..4311319fa9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2576,7 +2576,7 @@ dependencies = [ [[package]] name = "miden-protocol" version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#36a1d092d4e8cc3dc1e276d8518eae7b7baa70c5" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" dependencies = [ "bech32", "fs-err", @@ -2651,7 +2651,7 @@ dependencies = [ [[package]] name = "miden-standards" version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#36a1d092d4e8cc3dc1e276d8518eae7b7baa70c5" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" dependencies = [ "bon", "fs-err", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "miden-tx" version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#36a1d092d4e8cc3dc1e276d8518eae7b7baa70c5" +source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" dependencies = [ "miden-processor", "miden-protocol", From 8d26ffc8b4f9ad1d3648aa2c563acf4130d0f31d Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Wed, 1 Jul 2026 16:21:57 -0300 Subject: [PATCH 06/20] ci: switch ci runner to warp --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0cc094e1bb..25543e21f6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,7 +47,7 @@ jobs: build-wasm: name: Build wasm32 (no_std) - runs-on: ubuntu-latest + runs-on: warp-ubuntu-latest-x64-8x steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: From 7730735868809581b40e5689bfded424ee570d89 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 7 Jul 2026 18:45:40 -0300 Subject: [PATCH 07/20] chore: switch back to protocol next branch --- Cargo.lock | 38 +---- Cargo.toml | 5 +- crates/rust-client/Cargo.toml | 23 ++- crates/rust-client/build.rs | 43 ++++- crates/rust-client/src/grpc_support/mod.rs | 1 + crates/rust-client/src/lib.rs | 5 +- .../src/remote_prover/generated.rs | 17 ++ crates/rust-client/src/remote_prover/mod.rs | 58 +++++++ .../src/remote_prover/tx_prover.rs | 148 ++++++++++++++++++ crates/rust-client/src/transaction/prover.rs | 5 +- 10 files changed, 292 insertions(+), 51 deletions(-) create mode 100644 crates/rust-client/src/remote_prover/generated.rs create mode 100644 crates/rust-client/src/remote_prover/mod.rs create mode 100644 crates/rust-client/src/remote_prover/tx_prover.rs diff --git a/Cargo.lock b/Cargo.lock index 4311319fa9..5818c3e859 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1101,7 +1101,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1379,10 +1379,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi 6.0.0", - "wasm-bindgen", ] [[package]] @@ -2092,7 +2090,6 @@ dependencies = [ "miden-note-transport-proto-build", "miden-processor", "miden-protocol", - "miden-remote-prover-client", "miden-standards", "miden-testing", "miden-tx", @@ -2499,8 +2496,8 @@ dependencies = [ [[package]] name = "miden-node-proto-build" -version = "0.15.0" -source = "git+https://github.com/0xMiden/miden-node.git?branch=jmunoz-update-miden-protocol#1f529567caf175b6990c1aab2e571738c5449ee6" +version = "0.15.1" +source = "git+https://github.com/0xMiden/miden-node.git?branch=next#ad3b1f8cdc8a8120ffb6419b42c3fcd18811475c" dependencies = [ "build-rs", "codegen", @@ -2617,27 +2614,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "miden-remote-prover-client" -version = "0.15.0" -source = "git+https://github.com/0xMiden/miden-node.git?branch=jmunoz-update-miden-protocol#1f529567caf175b6990c1aab2e571738c5449ee6" -dependencies = [ - "build-rs", - "fs-err", - "getrandom 0.4.3", - "miden-node-proto-build", - "miden-protocol", - "miden-tx", - "miette", - "prost", - "thiserror", - "tokio", - "tonic", - "tonic-prost", - "tonic-prost-build", - "tonic-web-wasm-client", -] - [[package]] name = "miden-serde-utils" version = "0.25.1" @@ -4003,7 +3979,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4507,10 +4483,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.3", + "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5323,7 +5299,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4c4dad4219..e50774289b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,11 +42,8 @@ miden-tx = { branch = "next", default-features = false, git = "https://gi miden-tx-batch = { branch = "next", default-features = false, git = "https://github.com/0xMiden/protocol.git" } # Miden node dependencies -miden-node-proto-build = { branch = "jmunoz-update-miden-protocol", default-features = false, git = "https://github.com/0xMiden/miden-node.git" } +miden-node-proto-build = { branch = "next", default-features = false, git = "https://github.com/0xMiden/miden-node.git" } miden-note-transport-proto-build = { default-features = false, version = "0.4.1" } -miden-remote-prover-client = { branch = "jmunoz-update-miden-protocol", default-features = false, features = [ - "tx-prover", -], git = "https://github.com/0xMiden/miden-node.git" } # Miden debug dependency miden-debug = { default-features = false, features = ["dap", "std"], version = "0.8" } diff --git a/crates/rust-client/Cargo.toml b/crates/rust-client/Cargo.toml index a15b989879..958b4d0a0d 100644 --- a/crates/rust-client/Cargo.toml +++ b/crates/rust-client/Cargo.toml @@ -56,8 +56,7 @@ std = [ "dep:tokio", "miden-agglayer/std", "miden-protocol/std", - "miden-remote-prover-client/std", - "miden-remote-prover-client/tx-prover", + "miden-tx/std", "tonic/tls-native-roots", "tonic/tls-ring", "tonic/transport", @@ -74,15 +73,14 @@ tonic = [] [dependencies] # Miden dependencies -miden-agglayer = { workspace = true } -miden-debug = { optional = true, workspace = true } -miden-processor = { optional = true, workspace = true } -miden-protocol = { workspace = true } -miden-remote-prover-client = { default-features = false, features = ["tx-prover"], workspace = true } -miden-standards = { workspace = true } -miden-testing = { optional = true, workspace = true } -miden-tx = { workspace = true } -miden-tx-batch = { workspace = true } +miden-agglayer = { workspace = true } +miden-debug = { optional = true, workspace = true } +miden-processor = { optional = true, workspace = true } +miden-protocol = { workspace = true } +miden-standards = { workspace = true } +miden-testing = { optional = true, workspace = true } +miden-tx = { workspace = true } +miden-tx-batch = { workspace = true } # External dependencies anyhow = { workspace = true } @@ -97,7 +95,7 @@ serde = { workspace = true } serde_json = { workspace = true } tempfile = { optional = true, workspace = true } thiserror = { workspace = true } -tokio = { features = ["time"], optional = true, workspace = true } +tokio = { features = ["sync", "time"], optional = true, workspace = true } tonic = { features = ["codegen"], workspace = true } tonic-health = { version = "0.14" } tonic-prost = { version = "0.14" } @@ -107,6 +105,7 @@ uuid = { features = ["js", "serde", "v4"], optional = true, workspace = [target.'cfg(target_arch = "wasm32")'.dependencies] getrandom = { features = ["wasm_js"], version = "0.3" } gloo-timers = { features = ["futures"], version = "0.3" } +tokio = { default-features = false, features = ["sync"], version = "1.48" } tonic-web-wasm-client = { default-features = false, version = "0.9" } [build-dependencies] diff --git a/crates/rust-client/build.rs b/crates/rust-client/build.rs index c46b85ee52..1e27382ed9 100644 --- a/crates/rust-client/build.rs +++ b/crates/rust-client/build.rs @@ -1,7 +1,7 @@ use std::fs; use std::path::{Path, PathBuf}; -use miden_node_proto_build::rpc_api_descriptor; +use miden_node_proto_build::{remote_prover_api_descriptor, rpc_api_descriptor}; use miden_note_transport_proto_build::mnt_api_descriptor; use miette::IntoDiagnostic; @@ -9,11 +9,15 @@ const RPC_STD_DIR: &str = "rpc/std"; const RPC_NOSTD_DIR: &str = "rpc/nostd"; const NOTE_TRANSPORT_STD_DIR: &str = "note_transport/std"; const NOTE_TRANSPORT_NOSTD_DIR: &str = "note_transport/nostd"; +const REMOTE_PROVER_STD_DIR: &str = "remote_prover/std"; +const REMOTE_PROVER_NOSTD_DIR: &str = "remote_prover/nostd"; const RPC_STD_WRAPPER: &str = "rpc_std.rs"; const RPC_NOSTD_WRAPPER: &str = "rpc_nostd.rs"; const NOTE_TRANSPORT_STD_WRAPPER: &str = "note_transport_std.rs"; const NOTE_TRANSPORT_NOSTD_WRAPPER: &str = "note_transport_nostd.rs"; +const REMOTE_PROVER_STD_WRAPPER: &str = "remote_prover_std.rs"; +const REMOTE_PROVER_NOSTD_WRAPPER: &str = "remote_prover_nostd.rs"; fn main() -> miette::Result<()> { // Proto definitions come from build-dependency crates. Cargo automatically re-runs this @@ -26,14 +30,48 @@ fn main() -> miette::Result<()> { compile_tonic_client_proto(&out_dir)?; compile_tonic_note_transport_proto(&out_dir)?; + compile_tonic_remote_prover_proto(&out_dir)?; replace_no_std_types_in_dir(&out_dir.join(RPC_NOSTD_DIR))?; replace_no_std_types_in_dir(&out_dir.join(NOTE_TRANSPORT_NOSTD_DIR))?; + replace_no_std_types_in_dir(&out_dir.join(REMOTE_PROVER_NOSTD_DIR))?; generate_wrapper(&out_dir, RPC_STD_DIR, RPC_STD_WRAPPER)?; generate_wrapper(&out_dir, RPC_NOSTD_DIR, RPC_NOSTD_WRAPPER)?; generate_wrapper(&out_dir, NOTE_TRANSPORT_STD_DIR, NOTE_TRANSPORT_STD_WRAPPER)?; generate_wrapper(&out_dir, NOTE_TRANSPORT_NOSTD_DIR, NOTE_TRANSPORT_NOSTD_WRAPPER)?; + generate_wrapper(&out_dir, REMOTE_PROVER_STD_DIR, REMOTE_PROVER_STD_WRAPPER)?; + generate_wrapper(&out_dir, REMOTE_PROVER_NOSTD_DIR, REMOTE_PROVER_NOSTD_WRAPPER)?; + + Ok(()) +} + +// REMOTE PROVER CLIENT PROTO CODEGEN +// =============================================================================================== + +/// Generates the Rust protobuf bindings for the remote prover client. +fn compile_tonic_remote_prover_proto(out_dir: &Path) -> miette::Result<()> { + let file_descriptors = remote_prover_api_descriptor(); + + let std_out = out_dir.join(REMOTE_PROVER_STD_DIR); + let nostd_out = out_dir.join(REMOTE_PROVER_NOSTD_DIR); + fs::create_dir_all(&std_out).into_diagnostic()?; + fs::create_dir_all(&nostd_out).into_diagnostic()?; + + // The `nostd` bindings target `wasm32`, where the transport is provided by + // `tonic-web-wasm-client`, so tonic's own transport codegen must be disabled. + tonic_prost_build::configure() + .build_transport(false) + .build_server(false) + .out_dir(&nostd_out) + .compile_fds_with_config(file_descriptors.clone(), tonic_prost_build::Config::new()) + .into_diagnostic()?; + + tonic_prost_build::configure() + .build_server(false) + .out_dir(&std_out) + .compile_fds_with_config(file_descriptors, tonic_prost_build::Config::new()) + .into_diagnostic()?; Ok(()) } @@ -171,7 +209,8 @@ fn replace_no_std_types_in_dir(dir: &Path) -> miette::Result<()> { let content = fs::read_to_string(&path).into_diagnostic()?; let replaced = content .replace("std::result", "core::result") - .replace("std::marker", "core::marker"); + .replace("std::marker", "core::marker") + .replace("format!", "alloc::format!"); fs::write(&path, replaced).into_diagnostic()?; } } diff --git a/crates/rust-client/src/grpc_support/mod.rs b/crates/rust-client/src/grpc_support/mod.rs index 87de283638..f2a97a1294 100644 --- a/crates/rust-client/src/grpc_support/mod.rs +++ b/crates/rust-client/src/grpc_support/mod.rs @@ -1,5 +1,6 @@ use alloc::string::String; +#[cfg(any(feature = "std", target_arch = "wasm32"))] pub use crate::RemoteTransactionProver; /// Default remote prover endpoint for testnet. diff --git a/crates/rust-client/src/lib.rs b/crates/rust-client/src/lib.rs index 09d137dda7..f7a5c1c118 100644 --- a/crates/rust-client/src/lib.rs +++ b/crates/rust-client/src/lib.rs @@ -124,6 +124,8 @@ pub mod keystore; pub mod note; pub mod note_transport; pub mod pswap; +#[cfg(any(feature = "std", target_arch = "wasm32"))] +pub mod remote_prover; pub mod rpc; pub mod settings; pub mod store; @@ -323,8 +325,9 @@ pub use miden_protocol::{ Word, ZERO, }; -pub use miden_remote_prover_client::RemoteTransactionProver; pub use miden_tx::ExecutionOptions; +#[cfg(any(feature = "std", target_arch = "wasm32"))] +pub use remote_prover::RemoteTransactionProver; /// Provides test utilities for working with accounts and account IDs /// within the Miden network. This module is only available when the `testing` feature is diff --git a/crates/rust-client/src/remote_prover/generated.rs b/crates/rust-client/src/remote_prover/generated.rs new file mode 100644 index 0000000000..b716cb3a6f --- /dev/null +++ b/crates/rust-client/src/remote_prover/generated.rs @@ -0,0 +1,17 @@ +#[cfg(feature = "std")] +#[rustfmt::skip] +#[allow(dead_code)] +mod std_gen { + include!(concat!(env!("OUT_DIR"), "/remote_prover_std.rs")); +} +#[cfg(feature = "std")] +pub use std_gen::remote_prover::*; + +#[cfg(not(feature = "std"))] +#[rustfmt::skip] +#[allow(dead_code)] +mod nostd_gen { + include!(concat!(env!("OUT_DIR"), "/remote_prover_nostd.rs")); +} +#[cfg(not(feature = "std"))] +pub use nostd_gen::remote_prover::*; diff --git a/crates/rust-client/src/remote_prover/mod.rs b/crates/rust-client/src/remote_prover/mod.rs new file mode 100644 index 0000000000..44cbcb14e9 --- /dev/null +++ b/crates/rust-client/src/remote_prover/mod.rs @@ -0,0 +1,58 @@ +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use core::error::Error as CoreError; + +use thiserror::Error; + +mod generated; +mod tx_prover; + +pub use tx_prover::RemoteTransactionProver; + +// ERRORS +// ================================================================================================ + +/// Errors that can occur when communicating with a remote prover. +#[derive(Debug, Error)] +pub enum RemoteProverClientError { + /// Indicates that the provided gRPC server endpoint is invalid. + #[error("invalid uri {0}")] + InvalidEndpoint(String), + /// Indicates that the connection to the server failed. + #[error("failed to connect to prover {0}")] + ConnectionFailed(#[source] Box), + /// Custom error variant for errors not covered by the other variants. + #[error("{error_msg}")] + Other { + error_msg: Box, + source: Option>, + }, +} + +impl From for String { + fn from(err: RemoteProverClientError) -> Self { + err.to_string() + } +} + +impl RemoteProverClientError { + /// Creates a custom error using the [`RemoteProverClientError::Other`] variant from an error + /// message. + pub fn other(message: impl Into) -> Self { + let message: String = message.into(); + Self::Other { error_msg: message.into(), source: None } + } + + /// Creates a custom error using the [`RemoteProverClientError::Other`] variant from an error + /// message and a source error. + pub fn other_with_source( + message: impl Into, + source: impl CoreError + Send + Sync + 'static, + ) -> Self { + let message: String = message.into(); + Self::Other { + error_msg: message.into(), + source: Some(Box::new(source)), + } + } +} diff --git a/crates/rust-client/src/remote_prover/tx_prover.rs b/crates/rust-client/src/remote_prover/tx_prover.rs new file mode 100644 index 0000000000..3b7fa32561 --- /dev/null +++ b/crates/rust-client/src/remote_prover/tx_prover.rs @@ -0,0 +1,148 @@ +use alloc::string::String; +use alloc::sync::Arc; +use core::time::Duration; + +use miden_protocol::transaction::{ProvenTransaction, TransactionInputs}; +use miden_protocol::utils::serde::{Deserializable, DeserializationError, Serializable}; +use miden_protocol::vm::FutureMaybeSend; +use miden_tx::TransactionProverError; +use tokio::sync::Mutex; + +use super::generated::api_client::ApiClient; +use super::{RemoteProverClientError, generated as proto}; + +// REMOTE TRANSACTION PROVER +// ================================================================================================ + +/// A [`RemoteTransactionProver`] is a transaction prover that sends witness data to a remote +/// gRPC server and receives a proven transaction. +/// +/// When compiled for the `wasm32-unknown-unknown` target, it uses the `tonic_web_wasm_client` +/// transport. Otherwise, it uses the built-in `tonic::transport` for native platforms. +/// +/// The transport layer connection is established lazily when the first transaction is proven. +#[derive(Clone)] +pub struct RemoteTransactionProver { + #[cfg(target_arch = "wasm32")] + client: Arc>>>, + + #[cfg(not(target_arch = "wasm32"))] + client: Arc>>>, + + endpoint: String, + timeout: Duration, +} + +impl RemoteTransactionProver { + /// Creates a new [`RemoteTransactionProver`] with the specified gRPC server endpoint. The + /// endpoint should be in the format `{protocol}://{hostname}:{port}`. + pub fn new(endpoint: impl Into) -> Self { + RemoteTransactionProver { + endpoint: endpoint.into(), + client: Arc::new(Mutex::new(None)), + timeout: Duration::from_secs(10), + } + } + + /// Configures the timeout for requests to the remote prover server. + #[must_use] + pub fn with_timeout(mut self, timeout: Duration) -> Self { + self.timeout = timeout; + self + } + + /// Establishes a connection to the remote transaction prover server. The connection is + /// maintained for the lifetime of the prover. If the connection is already established, this + /// method does nothing. + async fn connect(&self) -> Result<(), RemoteProverClientError> { + let mut client = self.client.lock().await; + if client.is_some() { + return Ok(()); + } + + #[cfg(target_arch = "wasm32")] + let new_client = { + let fetch_options = + tonic_web_wasm_client::options::FetchOptions::new().timeout(self.timeout); + let web_client = tonic_web_wasm_client::Client::new_with_options( + self.endpoint.clone(), + fetch_options, + ); + ApiClient::new(web_client) + }; + + #[cfg(not(target_arch = "wasm32"))] + let new_client = { + let endpoint = tonic::transport::Endpoint::try_from(self.endpoint.clone()) + .map_err(|err| RemoteProverClientError::ConnectionFailed(err.into()))? + .timeout(self.timeout); + let channel = endpoint + .tls_config(tonic::transport::ClientTlsConfig::new().with_native_roots()) + .map_err(|err| RemoteProverClientError::ConnectionFailed(err.into()))? + .connect() + .await + .map_err(|err| RemoteProverClientError::ConnectionFailed(err.into()))?; + ApiClient::new(channel) + }; + + *client = Some(new_client); + + Ok(()) + } + + /// Proves the given transaction inputs on the remote prover, returning the resulting + /// [`ProvenTransaction`]. + pub fn prove( + &self, + tx_inputs: &TransactionInputs, + ) -> impl FutureMaybeSend> { + async move { + self.connect().await.map_err(|err| { + TransactionProverError::other_with_source( + "failed to connect to the remote prover", + err, + ) + })?; + + let mut client = self + .client + .lock() + .await + .as_ref() + .ok_or_else(|| TransactionProverError::other("client should be connected"))? + .clone(); + + let request = tonic::Request::new(tx_inputs.into()); + + let response = client.prove(request).await.map_err(|err| { + TransactionProverError::other_with_source("failed to prove transaction", err) + })?; + + ProvenTransaction::try_from(response.into_inner()).map_err(|_| { + TransactionProverError::other( + "failed to deserialize received response from remote transaction prover", + ) + }) + } + } +} + +// CONVERSIONS +// ================================================================================================ + +impl TryFrom for ProvenTransaction { + type Error = DeserializationError; + + fn try_from(response: proto::Proof) -> Result { + ProvenTransaction::read_from_bytes(&response.payload) + } +} + +impl From<&TransactionInputs> for proto::ProofRequest { + fn from(tx_inputs: &TransactionInputs) -> Self { + proto::ProofRequest { + proof_type: proto::ProofType::Transaction.into(), + payload: tx_inputs.to_bytes(), + } + } +} diff --git a/crates/rust-client/src/transaction/prover.rs b/crates/rust-client/src/transaction/prover.rs index 30fa4b187f..46ff7c0f1f 100644 --- a/crates/rust-client/src/transaction/prover.rs +++ b/crates/rust-client/src/transaction/prover.rs @@ -1,9 +1,11 @@ use alloc::boxed::Box; use miden_protocol::transaction::{ProvenTransaction, TransactionInputs}; -use miden_remote_prover_client::RemoteTransactionProver; use miden_tx::{LocalTransactionProver, TransactionProverError}; +#[cfg(any(feature = "std", target_arch = "wasm32"))] +use crate::remote_prover::RemoteTransactionProver; + #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] pub trait TransactionProver { @@ -24,6 +26,7 @@ impl TransactionProver for LocalTransactionProver { } } +#[cfg(any(feature = "std", target_arch = "wasm32"))] #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] impl TransactionProver for RemoteTransactionProver { From f87d141af75373c38f4d19207b5af0b19270b975 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Wed, 8 Jul 2026 15:40:05 -0300 Subject: [PATCH 08/20] ci: fix linting --- .github/workflows/lint.yml | 2 +- Cargo.toml | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b76e1ed3ad..5a3a6fab32 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -46,7 +46,7 @@ jobs: with: tool: cargo-binstall - name: Install workspace inheritance checker - run: cargo binstall --no-confirm cargo-workspace-inheritance-check@1.2.0 + run: cargo binstall --no-confirm cargo-workspace-inheritance-check@1.3.0 - name: Check workspace inheritance run: cargo workspace-inheritance-check --promotion-threshold 2 --promotion-failure diff --git a/Cargo.toml b/Cargo.toml index e50774289b..035845b426 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,12 @@ members = [ default-members = ["bin/miden-cli", "crates/rust-client"] +[workspace.metadata.inheritance-check] +ignore = [ + # rust-client's wasm32 target needs a sync-only tokio and can't inherit the workspace's rt-multi-thread features. + { dependency = "tokio", member = "crates/rust-client" }, +] + [workspace.package] authors = ["miden contributors"] edition = "2024" From c213fc8448067a2f1cbd5a9555a630802a8a8d08 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Mon, 13 Jul 2026 10:52:48 -0300 Subject: [PATCH 09/20] chore: address PR comments, remove middle-structs --- CHANGELOG.md | 2 +- Cargo.lock | 12 +- Cargo.toml | 2 +- bin/integration-tests/src/tests/client.rs | 4 +- crates/rust-client/src/grpc_support/mod.rs | 2 +- crates/rust-client/src/lib.rs | 4 +- .../src/rpc/domain/account_vault.rs | 110 ++++---- .../rust-client/src/rpc/domain/storage_map.rs | 115 ++++---- crates/rust-client/src/rpc/mod.rs | 38 +-- .../rust-client/src/rpc/tonic_client/mod.rs | 60 ++-- crates/rust-client/src/store/errors.rs | 3 + crates/rust-client/src/store/smt_forest.rs | 8 +- crates/rust-client/src/sync/state_sync.rs | 6 +- .../rust-client/src/sync/state_sync_update.rs | 263 ++++-------------- crates/rust-client/src/test_utils/mock.rs | 65 ++--- crates/rust-client/src/transaction/prover.rs | 4 +- crates/sqlite-store/src/account/accounts.rs | 3 +- crates/sqlite-store/src/account/vault.rs | 2 +- scripts/start-test-node.sh | 2 +- 19 files changed, 267 insertions(+), 438 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index def8e2a7ff..9d4cb6e1d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Breaking Changes -* [BREAKING][rust] Migrated to `miden-protocol` 0.16. Transaction-level fees were removed, block-level `FeeParameters` are unchanged. The relative `AccountDelta` model was replaced by the absolute `AccountPatch` model for account updates: `TransactionResult::account_delta` is now `account_patch`, `AccountUpdateDetails::Public` now carries an `AccountPatch`, account reconstruction is done via `Account::try_from(&AccountPatch)` or `Account::apply_patch` instead of previous `apply_delta`. Re-exports changed accordingly: `AccountStorageDelta` for `AccountStoragePatch`, `StorageMapDelta` for `StorageMapPatch`, and so on. Standards APIs were updated: `AuthMethod` was removed (use the concrete auth components), `create_fungible_faucet` became `create_user_fungible_faucet`. ([#XXXX](https://github.com/0xMiden/rust-sdk/pull/XXXX)). +* [BREAKING][rust] Migrated to `miden-protocol` 0.16. Transaction-level fees were removed, block-level `FeeParameters` are unchanged. The relative `AccountDelta` model was replaced by the absolute `AccountPatch` model for account updates: `TransactionResult::account_delta` is now `account_patch`, `AccountUpdateDetails::Public` now carries an `AccountPatch`, account reconstruction is done via `Account::try_from(&AccountPatch)` or `Account::apply_patch` instead of previous `apply_delta`. Re-exports changed accordingly: `AccountStorageDelta` for `AccountStoragePatch`, `StorageMapDelta` for `StorageMapPatch`, and so on. Standards APIs were updated: `AuthMethod` was removed (use the concrete auth components), `create_fungible_faucet` became `create_user_fungible_faucet`. ([#2290](https://github.com/0xMiden/rust-sdk/pull/2290)). * [BREAKING][store] The SQLite store now stores account IDs as serialized `BLOB` columns instead of hex `TEXT` ([#2309](https://github.com/0xMiden/rust-sdk/pull/2309)). * [BREAKING][param][store] `Store::insert_block_header` now takes a `nodes` argument and persists the header with its MMR authentication nodes in a single transaction; the standalone `Store::insert_partial_blockchain_nodes` is removed. Header-only inserts (e.g. genesis) pass an empty slice ([#2294](https://github.com/0xMiden/rust-sdk/pull/2294)). * [BREAKING][behavior][store] The `ConsumedExternal` note-metadata layout added in [#2308](https://github.com/0xMiden/rust-sdk/pull/2308) is now the only supported serialized format. The backward-compatible decoding of the older metadata-less layout is removed, so existing stores are not compatible and must be recreated ([#2313](https://github.com/0xMiden/rust-sdk/pull/2313)). diff --git a/Cargo.lock b/Cargo.lock index 56bbc72b2b..d00d742a28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1101,7 +1101,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2497,7 +2497,7 @@ dependencies = [ [[package]] name = "miden-node-proto-build" version = "0.15.1" -source = "git+https://github.com/0xMiden/miden-node.git?branch=next#ad3b1f8cdc8a8120ffb6419b42c3fcd18811475c" +source = "git+https://github.com/0xMiden/node.git?branch=next#ad3b1f8cdc8a8120ffb6419b42c3fcd18811475c" dependencies = [ "build-rs", "codegen", @@ -3979,7 +3979,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4483,10 +4483,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5299,7 +5299,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e08fa9080b..84d3d9238c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ miden-tx = { branch = "next", default-features = false, git = "https://gi miden-tx-batch = { branch = "next", default-features = false, git = "https://github.com/0xMiden/protocol.git" } # Miden node dependencies -miden-node-proto-build = { branch = "next", default-features = false, git = "https://github.com/0xMiden/miden-node.git" } +miden-node-proto-build = { branch = "next", default-features = false, git = "https://github.com/0xMiden/node.git" } miden-note-transport-proto-build = { default-features = false, version = "0.4.1" } # Miden debug dependency diff --git a/bin/integration-tests/src/tests/client.rs b/bin/integration-tests/src/tests/client.rs index 03bdb9268e..c34298971a 100644 --- a/bin/integration-tests/src/tests/client.rs +++ b/bin/integration-tests/src/tests/client.rs @@ -1608,8 +1608,8 @@ pub async fn test_unused_rpc_api(client_config: ClientConfig) -> Result<()> { assert_eq!(node_nullifier.nullifier, nullifier); assert_eq!(note.script().root(), retrieved_note_script.root()); - assert!(!sync_storage_maps.updates.is_empty()); - assert!(!account_vault_info.updates.is_empty()); + assert!(!sync_storage_maps.map_entries.is_empty()); + assert!(!account_vault_info.vault_patch.is_empty()); assert!(!transactions.is_empty()); Ok(()) diff --git a/crates/rust-client/src/grpc_support/mod.rs b/crates/rust-client/src/grpc_support/mod.rs index f2a97a1294..42514a8023 100644 --- a/crates/rust-client/src/grpc_support/mod.rs +++ b/crates/rust-client/src/grpc_support/mod.rs @@ -1,6 +1,6 @@ use alloc::string::String; -#[cfg(any(feature = "std", target_arch = "wasm32"))] +#[cfg(feature = "tonic")] pub use crate::RemoteTransactionProver; /// Default remote prover endpoint for testnet. diff --git a/crates/rust-client/src/lib.rs b/crates/rust-client/src/lib.rs index f7a5c1c118..cccd836fb3 100644 --- a/crates/rust-client/src/lib.rs +++ b/crates/rust-client/src/lib.rs @@ -124,7 +124,7 @@ pub mod keystore; pub mod note; pub mod note_transport; pub mod pswap; -#[cfg(any(feature = "std", target_arch = "wasm32"))] +#[cfg(feature = "tonic")] pub mod remote_prover; pub mod rpc; pub mod settings; @@ -326,7 +326,7 @@ pub use miden_protocol::{ ZERO, }; pub use miden_tx::ExecutionOptions; -#[cfg(any(feature = "std", target_arch = "wasm32"))] +#[cfg(feature = "tonic")] pub use remote_prover::RemoteTransactionProver; /// Provides test utilities for working with accounts and account IDs diff --git a/crates/rust-client/src/rpc/domain/account_vault.rs b/crates/rust-client/src/rpc/domain/account_vault.rs index 297623ffb1..e65d7f7008 100644 --- a/crates/rust-client/src/rpc/domain/account_vault.rs +++ b/crates/rust-client/src/rpc/domain/account_vault.rs @@ -2,12 +2,21 @@ use alloc::string::ToString; use alloc::vec::Vec; use miden_protocol::Word; +use miden_protocol::account::AccountVaultPatch; use miden_protocol::asset::{Asset, AssetVaultKey}; use miden_protocol::block::BlockNumber; use crate::rpc::domain::MissingFieldHelper; use crate::rpc::{RpcConversionError, RpcError, generated as proto}; +/// A single vault update as reported by the node: the block it occurred in, the affected vault key, +/// and its new asset (`None` if the asset was removed at that block). +pub(crate) struct VaultUpdate { + pub(crate) block_num: BlockNumber, + pub(crate) vault_key: AssetVaultKey, + pub(crate) asset: Option, +} + // ASSET CONVERSION // ================================================================================================ @@ -31,16 +40,18 @@ impl TryFrom for Asset { // ACCOUNT VAULT INFO // ================================================================================================ -/// Represents a `proto::rpc::SyncAccountVaultResponse` with fields converted into domain -/// types. Contains information of asset updates in a given range of blocks specified on request. -/// Also provides the current chain tip while processing the request. +/// The merged result of syncing an account's vault over a block range. +/// +/// The node reports per-block asset updates that may repeat a vault key across blocks; these are +/// merged into a single absolute [`AccountVaultPatch`] (latest block wins per key). Also +/// provides the current chain tip observed while processing the request. pub struct AccountVaultInfo { - /// Current chain tip + /// Current chain tip. pub chain_tip: BlockNumber, /// The block number of the last check included in this response. pub block_number: BlockNumber, - /// List of asset updates for the account. - pub updates: Vec, + /// The absolute vault patch merged from the per-block updates. + pub vault_patch: AccountVaultPatch, } // ACCOUNT VAULT CONVERSION @@ -56,19 +67,17 @@ impl TryFrom for AccountVaultInfo { .ok_or(proto::rpc::SyncAccountVaultResponse::missing_field(stringify!( pagination_info )))?; - let chain_tip = pagination_info.chain_tip; - let block_number = pagination_info.block_num; let updates = value .updates - .iter() - .map(|update| (*update).try_into()) + .into_iter() + .map(vault_update_from_proto) .collect::, _>>()?; Ok(Self { - chain_tip: chain_tip.into(), - block_number: block_number.into(), - updates, + chain_tip: pagination_info.chain_tip.into(), + block_number: pagination_info.block_num.into(), + vault_patch: merge_vault_updates(updates), }) } } @@ -76,47 +85,44 @@ impl TryFrom for AccountVaultInfo { // ACCOUNT VAULT UPDATE // ================================================================================================ -/// Represents an update to an account vault, including the vault key and asset value involved. -#[derive(Debug, Clone)] -pub struct AccountVaultUpdate { - /// Block number in which the slot was updated. - pub block_num: BlockNumber, - /// Asset value related to the vault key. If not present, the asset was removed from the vault. - pub asset: Option, - /// Vault key associated with the asset. - pub vault_key: AssetVaultKey, -} - -// ACCOUNT VAULT UPDATE CONVERSION -// ================================================================================================ - -impl TryFrom for AccountVaultUpdate { - type Error = RpcError; - - fn try_from(value: proto::rpc::AccountVaultUpdate) -> Result { - let block_num = value.block_num; - - let vault_key_inner: Word = value - .vault_key - .ok_or(proto::rpc::SyncAccountVaultResponse::missing_field(stringify!(vault_key)))? - .try_into()?; - let vault_key = AssetVaultKey::try_from(vault_key_inner) - .map_err(|e| RpcError::InvalidResponse(e.to_string()))?; +/// Converts a single proto vault update into a [`VaultUpdate`], validating that a present asset's +/// vault key matches the reported key. +fn vault_update_from_proto(value: proto::rpc::AccountVaultUpdate) -> Result { + let block_num = BlockNumber::from(value.block_num); + + let vault_key_inner: Word = value + .vault_key + .ok_or(proto::rpc::SyncAccountVaultResponse::missing_field(stringify!(vault_key)))? + .try_into()?; + let vault_key = AssetVaultKey::try_from(vault_key_inner) + .map_err(|e| RpcError::InvalidResponse(e.to_string()))?; + + let asset = value.asset.map(Asset::try_from).transpose()?; + + if let Some(ref asset) = asset + && asset.vault_key() != vault_key + { + return Err(RpcError::InvalidResponse( + "account vault update returned mismatched asset key".to_string(), + )); + } - let asset = value.asset.map(Asset::try_from).transpose()?; + Ok(VaultUpdate { block_num, vault_key, asset }) +} - if let Some(ref asset) = asset - && asset.vault_key() != vault_key - { - return Err(RpcError::InvalidResponse( - "account vault update returned mismatched asset key".to_string(), - )); +/// Merges per-block vault updates into an absolute [`AccountVaultPatch`]. +/// +/// The node may report the same vault key in more than one block; applying the updates in ascending +/// block order lets the latest block win, with an absent asset (`None`) encoding a removal. +pub(crate) fn merge_vault_updates(mut updates: Vec) -> AccountVaultPatch { + updates.sort_by_key(|update| update.block_num); + + let mut patch = AccountVaultPatch::default(); + for VaultUpdate { vault_key, asset, .. } in updates { + match asset { + Some(asset) => patch.insert_asset(asset), + None => patch.remove_asset(vault_key), } - - Ok(Self { - block_num: block_num.into(), - asset, - vault_key, - }) } + patch } diff --git a/crates/rust-client/src/rpc/domain/storage_map.rs b/crates/rust-client/src/rpc/domain/storage_map.rs index 41313b0719..5d661f963c 100644 --- a/crates/rust-client/src/rpc/domain/storage_map.rs +++ b/crates/rust-client/src/rpc/domain/storage_map.rs @@ -1,26 +1,38 @@ +use alloc::collections::BTreeMap; use alloc::string::ToString; use alloc::vec::Vec; use miden_protocol::Word; -use miden_protocol::account::{StorageMapKey, StorageSlotName}; +use miden_protocol::account::{StorageMapKey, StorageMapPatchEntries, StorageSlotName}; use miden_protocol::block::BlockNumber; use crate::rpc::domain::MissingFieldHelper; -use crate::rpc::{RpcConversionError, RpcError, generated as proto}; +use crate::rpc::{RpcError, generated as proto}; + +/// A single storage-map update as reported by the node: the block it occurred in, the affected slot +/// and key, and the new value. +pub(crate) struct StorageMapUpdate { + pub(crate) block_num: BlockNumber, + pub(crate) slot_name: StorageSlotName, + pub(crate) key: StorageMapKey, + pub(crate) value: Word, +} // STORAGE MAP INFO // ================================================================================================ -/// Represents a `proto::rpc::SyncStorageMapsResponse` with fields converted into domain -/// types. Contains information of updated map slots in a given range of blocks specified on -/// request. Also provides the current chain tip while processing the request. +/// The merged result of syncing an account's storage maps over a block range. +/// +/// The node reports per-block map entry updates that may repeat a `(slot, key)` across blocks; +/// these are merged per slot into the absolute changed entries (latest block wins per key). Also +/// provides the current chain tip observed while processing the request. pub struct StorageMapInfo { - /// Current chain tip + /// Current chain tip. pub chain_tip: BlockNumber, /// The block number of the last check included in this response. pub block_number: BlockNumber, - /// The list of storage map updates. - pub updates: Vec, + /// The absolute changed entries per storage map slot, merged from the per-block updates. + pub map_entries: BTreeMap, } // STORAGE MAP INFO CONVERSION @@ -33,19 +45,17 @@ impl TryFrom for StorageMapInfo { let pagination_info = value.pagination_info.ok_or( proto::rpc::SyncAccountStorageMapsResponse::missing_field(stringify!(pagination_info)), )?; - let chain_tip = pagination_info.chain_tip; - let block_number = pagination_info.block_num; let updates = value .updates .into_iter() - .map(TryInto::try_into) + .map(storage_map_update_from_proto) .collect::, _>>()?; Ok(Self { - chain_tip: chain_tip.into(), - block_number: block_number.into(), - updates, + chain_tip: pagination_info.chain_tip.into(), + block_number: pagination_info.block_num.into(), + map_entries: merge_storage_map_updates(updates), }) } } @@ -53,46 +63,45 @@ impl TryFrom for StorageMapInfo { // STORAGE MAP UPDATE // ================================================================================================ -/// Represents a `proto::rpc::StorageMapUpdate` -#[derive(Debug, Clone)] -pub struct StorageMapUpdate { - /// Block number in which the slot was updated. - pub block_num: BlockNumber, - /// Name of the storage slot. - pub slot_name: StorageSlotName, - /// The storage map key - pub key: StorageMapKey, - /// The storage map value. - pub value: Word, +/// Converts a single proto storage-map update into a [`StorageMapUpdate`]. +fn storage_map_update_from_proto( + value: proto::rpc::StorageMapUpdate, +) -> Result { + let block_num = BlockNumber::from(value.block_num); + + let slot_name = StorageSlotName::new(value.slot_name) + .map_err(|err| RpcError::InvalidResponse(err.to_string()))?; + + let key: StorageMapKey = value + .key + .ok_or(proto::rpc::StorageMapUpdate::missing_field(stringify!(key)))? + .try_into()?; + + let map_value: Word = value + .value + .ok_or(proto::rpc::StorageMapUpdate::missing_field(stringify!(value)))? + .try_into()?; + + Ok(StorageMapUpdate { + block_num, + slot_name, + key, + value: map_value, + }) } -// STORAGE MAP UPDATE CONVERSION -// ================================================================================================ - -impl TryFrom for StorageMapUpdate { - type Error = RpcConversionError; - - fn try_from(value: proto::rpc::StorageMapUpdate) -> Result { - let block_num = value.block_num; - - let slot_name = StorageSlotName::new(value.slot_name) - .map_err(|err| RpcConversionError::InvalidField(err.to_string()))?; - - let key: StorageMapKey = value - .key - .ok_or(proto::rpc::StorageMapUpdate::missing_field(stringify!(key)))? - .try_into()?; - - let value: Word = value - .value - .ok_or(proto::rpc::StorageMapUpdate::missing_field(stringify!(value)))? - .try_into()?; - - Ok(Self { - block_num: block_num.into(), - slot_name, - key, - value, - }) +/// Merges per-block map updates into the absolute changed entries per slot. +/// +/// The node may report the same `(slot, key)` in more than one block; applying the updates in +/// ascending block order lets the latest block win, with an empty value encoding a cleared entry. +pub(crate) fn merge_storage_map_updates( + mut updates: Vec, +) -> BTreeMap { + updates.sort_by_key(|update| update.block_num); + + let mut entries_by_slot: BTreeMap = BTreeMap::new(); + for StorageMapUpdate { slot_name, key, value, .. } in updates { + entries_by_slot.entry(slot_name).or_default().insert(key, value); } + entries_by_slot } diff --git a/crates/rust-client/src/rpc/mod.rs b/crates/rust-client/src/rpc/mod.rs index 8070c7a0b4..b3d273d0bd 100644 --- a/crates/rust-client/src/rpc/mod.rs +++ b/crates/rust-client/src/rpc/mod.rs @@ -393,17 +393,9 @@ pub trait NodeRpcClient: Send + Sync { } let vault_info = self.sync_account_vault(BlockNumber::GENESIS, block_to, account_id).await?; - let mut updates = vault_info.updates; - // The node returns the full history of vault entries, so a given key may appear in more - // than one block. Sort by block so the BTreeMap keeps the latest value per key. - updates.sort_by_key(|u| u.block_num); - details.vault_details.assets = updates - .into_iter() - .map(|u| (u.vault_key, u.asset)) - .collect::>() - .into_values() - .flatten() - .collect(); + // Syncing from genesis merges the full vault history into an absolute patch, so its + // updated (non-removed) assets are the account's current vault contents. + details.vault_details.assets = vault_info.vault_patch.updated_assets().collect(); details.vault_details.too_many_assets = false; Ok(()) } @@ -425,18 +417,18 @@ pub trait NodeRpcClient: Send + Sync { if !map_details.too_many_entries { continue; } - // The node returns the full history of map entries, so a given key may appear in - // more than one block. Sort by block so the BTreeMap keeps the latest value per key. - let mut sorted: Vec<_> = - info.updates.iter().filter(|u| u.slot_name == map_details.slot_name).collect(); - sorted.sort_by_key(|u| u.block_num); - let entries: Vec = sorted - .into_iter() - .map(|u| (u.key, u.value)) - .collect::>() - .into_iter() - .map(|(key, value)| StorageMapEntry { key, value }) - .collect(); + // Syncing from genesis merges the full history of each slot into its absolute + let entries: Vec = info + .map_entries + .get(&map_details.slot_name) + .map(|entries| { + entries + .as_map() + .iter() + .map(|(key, value)| StorageMapEntry { key: *key, value: *value }) + .collect() + }) + .unwrap_or_default(); map_details.too_many_entries = false; map_details.entries = StorageMapEntries::AllEntries(entries); } diff --git a/crates/rust-client/src/rpc/tonic_client/mod.rs b/crates/rust-client/src/rpc/tonic_client/mod.rs index 3790696c38..cac0b9cf56 100644 --- a/crates/rust-client/src/rpc/tonic_client/mod.rs +++ b/crates/rust-client/src/rpc/tonic_client/mod.rs @@ -10,7 +10,13 @@ use miden_protocol::vm::FutureMaybeSend; type RpcFuture = Pin>>; -use miden_protocol::account::{AccountCode, AccountId}; +use miden_protocol::account::{ + AccountCode, + AccountId, + AccountVaultPatch, + StorageMapPatchEntries, + StorageSlotName, +}; use miden_protocol::address::NetworkId; use miden_protocol::batch::{ProposedBatch, ProvenBatch}; use miden_protocol::block::account_tree::AccountWitness; @@ -37,10 +43,10 @@ use super::domain::nullifier::NullifierUpdate; use super::generated::rpc::AccountRequest; use super::generated::rpc::account_request::AccountDetailRequest; use super::{Endpoint, NodeRpcClient, RpcEndpoint, RpcError, RpcStatusInfo}; -use crate::rpc::domain::account_vault::{AccountVaultInfo, AccountVaultUpdate}; +use crate::rpc::domain::account_vault::AccountVaultInfo; use crate::rpc::domain::limits::RpcLimits; use crate::rpc::domain::status::NetworkNoteStatusInfo; -use crate::rpc::domain::storage_map::{StorageMapInfo, StorageMapUpdate}; +use crate::rpc::domain::storage_map::StorageMapInfo; use crate::rpc::domain::sync::{ChainMmrInfo, SyncTarget}; use crate::rpc::domain::transaction::TransactionRecord; use crate::rpc::errors::node::parse_node_error; @@ -863,7 +869,7 @@ impl NodeRpcClient for GrpcClient { account_id: AccountId, ) -> Result { let mut pagination = BlockPagination::new(block_from, block_to); - let mut updates = Vec::new(); + let mut map_entries: BTreeMap = BTreeMap::new(); let (chain_tip, block_number) = loop { let request = proto::rpc::SyncAccountStorageMapsRequest { @@ -879,20 +885,17 @@ impl NodeRpcClient for GrpcClient { Box::pin(async move { rpc_api.sync_account_storage_maps(request).await }) }) .await?; - let response = response.into_inner(); - let page = response - .pagination_info - .ok_or(RpcError::ExpectedDataMissing("pagination_info".to_owned()))?; - let page_block_num = BlockNumber::from(page.block_num); - let page_chain_tip = BlockNumber::from(page.chain_tip); - let batch = response - .updates - .into_iter() - .map(TryInto::try_into) - .collect::, _>>()?; - updates.extend(batch); + let page = StorageMapInfo::try_from(response.into_inner())?; + + for (slot_name, entries) in page.map_entries { + map_entries + .entry(slot_name) + .or_default() + .as_map_mut() + .extend(entries.into_map()); + } - match pagination.advance(page_block_num, page_chain_tip)? { + match pagination.advance(page.block_number, page.chain_tip)? { PaginationResult::Continue => {}, PaginationResult::Done { chain_tip: final_chain_tip, @@ -901,7 +904,7 @@ impl NodeRpcClient for GrpcClient { } }; - Ok(StorageMapInfo { chain_tip, block_number, updates }) + Ok(StorageMapInfo { chain_tip, block_number, map_entries }) } async fn sync_account_vault( @@ -911,7 +914,7 @@ impl NodeRpcClient for GrpcClient { account_id: AccountId, ) -> Result { let mut pagination = BlockPagination::new(block_from, block_to); - let mut updates = Vec::new(); + let mut vault_patch = AccountVaultPatch::default(); let (chain_tip, block_number) = loop { let request = proto::rpc::SyncAccountVaultRequest { @@ -927,20 +930,11 @@ impl NodeRpcClient for GrpcClient { Box::pin(async move { rpc_api.sync_account_vault(request).await }) }) .await?; - let response = response.into_inner(); - let page = response - .pagination_info - .ok_or(RpcError::ExpectedDataMissing("pagination_info".to_owned()))?; - let page_block_num = BlockNumber::from(page.block_num); - let page_chain_tip = BlockNumber::from(page.chain_tip); - let batch = response - .updates - .iter() - .map(|u| (*u).try_into()) - .collect::, _>>()?; - updates.extend(batch); + let page = AccountVaultInfo::try_from(response.into_inner())?; + + vault_patch.merge(page.vault_patch); - match pagination.advance(page_block_num, page_chain_tip)? { + match pagination.advance(page.block_number, page.chain_tip)? { PaginationResult::Continue => {}, PaginationResult::Done { chain_tip: final_chain_tip, @@ -949,7 +943,7 @@ impl NodeRpcClient for GrpcClient { } }; - Ok(AccountVaultInfo { chain_tip, block_number, updates }) + Ok(AccountVaultInfo { chain_tip, block_number, vault_patch }) } /// Sends one or more `SyncTransactions` requests to the node and concatenates the responses diff --git a/crates/rust-client/src/store/errors.rs b/crates/rust-client/src/store/errors.rs index 331246148d..f74b2d68e6 100644 --- a/crates/rust-client/src/store/errors.rs +++ b/crates/rust-client/src/store/errors.rs @@ -2,6 +2,7 @@ use alloc::string::String; use core::num::TryFromIntError; use miden_protocol::account::AccountId; +use miden_protocol::asset::AssetVaultKey; use miden_protocol::block::BlockNumber; use miden_protocol::crypto::merkle::MerkleError; use miden_protocol::crypto::merkle::mmr::MmrError; @@ -93,6 +94,8 @@ pub enum StoreError { TransactionScriptError(#[from] TransactionScriptError), #[error("account vault data for root {0} not found")] VaultDataNotFound(Word), + #[error("vault key {0} (hashed to {1}) is not tracked in the vault")] + VaultKeyNotTracked(AssetVaultKey, Word), #[error("failed to parse word")] WordError(#[from] WordError), } diff --git a/crates/rust-client/src/store/smt_forest.rs b/crates/rust-client/src/store/smt_forest.rs index e4b0e4a538..90670ef66f 100644 --- a/crates/rust-client/src/store/smt_forest.rs +++ b/crates/rust-client/src/store/smt_forest.rs @@ -11,8 +11,8 @@ use miden_protocol::account::{ StorageSlotContent, }; use miden_protocol::asset::{Asset, AssetVault, AssetVaultKey, AssetWitness}; +use miden_protocol::crypto::merkle::EmptySubtreeRoots; use miden_protocol::crypto::merkle::smt::{SMT_DEPTH, Smt, SmtForest}; -use miden_protocol::crypto::merkle::{EmptySubtreeRoots, MerkleError}; use miden_protocol::{EMPTY_WORD, Word}; use super::StoreError; @@ -55,9 +55,11 @@ impl AccountSmtForest { let vault_key_word: Word = vault_key.into(); let hashed_key: Word = vault_key.hash().into(); let proof = self.forest.open(vault_root, hashed_key)?; - let asset_word = proof.get(&hashed_key).ok_or(MerkleError::UntrackedKey(hashed_key))?; + let asset_word = proof + .get(&hashed_key) + .ok_or(StoreError::VaultKeyNotTracked(vault_key, hashed_key))?; if asset_word == EMPTY_WORD { - return Err(MerkleError::UntrackedKey(hashed_key).into()); + return Err(StoreError::VaultKeyNotTracked(vault_key, hashed_key)); } let asset = Asset::from_key_value_words(vault_key_word, asset_word)?; diff --git a/crates/rust-client/src/sync/state_sync.rs b/crates/rust-client/src/sync/state_sync.rs index e0f21ee636..ca486043cc 100644 --- a/crates/rust-client/src/sync/state_sync.rs +++ b/crates/rust-client/src/sync/state_sync.rs @@ -974,11 +974,9 @@ impl StateSync { Ok(PublicAccountUpdate::Patch(PublicAccountPatch::new( details.header.clone(), - block_from, - block_to, value_slot_updates, - map_info.updates, - vault_info.updates, + map_info.map_entries, + vault_info.vault_patch, ))) } diff --git a/crates/rust-client/src/sync/state_sync_update.rs b/crates/rust-client/src/sync/state_sync_update.rs index 9017b67bb5..7e594eafc7 100644 --- a/crates/rust-client/src/sync/state_sync_update.rs +++ b/crates/rust-client/src/sync/state_sync_update.rs @@ -24,8 +24,6 @@ use miden_protocol::{Felt, ONE, Word}; use super::SyncSummary; use crate::note::{NoteUpdateTracker, NoteUpdateType}; -use crate::rpc::domain::account_vault::AccountVaultUpdate; -use crate::rpc::domain::storage_map::StorageMapUpdate; use crate::rpc::domain::transaction::TransactionRecord as RpcTransactionRecord; use crate::transaction::{DiscardCause, TransactionRecord, TransactionStatus}; @@ -377,78 +375,54 @@ impl PublicAccountUpdate { /// Patch payload for a public account update. /// -/// Carries the new account header plus the per-block updates fetched from the node's incremental -/// endpoints (`sync_storage_maps` and `sync_account_vault`). The store turns these absolute -/// updates into the [`AccountPatch`] to apply via [`Self::compute_account_patch`]. +/// Carries the new account header plus the merged updates fetched from the node's incremental +/// endpoints (`sync_storage_maps` and `sync_account_vault`): the value-slot values, the absolute +/// changed map entries per slot, and the absolute vault patch. The store turns these into the +/// [`AccountPatch`] to apply via [`Self::compute_account_patch`]. #[derive(Debug, Clone)] pub struct PublicAccountPatch { /// The new account header after applying these updates. new_header: AccountHeader, - /// First block of the synced range (the client's previous sync height). - block_from: BlockNumber, - /// Last block of the synced range (the block at which `new_header` is observed). - block_to: BlockNumber, /// New value-slot values from the `get_account` storage header. Value slots are always /// small enough to fit in the response. value_slot_updates: Vec<(StorageSlotName, Word)>, - /// Per-block storage map updates from `sync_storage_maps`. - storage_map_updates: Vec, - /// Per-block vault updates from `sync_account_vault`. - vault_updates: Vec, + /// Absolute changed entries per storage map slot, merged from `sync_storage_maps`. + map_entries: BTreeMap, + /// Absolute vault patch merged from `sync_account_vault`. + vault_patch: AccountVaultPatch, } impl PublicAccountPatch { /// Creates a new [`PublicAccountPatch`]. pub fn new( new_header: AccountHeader, - block_from: BlockNumber, - block_to: BlockNumber, value_slot_updates: Vec<(StorageSlotName, Word)>, - storage_map_updates: Vec, - vault_updates: Vec, + map_entries: BTreeMap, + vault_patch: AccountVaultPatch, ) -> Self { Self { new_header, - block_from, - block_to, value_slot_updates, - storage_map_updates, - vault_updates, + map_entries, + vault_patch, } } - /// Returns the account ID this delta applies to. + /// Returns the account ID this patch applies to. pub fn id(&self) -> AccountId { self.new_header.id() } - /// Returns the new account header that this delta advances the local state to. + /// Returns the new account header that this patch advances the local state to. pub fn new_header(&self) -> &AccountHeader { &self.new_header } - /// Returns the first block of the synced range. - pub fn block_from(&self) -> BlockNumber { - self.block_from - } - - /// Returns the names of the value slots referenced by this delta. - pub fn value_slot_names(&self) -> Vec { - self.value_slot_updates.iter().map(|(name, _)| name.clone()).collect() - } - - /// Returns the last block of the synced range. - pub fn block_to(&self) -> BlockNumber { - self.block_to - } - /// Builds the absolute [`AccountPatch`] implied by this payload. /// - /// The node's incremental endpoints already report the new absolute value of each changed - /// storage slot, map entry, and vault asset, so the patch is assembled directly from them with - /// no need to load the prior account state: value slots and map entries become storage-patch - /// entries, present assets become vault-patch insertions, and absent assets (the node's removal - /// signal) become vault-patch removals. + /// The carried updates are already merged to the new absolute value of each changed storage + /// slot, map entry, and vault asset, so the patch is assembled directly from them with no need + /// to load the prior account state. /// /// An update of an existing account (final nonce > 1) yields a partial-state patch with no /// code. A newly created account (final nonce 1) cannot be represented as a partial-state @@ -472,44 +446,26 @@ impl PublicAccountPatch { (slot_name.clone(), StorageSlotPatch::Value(value_patch)) }); - // Map entries are absolute per (slot, key), applying them in block order lets a later block - // overwrite an earlier one for the same key, yielding the final value. - let mut map_updates: Vec<&StorageMapUpdate> = self.storage_map_updates.iter().collect(); - map_updates.sort_by_key(|u| u.block_num); - let mut map_entries_by_slot: BTreeMap = - BTreeMap::new(); - for update in map_updates { - map_entries_by_slot - .entry(update.slot_name.clone()) - .or_default() - .insert(update.key, update.value); - } - let map_entries = map_entries_by_slot.into_iter().map(|(slot_name, entries)| { + let map_entries = self.map_entries.iter().map(|(slot_name, entries)| { let map_patch = if is_full_state { - StorageMapPatch::Create { entries } + StorageMapPatch::Create { entries: entries.clone() } } else { - StorageMapPatch::Update { entries } + StorageMapPatch::Update { entries: entries.clone() } }; - (slot_name, StorageSlotPatch::Map(map_patch)) + (slot_name.clone(), StorageSlotPatch::Map(map_patch)) }); let storage = AccountStoragePatch::from_entries(value_entries.chain(map_entries))?; - // Vault entries are absolute per key, applying them in block order yields the final value, - // with a `None` asset encoding a removal. - let mut vault = AccountVaultPatch::default(); - let mut vault_updates: Vec<&AccountVaultUpdate> = self.vault_updates.iter().collect(); - vault_updates.sort_by_key(|u| u.block_num); - for update in vault_updates { - match update.asset { - Some(asset) => vault.insert_asset(asset), - None => vault.remove_asset(update.vault_key), - } - } + let code = if is_full_state { code } else { None }; - let code = if self.new_header.nonce() == ONE { code } else { None }; - - AccountPatch::new(account_id, storage, vault, code, Some(self.new_header.nonce())) + AccountPatch::new( + account_id, + storage, + self.vault_patch.clone(), + code, + Some(self.new_header.nonce()), + ) } } @@ -564,14 +520,11 @@ impl AccountUpdates { #[cfg(test)] mod tests { + use alloc::collections::BTreeMap; use alloc::vec; - use miden_protocol::account::StorageMapKey; - use miden_protocol::asset::{Asset, AssetVaultKey, FungibleAsset}; - use miden_protocol::testing::account_id::{ - ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, - }; + use miden_protocol::account::{AccountCode, StorageMapKey, StorageMapPatchEntries}; + use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE; use super::*; @@ -579,18 +532,10 @@ mod tests { ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into().unwrap() } - fn faucet_id() -> AccountId { - ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET.try_into().unwrap() - } - fn slot_name(name: &str) -> StorageSlotName { StorageSlotName::new(name).unwrap() } - fn map_key(n: u64) -> StorageMapKey { - StorageMapKey::from_raw(word(n)) - } - fn word(n: u64) -> Word { Word::from([ Felt::new_unchecked(n), @@ -600,10 +545,6 @@ mod tests { ]) } - fn fungible(amount: u64) -> Asset { - Asset::Fungible(FungibleAsset::new(faucet_id(), amount).unwrap()) - } - fn header_with_nonce(nonce: u64) -> AccountHeader { AccountHeader::new( account_id(), @@ -614,61 +555,22 @@ mod tests { ) } - fn empty_payload(new_header: AccountHeader) -> PublicAccountPatch { - PublicAccountPatch::new( - new_header, - BlockNumber::from(0u32), - BlockNumber::from(1u32), - vec![], - vec![], - vec![], - ) - } - - fn storage_map_update( - block: u32, - slot: &StorageSlotName, - key: StorageMapKey, - value: Word, - ) -> StorageMapUpdate { - StorageMapUpdate { - block_num: BlockNumber::from(block), - slot_name: slot.clone(), - key, - value, - } - } - - fn vault_update( - block: u32, - asset: Option, - vault_key: AssetVaultKey, - ) -> AccountVaultUpdate { - AccountVaultUpdate { - block_num: BlockNumber::from(block), - asset, - vault_key, - } - } - fn payload( new_nonce: u64, value_slot_updates: Vec<(StorageSlotName, Word)>, - storage_map_updates: Vec, - vault_updates: Vec, + map_entries: BTreeMap, ) -> PublicAccountPatch { PublicAccountPatch::new( header_with_nonce(new_nonce), - BlockNumber::from(0u32), - BlockNumber::from(1u32), value_slot_updates, - storage_map_updates, - vault_updates, + map_entries, + AccountVaultPatch::default(), ) } - // COMPUTE ACCOUNT PATCH - STORAGE - // -------------------------------------------------------------------------------------------- + fn empty_payload(new_header: AccountHeader) -> PublicAccountPatch { + PublicAccountPatch::new(new_header, vec![], BTreeMap::new(), AccountVaultPatch::default()) + } #[test] fn compute_patch_empty_payload_carries_only_nonce() { @@ -683,7 +585,7 @@ mod tests { #[test] fn compute_patch_sets_value_slot_absolutely() { let value_slot = slot_name("miden::test::value"); - let patch = payload(2, vec![(value_slot.clone(), word(2))], vec![], vec![]) + let patch = payload(2, vec![(value_slot.clone(), word(2))], BTreeMap::new()) .compute_account_patch(None) .unwrap(); @@ -691,91 +593,20 @@ mod tests { } #[test] - fn compute_patch_map_dedup_keeps_latest_block_per_key() { + fn compute_patch_wraps_merged_map_entries() { let map_slot = slot_name("miden::test::map"); - let key = map_key(42); - let updates = vec![ - storage_map_update(1, &map_slot, key, word(100)), - storage_map_update(3, &map_slot, key, word(300)), - storage_map_update(2, &map_slot, key, word(200)), - ]; + let key = StorageMapKey::from_raw(word(42)); + let mut entries = StorageMapPatchEntries::new(); + entries.insert(key, word(300)); + let map_entries = BTreeMap::from([(map_slot.clone(), entries)]); - let patch = payload(2, vec![], updates, vec![]).compute_account_patch(None).unwrap(); + let patch = payload(2, vec![], map_entries).compute_account_patch(None).unwrap(); let map = patch.storage().get_map(&map_slot).expect("patch should contain map slot"); assert_eq!(map.entries().unwrap().as_map().len(), 1); assert_eq!(*map.entries().unwrap().as_map().values().next().unwrap(), word(300)); } - #[test] - fn compute_patch_map_multiple_keys_in_same_slot_all_kept() { - let map_slot = slot_name("miden::test::map"); - let updates = vec![ - storage_map_update(1, &map_slot, map_key(1), word(100)), - storage_map_update(2, &map_slot, map_key(2), word(200)), - ]; - - let patch = payload(2, vec![], updates, vec![]).compute_account_patch(None).unwrap(); - let map = patch.storage().get_map(&map_slot).unwrap(); - assert_eq!(map.entries().unwrap().as_map().len(), 2); - } - - // COMPUTE ACCOUNT PATCH - VAULT - // -------------------------------------------------------------------------------------------- - - #[test] - fn compute_patch_inserts_asset_absolutely() { - let asset = fungible(100); - let updates = vec![vault_update(1, Some(asset), asset.vault_key())]; - - let patch = payload(2, vec![], vec![], updates).compute_account_patch(None).unwrap(); - - let updated_assets: Vec<_> = patch.vault().updated_assets().collect(); - assert_eq!(updated_assets, vec![asset]); - assert_eq!(patch.vault().removed_asset_keys().count(), 0); - } - - #[test] - fn compute_patch_removed_asset_is_a_removal() { - let asset = fungible(100); - let updates = vec![vault_update(1, None, asset.vault_key())]; - - let patch = payload(2, vec![], vec![], updates).compute_account_patch(None).unwrap(); - - let removed: Vec<_> = patch.vault().removed_asset_keys().copied().collect(); - assert_eq!(removed, vec![asset.vault_key()]); - assert_eq!(patch.vault().updated_assets().count(), 0); - } - - #[test] - fn compute_patch_vault_dedup_keeps_latest_block_per_key() { - let asset_v1 = fungible(100); - let asset_v3 = fungible(300); - let asset_v2 = fungible(200); - let key = asset_v1.vault_key(); - let updates = vec![ - vault_update(1, Some(asset_v1), key), - vault_update(3, Some(asset_v3), key), - vault_update(2, Some(asset_v2), key), - ]; - - let patch = payload(2, vec![], vec![], updates).compute_account_patch(None).unwrap(); - let updated_assets: Vec<_> = patch.vault().updated_assets().collect(); - assert_eq!(updated_assets, vec![asset_v3]); - } - - #[test] - fn compute_patch_added_then_removed_is_a_removal() { - let asset = fungible(100); - let key = asset.vault_key(); - let updates = vec![vault_update(1, Some(asset), key), vault_update(2, None, key)]; - - let patch = payload(2, vec![], vec![], updates).compute_account_patch(None).unwrap(); - assert_eq!(patch.vault().updated_assets().count(), 0); - let removed: Vec<_> = patch.vault().removed_asset_keys().copied().collect(); - assert_eq!(removed, vec![key]); - } - #[test] fn compute_patch_rejects_zero_nonce() { let result = empty_payload(header_with_nonce(0)).compute_account_patch(None); @@ -787,7 +618,7 @@ mod tests { #[test] fn compute_patch_for_new_account_is_full_state() { let value_slot = slot_name("miden::test::value"); - let patch = payload(1, vec![(value_slot, word(1))], vec![], vec![]) + let patch = payload(1, vec![(value_slot, word(1))], BTreeMap::new()) .compute_account_patch(Some(AccountCode::mock())) .unwrap(); @@ -799,7 +630,7 @@ mod tests { /// patch), so the missing-code case is reported rather than silently producing a wrong patch. #[test] fn compute_patch_for_new_account_without_code_errors() { - let result = payload(1, vec![], vec![], vec![]).compute_account_patch(None); + let result = payload(1, vec![], BTreeMap::new()).compute_account_patch(None); assert!(result.is_err()); } } diff --git a/crates/rust-client/src/test_utils/mock.rs b/crates/rust-client/src/test_utils/mock.rs index 6a3ee3322b..d2eeed61d1 100644 --- a/crates/rust-client/src/test_utils/mock.rs +++ b/crates/rust-client/src/test_utils/mock.rs @@ -32,11 +32,15 @@ use crate::rpc::domain::account::{ StorageMapEntry, StorageMapFetch, }; -use crate::rpc::domain::account_vault::{AccountVaultInfo, AccountVaultUpdate}; +use crate::rpc::domain::account_vault::{AccountVaultInfo, VaultUpdate, merge_vault_updates}; use crate::rpc::domain::note::{CommittedNote, FetchedNote, NoteSyncBlock}; use crate::rpc::domain::nullifier::NullifierUpdate; use crate::rpc::domain::status::NetworkNoteStatusInfo; -use crate::rpc::domain::storage_map::{StorageMapInfo, StorageMapUpdate}; +use crate::rpc::domain::storage_map::{ + StorageMapInfo, + StorageMapUpdate, + merge_storage_map_updates, +}; use crate::rpc::domain::sync::{ChainMmrInfo, SyncTarget}; use crate::rpc::domain::transaction::TransactionRecord; use crate::rpc::{AccountStateAt, NodeRpcClient, RpcError, RpcStatusInfo}; @@ -148,7 +152,7 @@ impl MockRpcApi { block_from: BlockNumber, block_to: BlockNumber, account_id: AccountId, - ) -> AccountVaultInfo { + ) -> (BlockNumber, BlockNumber, Vec) { let chain_tip = self.get_chain_tip_block_num(); let target_block = block_to.min(chain_tip); @@ -177,28 +181,24 @@ impl MockRpcApi { let vault_patch = patch.vault(); for asset in vault_patch.updated_assets() { - updates.push(AccountVaultUpdate { + updates.push(VaultUpdate { block_num: block_number, - asset: Some(asset), vault_key: asset.vault_key(), + asset: Some(asset), }); } for vault_key in vault_patch.removed_asset_keys() { - updates.push(AccountVaultUpdate { + updates.push(VaultUpdate { block_num: block_number, - asset: None, vault_key: *vault_key, + asset: None, }); } } } - AccountVaultInfo { - chain_tip, - block_number: page_end_block, - updates, - } + (chain_tip, page_end_block, updates) } /// Retrieves transactions in a given block range that match the provided account IDs @@ -242,7 +242,7 @@ impl MockRpcApi { block_from: BlockNumber, block_to: BlockNumber, account_id: AccountId, - ) -> StorageMapInfo { + ) -> (BlockNumber, BlockNumber, Vec) { let chain_tip = self.get_chain_tip_block_num(); let target_block = block_to.min(chain_tip); @@ -273,24 +273,19 @@ impl MockRpcApi { for (slot_name, map_patch) in storage_patch.maps() { if let Some(entries) = map_patch.entries() { for (key, value) in entries.as_map() { - let storage_map_info = StorageMapUpdate { + updates.push(StorageMapUpdate { block_num: block_number, slot_name: slot_name.clone(), key: *key, value: *value, - }; - updates.push(storage_map_info); + }); } } } } } - StorageMapInfo { - chain_tip, - block_number: page_end_block, - updates, - } + (chain_tip, page_end_block, updates) } pub fn get_available_notes(&self) -> Vec { @@ -650,19 +645,19 @@ impl NodeRpcClient for MockRpcApi { let target_block = block_to.min(chain_tip); loop { - let response = + let (page_chain_tip, page_block_number, updates) = self.get_sync_storage_maps_request(current_block_from, block_to, account_id); - all_updates.extend(response.updates); + all_updates.extend(updates); - if response.block_number >= target_block { + if page_block_number >= target_block { return Ok(StorageMapInfo { - chain_tip: response.chain_tip, - block_number: response.block_number, - updates: all_updates, + chain_tip: page_chain_tip, + block_number: page_block_number, + map_entries: merge_storage_map_updates(all_updates), }); } - current_block_from = (response.block_number.as_u32() + 1).into(); + current_block_from = (page_block_number.as_u32() + 1).into(); } } @@ -678,19 +673,19 @@ impl NodeRpcClient for MockRpcApi { let target_block = block_to.min(chain_tip); loop { - let response = + let (page_chain_tip, page_block_number, updates) = self.get_sync_account_vault_request(current_block_from, block_to, account_id); - all_updates.extend(response.updates); + all_updates.extend(updates); - if response.block_number >= target_block { + if page_block_number >= target_block { return Ok(AccountVaultInfo { - chain_tip: response.chain_tip, - block_number: response.block_number, - updates: all_updates, + chain_tip: page_chain_tip, + block_number: page_block_number, + vault_patch: merge_vault_updates(all_updates), }); } - current_block_from = (response.block_number.as_u32() + 1).into(); + current_block_from = (page_block_number.as_u32() + 1).into(); } } diff --git a/crates/rust-client/src/transaction/prover.rs b/crates/rust-client/src/transaction/prover.rs index 46ff7c0f1f..d6a6e93d54 100644 --- a/crates/rust-client/src/transaction/prover.rs +++ b/crates/rust-client/src/transaction/prover.rs @@ -3,7 +3,7 @@ use alloc::boxed::Box; use miden_protocol::transaction::{ProvenTransaction, TransactionInputs}; use miden_tx::{LocalTransactionProver, TransactionProverError}; -#[cfg(any(feature = "std", target_arch = "wasm32"))] +#[cfg(feature = "tonic")] use crate::remote_prover::RemoteTransactionProver; #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] @@ -26,7 +26,7 @@ impl TransactionProver for LocalTransactionProver { } } -#[cfg(any(feature = "std", target_arch = "wasm32"))] +#[cfg(feature = "tonic")] #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] impl TransactionProver for RemoteTransactionProver { diff --git a/crates/sqlite-store/src/account/accounts.rs b/crates/sqlite-store/src/account/accounts.rs index b9f361241f..93237034c6 100644 --- a/crates/sqlite-store/src/account/accounts.rs +++ b/crates/sqlite-store/src/account/accounts.rs @@ -36,7 +36,6 @@ use miden_client::utils::{Deserializable, Serializable}; use miden_client::{AccountError, Felt, Word}; use miden_protocol::account::{AccountStorageHeader, StorageMapWitness, StorageSlotHeader}; use miden_protocol::asset::{AssetVaultKey, PartialVault}; -use miden_protocol::crypto::merkle::MerkleError; use rusqlite::types::Value; use rusqlite::{Connection, OptionalExtension, Transaction, named_params, params}; @@ -258,7 +257,7 @@ impl SqliteStore { match smt_forest.get_asset_and_witness(header.vault_root(), vault_key) { Ok((asset, witness)) => Ok(Some((asset, witness))), - Err(StoreError::MerkleStoreError(MerkleError::UntrackedKey(_))) => Ok(None), + Err(StoreError::VaultKeyNotTracked(..)) => Ok(None), Err(err) => Err(err), } } diff --git a/crates/sqlite-store/src/account/vault.rs b/crates/sqlite-store/src/account/vault.rs index 547a11655f..e2449e50f6 100644 --- a/crates/sqlite-store/src/account/vault.rs +++ b/crates/sqlite-store/src/account/vault.rs @@ -3,10 +3,10 @@ use std::rc::Rc; use std::vec::Vec; +use miden_client::Serializable; use miden_client::account::{AccountHeader, AccountId, AccountVaultPatch}; use miden_client::asset::Asset; use miden_client::store::{AccountSmtForest, StoreError}; -use miden_client::Serializable; use miden_protocol::asset::AssetVaultKey; use miden_protocol::crypto::merkle::MerkleError; use rusqlite::types::Value; diff --git a/scripts/start-test-node.sh b/scripts/start-test-node.sh index f8ce8e4055..8be2690871 100755 --- a/scripts/start-test-node.sh +++ b/scripts/start-test-node.sh @@ -43,7 +43,7 @@ NODE_BINS=(miden-validator miden-node miden-ntx-builder miden-remote-prover) # Resolve the pinned node source from Cargo.lock: a git pin takes precedence, otherwise use the # crates.io version locked for `miden-node-proto-build`. -SRC_LINE="$(grep -m1 'source = "git+https://github.com/0xMiden/miden-node' "$ROOT/Cargo.lock" || true)" +SRC_LINE="$(grep -m1 'source = "git+https://github.com/0xMiden/node' "$ROOT/Cargo.lock" || true)" if [ -n "$SRC_LINE" ]; then NODE_SOURCE="git" SRC="${SRC_LINE#*\"git+}"; SRC="${SRC%\"}" From 761cccbd47770063b8e482c7736e56779edba067 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Mon, 13 Jul 2026 14:40:29 -0300 Subject: [PATCH 10/20] fix: apply_block Remove & Create procedures --- CHANGELOG.md | 1 + .../rust-client/src/sync/state_sync_update.rs | 32 ++++ crates/sqlite-store/src/account/storage.rs | 148 +++++++++++++-- crates/sqlite-store/src/account/tests.rs | 168 ++++++++++++++++++ 4 files changed, 330 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d4cb6e1d0..95eb943e78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Fixes +* [FIX][store] The SQLite store now honors `StorageMapPatch` create/remove semantics: a `Create` patch on an existing map slot clears the prior entries before writing (so its root reflects only the created entries) and a `Remove` patch drops the slot's entries and collapses its root to the empty-map root ([#2290](https://github.com/0xMiden/rust-sdk/pull/2290)). * [FIX][rust] Storing an authenticated block header now persists the header and its MMR authentication nodes in a single store transaction, so an interrupted write can no longer leave a tracked block without the MMR nodes needed to rebuild the `PartialMmr` ([#2294](https://github.com/0xMiden/rust-sdk/pull/2294)). * [FIX][rust] RPC endpoint parsing now rejects endpoint strings that omit either the protocol or host. ([#2266](https://github.com/0xMiden/miden-client/pull/2266)) * [FIX][rust] State sync now re-verifies a tracked private account's commitment mismatch against the witness `get_account` returns. The witness is checked against the synced block's account root before locking the account, so a node can no longer durably lock it with a forged `sync_transactions` commitment ([#2260](https://github.com/0xMiden/rust-sdk/pull/2260)). diff --git a/crates/rust-client/src/sync/state_sync_update.rs b/crates/rust-client/src/sync/state_sync_update.rs index 7e594eafc7..cab2fc2a75 100644 --- a/crates/rust-client/src/sync/state_sync_update.rs +++ b/crates/rust-client/src/sync/state_sync_update.rs @@ -633,4 +633,36 @@ mod tests { let result = payload(1, vec![], BTreeMap::new()).compute_account_patch(None); assert!(result.is_err()); } + + /// A newly created account (final nonce 1, full-state) emits each map slot as a `Create`, which + /// the store applies by starting the slot from an empty map. + #[test] + fn compute_patch_emits_map_create_for_new_account() { + let map_slot = slot_name("miden::test::map"); + let mut entries = StorageMapPatchEntries::new(); + entries.insert(StorageMapKey::from_raw(word(1)), word(100)); + let map_entries = BTreeMap::from([(map_slot.clone(), entries)]); + + let patch = payload(1, vec![], map_entries) + .compute_account_patch(Some(AccountCode::mock())) + .unwrap(); + + let map = patch.storage().get_map(&map_slot).expect("patch should contain map slot"); + assert!(matches!(map, StorageMapPatch::Create { .. })); + } + + /// An update to an existing account (final nonce > 1) emits map slots as `Update`, never + /// `Create`, so the sync path never asks the store to re-create a populated map. + #[test] + fn compute_patch_emits_map_update_for_existing_account() { + let map_slot = slot_name("miden::test::map"); + let mut entries = StorageMapPatchEntries::new(); + entries.insert(StorageMapKey::from_raw(word(1)), word(100)); + let map_entries = BTreeMap::from([(map_slot.clone(), entries)]); + + let patch = payload(2, vec![], map_entries).compute_account_patch(None).unwrap(); + + let map = patch.storage().get_map(&map_slot).expect("patch should contain map slot"); + assert!(matches!(map, StorageMapPatch::Update { .. })); + } } diff --git a/crates/sqlite-store/src/account/storage.rs b/crates/sqlite-store/src/account/storage.rs index 25eb961638..2fc803660a 100644 --- a/crates/sqlite-store/src/account/storage.rs +++ b/crates/sqlite-store/src/account/storage.rs @@ -1,6 +1,6 @@ //! Storage-related database operations for accounts. -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::rc::Rc; use std::string::ToString; use std::vec::Vec; @@ -9,6 +9,7 @@ use miden_client::account::{ AccountId, AccountStoragePatch, StorageMap, + StorageMapPatch, StorageSlot, StorageSlotContent, StorageSlotName, @@ -168,19 +169,9 @@ impl SqliteStore { let account_id_bytes = account_id.to_bytes(); let nonce_val = u64_to_value(nonce); - // Collect the patch's changed map entries for efficient lookup - let patch_map_entries: BTreeMap<&StorageSlotName, Vec<(Word, Word)>> = storage_patch - .maps() - .map(|(slot_name, map_patch)| { - let entries: Vec<(Word, Word)> = map_patch - .entries() - .into_iter() - .flat_map(|e| e.as_map().iter()) - .map(|(key, value)| ((*key).into(), *value)) - .collect(); - (slot_name, entries) - }) - .collect(); + // Look up each map slot's patch by name so the write path can honor the patch operation. + let patch_maps: BTreeMap<&StorageSlotName, &StorageMapPatch> = + storage_patch.maps().collect(); for (slot_name, (value, slot_type)) in updated_slots { let slot_name_str = slot_name.to_string(); @@ -212,15 +203,15 @@ impl SqliteStore { .execute(params![&account_id_bytes, &slot_name_str, &slot_value_hex, slot_type_val]) .into_store_error()?; - if let Some(changed_entries) = patch_map_entries.get(slot_name) { - Self::write_map_entry_delta( + if let Some(map_patch) = patch_maps.get(slot_name) { + Self::write_map_patch( tx, &mut latest_map_stmt, &mut hist_map_stmt, &account_id_bytes, &nonce_val, &slot_name_str, - changed_entries, + map_patch, )?; } } @@ -228,6 +219,117 @@ impl SqliteStore { Ok(()) } + /// Applies a single map slot's patch to the latest and historical tables. + /// + /// - `Update` layers the patch entries onto the existing map, deleting entries whose new value + /// is the empty word. + /// - `Create` and `Remove` discard the map's current contents first: every existing entry is + /// archived and removed, then the patch's entries (none, for `Remove`) are written. `Create` + /// can target an already-populated slot when merged from a remove/create pair, so it cannot + /// assume the slot starts empty. + fn write_map_patch( + tx: &Transaction<'_>, + latest_map_stmt: &mut rusqlite::CachedStatement<'_>, + hist_map_stmt: &mut rusqlite::CachedStatement<'_>, + account_id_bytes: &[u8], + nonce_val: &rusqlite::types::Value, + slot_name_str: &str, + map_patch: &StorageMapPatch, + ) -> Result<(), StoreError> { + match map_patch { + StorageMapPatch::Update { entries } => { + let changed: Vec<(Word, Word)> = + entries.as_map().iter().map(|(key, value)| ((*key).into(), *value)).collect(); + Self::write_map_entry_delta( + tx, + latest_map_stmt, + hist_map_stmt, + account_id_bytes, + nonce_val, + slot_name_str, + &changed, + ) + }, + StorageMapPatch::Create { entries } => { + let new_entries: Vec<(Word, Word)> = + entries.as_map().iter().map(|(key, value)| ((*key).into(), *value)).collect(); + Self::replace_map_entries( + tx, + latest_map_stmt, + hist_map_stmt, + account_id_bytes, + nonce_val, + slot_name_str, + &new_entries, + ) + }, + StorageMapPatch::Remove => Self::replace_map_entries( + tx, + latest_map_stmt, + hist_map_stmt, + account_id_bytes, + nonce_val, + slot_name_str, + &[], + ), + } + } + + /// Replaces all latest entries of a map slot with `new_entries`, archiving every affected key. + /// + /// Each key in the union of the slot's current keys and `new_entries` is archived exactly once + /// with its prior value (NULL if the key is new), so historical rows stay consistent. Entries + /// whose new value is the empty word are treated as absent. + fn replace_map_entries( + tx: &Transaction<'_>, + latest_map_stmt: &mut rusqlite::CachedStatement<'_>, + hist_map_stmt: &mut rusqlite::CachedStatement<'_>, + account_id_bytes: &[u8], + nonce_val: &rusqlite::types::Value, + slot_name_str: &str, + new_entries: &[(Word, Word)], + ) -> Result<(), StoreError> { + const READ_ALL_MAP_ENTRIES: &str = "SELECT key, value FROM latest_storage_map_entries WHERE account_id = ? AND slot_name = ?"; + const DELETE_ALL_MAP_ENTRIES: &str = + "DELETE FROM latest_storage_map_entries WHERE account_id = ? AND slot_name = ?"; + + let existing: BTreeMap = { + let mut read_stmt = tx.prepare_cached(READ_ALL_MAP_ENTRIES).into_store_error()?; + let rows = read_stmt + .query_map(params![account_id_bytes, slot_name_str], |row| { + Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?)) + }) + .into_store_error()?; + rows.collect::>().into_store_error()? + }; + + let new_map: BTreeMap = new_entries + .iter() + .filter(|(_, value)| *value != EMPTY_WORD) + .map(|(key, value)| (key.to_hex(), value.to_hex())) + .collect(); + + // Archive each affected key once, recording the value it held before this nonce. + let mut affected: BTreeSet<&String> = existing.keys().collect(); + affected.extend(new_map.keys()); + for key_hex in affected { + let old_value = existing.get(key_hex).cloned(); + hist_map_stmt + .execute(params![account_id_bytes, nonce_val, slot_name_str, key_hex, old_value]) + .into_store_error()?; + } + + tx.execute(DELETE_ALL_MAP_ENTRIES, params![account_id_bytes, slot_name_str]) + .into_store_error()?; + for (key_hex, value_hex) in &new_map { + latest_map_stmt + .execute(params![account_id_bytes, slot_name_str, key_hex, value_hex]) + .into_store_error()?; + } + + Ok(()) + } + /// Archives old map entry values to historical and updates latest for each changed entry. fn write_map_entry_delta( tx: &Transaction<'_>, @@ -307,7 +409,15 @@ impl SqliteStore { let default_map_root = StorageMap::default().root(); for (slot_name, map_patch) in storage_patch.maps() { - let old_root = old_map_roots.get(slot_name).copied().unwrap_or(default_map_root); + // Update layers its entries onto the existing map. Create and Remove start from an + // empty map, so Create reflects only its own entries and Remove collapses + // to the empty root. + let base_root = match map_patch { + StorageMapPatch::Update { .. } => { + old_map_roots.get(slot_name).copied().unwrap_or(default_map_root) + }, + StorageMapPatch::Create { .. } | StorageMapPatch::Remove => default_map_root, + }; let entries: Vec<_> = map_patch .entries() .into_iter() @@ -315,7 +425,7 @@ impl SqliteStore { .map(|(key, value)| (*key, *value)) .collect(); - let new_root = smt_forest.update_storage_map_nodes(old_root, entries.into_iter())?; + let new_root = smt_forest.update_storage_map_nodes(base_root, entries.into_iter())?; updated_slots.insert(slot_name.clone(), (new_root, StorageSlotType::Map)); } diff --git a/crates/sqlite-store/src/account/tests.rs b/crates/sqlite-store/src/account/tests.rs index e337009852..e0739bb683 100644 --- a/crates/sqlite-store/src/account/tests.rs +++ b/crates/sqlite-store/src/account/tests.rs @@ -2326,3 +2326,171 @@ async fn watched_status_survives_state_replacement() -> anyhow::Result<()> { Ok(()) } + +// STORAGE MAP CREATE/REMOVE PATCH TESTS +// ================================================================================================ + +/// Applies a storage patch through the low-level store helpers, bypassing `Account::apply_patch`, +/// so map create/remove semantics can be exercised directly against the store tables. +async fn apply_storage_patch_directly( + store: &SqliteStore, + account_id: AccountId, + nonce: u64, + storage_patch: AccountStoragePatch, +) -> anyhow::Result<()> { + let smt_forest = store.smt_forest.clone(); + store + .interact_with_connection(move |conn| { + let old_map_roots = + SqliteStore::get_storage_map_roots_for_patch(conn, account_id, &storage_patch)?; + let tx = conn.transaction().into_store_error()?; + let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned"); + let updated_slots = SqliteStore::apply_account_storage_patch( + &mut smt_forest, + &old_map_roots, + &storage_patch, + )?; + SqliteStore::write_storage_patch( + &tx, + account_id, + nonce, + &updated_slots, + &storage_patch, + )?; + tx.commit().into_store_error()?; + Ok(()) + }) + .await?; + Ok(()) +} + +/// Reads the latest map entries of a slot as a `key_hex -> value_hex` map. +async fn read_latest_map_entries( + store: &SqliteStore, + account_id: AccountId, + slot_name: &StorageSlotName, +) -> anyhow::Result> { + let account_id_bytes = account_id.to_bytes(); + let slot = slot_name.to_string(); + let entries = store + .interact_with_connection(move |conn| { + let mut stmt = conn + .prepare( + "SELECT key, value FROM latest_storage_map_entries \ + WHERE account_id = ? AND slot_name = ?", + ) + .into_store_error()?; + let rows = stmt + .query_map(params![account_id_bytes, slot], |r| { + Ok((r.get::<_, String>(0)?, r.get::<_, String>(1)?)) + }) + .into_store_error()?; + let map: BTreeMap = + rows.collect::>().into_store_error()?; + Ok(map) + }) + .await?; + Ok(entries) +} + +/// Reads the latest top-level value (the map root, for map slots) of a storage slot. +async fn read_slot_value( + store: &SqliteStore, + account_id: AccountId, + slot_name: &StorageSlotName, +) -> anyhow::Result { + let account_id_bytes = account_id.to_bytes(); + let slot = slot_name.to_string(); + let value = store + .interact_with_connection(move |conn| { + conn.query_row( + "SELECT slot_value FROM latest_account_storage \ + WHERE account_id = ? AND slot_name = ?", + params![account_id_bytes, slot], + |r| r.get::<_, String>(0), + ) + .into_store_error() + }) + .await?; + Ok(value) +} + +/// A `Create` patch on an already-populated map slot must discard the prior entries: the resulting +/// root and latest entries reflect only the created entries, not a merge with the old ones. +#[tokio::test] +async fn create_map_patch_replaces_existing_entries() -> anyhow::Result<()> { + let store = create_test_store().await; + let map_slot_name = StorageSlotName::new("test::create::map").expect("valid slot name"); + + // Account starts with 5 entries (keys 1..=5, values i*100). + let account = setup_account_with_map(&store, 5, &map_slot_name).await?; + let account_id = account.id(); + + // Create the map anew with a different entry set: key 1 changes value, key 6 is new, + // keys 2..=5 disappear. + let key1 = StorageMapKey::new([Felt::from(1u32), ZERO, ZERO, ZERO].into()); + let key6 = StorageMapKey::new([Felt::from(6u32), ZERO, ZERO, ZERO].into()); + let val1 = [Felt::from(999u32), ZERO, ZERO, ZERO].into(); + let val6 = [Felt::from(600u32), ZERO, ZERO, ZERO].into(); + + let mut map_entries = StorageMapPatchEntries::new(); + map_entries.insert(key1, val1); + map_entries.insert(key6, val6); + let storage_patch = AccountStoragePatch::from_entries([( + map_slot_name.clone(), + StorageSlotPatch::Map(StorageMapPatch::Create { entries: map_entries }), + )])?; + + apply_storage_patch_directly(&store, account_id, 2, storage_patch).await?; + + // Latest entries must be exactly the created set. + let latest = read_latest_map_entries(&store, account_id, &map_slot_name).await?; + let mut expected = StorageMap::new(); + expected.insert(key1, val1)?; + expected.insert(key6, val6)?; + let expected_entries: BTreeMap = + expected.entries().map(|(k, v)| (k.to_hex(), v.to_hex())).collect(); + assert_eq!(latest, expected_entries); + + // The stored root must match a map built from only the created entries. + let root_hex = read_slot_value(&store, account_id, &map_slot_name).await?; + assert_eq!(root_hex, expected.root().to_hex()); + + // Every affected key (union of old {1..5} and new {1,6}) is archived exactly once. + let m = get_storage_metrics(&store).await; + assert_eq!(m.historical_storage_map_entries, 6); + + Ok(()) +} + +/// A `Remove` patch clears the map slot: its latest entries are dropped and its root collapses to +/// the empty-map root. +#[tokio::test] +async fn remove_map_patch_clears_slot() -> anyhow::Result<()> { + let store = create_test_store().await; + let map_slot_name = StorageSlotName::new("test::remove::map").expect("valid slot name"); + + let account = setup_account_with_map(&store, 5, &map_slot_name).await?; + let account_id = account.id(); + + let storage_patch = AccountStoragePatch::from_entries([( + map_slot_name.clone(), + StorageSlotPatch::Map(StorageMapPatch::Remove), + )])?; + + apply_storage_patch_directly(&store, account_id, 2, storage_patch).await?; + + // No latest entries remain for the slot. + let latest = read_latest_map_entries(&store, account_id, &map_slot_name).await?; + assert!(latest.is_empty(), "removed map slot must have no latest entries"); + + // The root collapses to the empty-map root. + let root_hex = read_slot_value(&store, account_id, &map_slot_name).await?; + assert_eq!(root_hex, StorageMap::new().root().to_hex()); + + // The 5 prior entries are archived. + let m = get_storage_metrics(&store).await; + assert_eq!(m.historical_storage_map_entries, 5); + + Ok(()) +} From 8df24f56466327b2d458601adf68b61cabd95e1c Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Mon, 13 Jul 2026 18:30:00 -0300 Subject: [PATCH 11/20] chore: prepare for alpha release --- CHANGELOG.md | 3 +- Cargo.lock | 928 +++++++++--------- Cargo.toml | 30 +- .../tests/agglayer/agglayer_bridge_in_out.rs | 3 +- bin/integration-tests/src/tests/client.rs | 3 - bin/integration-tests/src/tests/config.rs | 5 +- .../src/tests/network_transaction.rs | 41 +- bin/integration-tests/src/tests/onchain.rs | 2 +- .../src/tests/pass_through.rs | 4 +- bin/miden-bench/src/benchmarks/transaction.rs | 2 +- bin/miden-bench/src/config.rs | 5 +- bin/miden-bench/src/deploy.rs | 2 +- bin/miden-bench/src/expand.rs | 11 +- .../src/generators/large_account.rs | 2 +- bin/miden-cli/Cargo.toml | 2 +- bin/miden-cli/build.rs | 52 +- bin/miden-cli/src/commands/call.rs | 3 +- bin/miden-cli/src/commands/new_account.rs | 4 +- bin/miden-cli/src/config.rs | 8 +- bin/miden-cli/src/lib.rs | 52 +- bin/miden-cli/src/utils.rs | 2 +- bin/miden-cli/tests/cli.rs | 146 +-- crates/rust-client/Cargo.toml | 3 +- .../rust-client/src/account/account_reader.rs | 14 +- crates/rust-client/src/account/mod.rs | 10 +- crates/rust-client/src/builder.rs | 19 +- crates/rust-client/src/lib.rs | 58 +- crates/rust-client/src/pswap/lineage.rs | 4 +- crates/rust-client/src/pswap/mod.rs | 2 +- crates/rust-client/src/pswap/store.rs | 2 +- .../src/rpc/domain/account_vault.rs | 12 +- crates/rust-client/src/rpc/domain/block.rs | 28 +- .../rust-client/src/store/data_store/mod.rs | 27 +- crates/rust-client/src/store/errors.rs | 6 +- crates/rust-client/src/store/mod.rs | 8 +- crates/rust-client/src/store/smt_forest.rs | 13 +- crates/rust-client/src/sync/state_sync.rs | 30 +- .../rust-client/src/sync/state_sync_update.rs | 15 +- crates/rust-client/src/test_utils/common.rs | 2 +- crates/rust-client/src/test_utils/mock.rs | 4 +- .../src/transaction/batch/data_store.rs | 20 +- .../src/transaction/dap_executor.rs | 35 +- crates/rust-client/src/transaction/prover.rs | 2 +- .../src/transaction/request/builder.rs | 4 +- crates/sqlite-store/src/account/accounts.rs | 4 +- crates/sqlite-store/src/account/helpers.rs | 2 +- crates/sqlite-store/src/account/tests.rs | 21 +- crates/sqlite-store/src/account/vault.rs | 15 +- crates/sqlite-store/src/lib.rs | 4 +- .../testing/miden-client-tests/src/tests.rs | 91 +- .../miden-client-tests/src/tests/batch.rs | 4 +- .../miden-client-tests/src/tests/store.rs | 22 +- .../src/tests/transaction.rs | 8 +- .../miden-client-tests/src/tests/transport.rs | 20 +- .../testing/test-node-genesis/src/agglayer.rs | 2 +- crates/testing/test-node-genesis/src/lib.rs | 2 +- .../src/rust-client/cli/cli-config.md | 2 - .../rust-client/cli/cli-troubleshooting.md | 23 +- docs/external/src/rust-client/cli/index.md | 8 - docs/external/src/rust-client/debug-output.md | 53 +- docs/external/src/rust-client/debugging.md | 10 + docs/external/src/rust-client/library.md | 1 - rust-toolchain.toml | 2 +- 63 files changed, 863 insertions(+), 1059 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95eb943e78..14ac0bd3ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Changelog -## Unreleased +## 0.16.0-alpha.1 (TBD) ### Breaking Changes +* [BREAKING][rust][cli] Removed the client debug-mode toggle: `DebugMode`, `ClientBuilder::in_debug_mode`, `Client::in_debug_mode`, the CLI `--debug` flag, and the `MIDEN_DEBUG` environment variable are gone, along with the `debug_mode` parameter of `CliClient::new`/`CliClient::from_config`. Miden VM 0.24 replaced the flag-gated `debug.*` MASM decorators with `miden::core::debug` procedures whose output the transaction executor prints by default, so the toggle no longer had anything to gate. ([#2290](https://github.com/0xMiden/rust-sdk/pull/2290)). * [BREAKING][rust] Migrated to `miden-protocol` 0.16. Transaction-level fees were removed, block-level `FeeParameters` are unchanged. The relative `AccountDelta` model was replaced by the absolute `AccountPatch` model for account updates: `TransactionResult::account_delta` is now `account_patch`, `AccountUpdateDetails::Public` now carries an `AccountPatch`, account reconstruction is done via `Account::try_from(&AccountPatch)` or `Account::apply_patch` instead of previous `apply_delta`. Re-exports changed accordingly: `AccountStorageDelta` for `AccountStoragePatch`, `StorageMapDelta` for `StorageMapPatch`, and so on. Standards APIs were updated: `AuthMethod` was removed (use the concrete auth components), `create_fungible_faucet` became `create_user_fungible_faucet`. ([#2290](https://github.com/0xMiden/rust-sdk/pull/2290)). * [BREAKING][store] The SQLite store now stores account IDs as serialized `BLOB` columns instead of hex `TEXT` ([#2309](https://github.com/0xMiden/rust-sdk/pull/2309)). * [BREAKING][param][store] `Store::insert_block_header` now takes a `nodes` argument and persists the header with its MMR authentication nodes in a single transaction; the standalone `Store::insert_partial_blockchain_nodes` is removed. Header-only inserts (e.g. genesis) pass an empty slice ([#2294](https://github.com/0xMiden/rust-sdk/pull/2294)). diff --git a/Cargo.lock b/Cargo.lock index d00d742a28..cdba3a2225 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,12 +19,12 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" -version = "0.5.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +checksum = "1973cfbc1a2daf9cf550e74e1f088c28e7f7d8c1e1418fb6c9dc5184b7e84c99" dependencies = [ - "crypto-common 0.1.7", - "generic-array", + "crypto-common", + "inout", ] [[package]] @@ -188,15 +188,6 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" -[[package]] -name = "ascii-canvas" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891" -dependencies = [ - "term", -] - [[package]] name = "assert_cmd" version = "2.2.2" @@ -276,9 +267,9 @@ dependencies = [ [[package]] name = "base16ct" -version = "0.2.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" [[package]] name = "base64" @@ -304,30 +295,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bit-set" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" - [[package]] name = "bitflags" version = "1.3.2" @@ -354,15 +321,6 @@ dependencies = [ "cpufeatures 0.3.0", ] -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "block-buffer" version = "0.12.1" @@ -470,26 +428,26 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chacha20" -version = "0.9.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" dependencies = [ "cfg-if", "cipher", - "cpufeatures 0.2.17", + "cpufeatures 0.3.0", + "rand_core 0.10.1", ] [[package]] name = "chacha20poly1305" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +checksum = "9b89e1c441e926b9c82a8d023f6e1b7ae0adcfaa7d621814e4d60789bac751cb" dependencies = [ "aead", "chacha20", "cipher", "poly1305", - "zeroize", ] [[package]] @@ -507,13 +465,13 @@ dependencies = [ [[package]] name = "cipher" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" dependencies = [ - "crypto-common 0.1.7", + "block-buffer", + "crypto-common", "inout", - "zeroize", ] [[package]] @@ -556,6 +514,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "codegen" version = "0.3.0" @@ -610,9 +574,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.6" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" [[package]] name = "constant_time_eq" @@ -645,6 +609,12 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -725,46 +695,49 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-bigint" -version = "0.5.5" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +checksum = "1a52aa3fcda4e6302a9f48734f234d35d4721b96f8fe07d073f07ce9df4f0271" dependencies = [ - "generic-array", - "rand_core 0.6.4", + "cpubits", + "ctutils", + "hybrid-array", + "num-traits", + "rand_core 0.10.1", "subtle", "zeroize", ] [[package]] name = "crypto-common" -version = "0.1.7" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ - "generic-array", - "rand_core 0.6.4", - "typenum", + "hybrid-array", + "rand_core 0.10.1", ] [[package]] -name = "crypto-common" -version = "0.2.2" +name = "ctutils" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" dependencies = [ - "hybrid-array", + "cmov", + "subtle", ] [[package]] name = "curve25519-dalek" -version = "4.1.3" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", + "cpufeatures 0.3.0", "curve25519-dalek-derive", - "digest 0.10.7", + "digest", "fiat-crypto", "rustc_version 0.4.1", "subtle", @@ -880,9 +853,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.10" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d" dependencies = [ "const-oid", "zeroize", @@ -917,26 +890,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "const-oid", - "crypto-common 0.1.7", - "subtle", -] - [[package]] name = "digest" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ - "block-buffer 0.12.1", - "crypto-common 0.2.2", + "block-buffer", + "const-oid", + "crypto-common", + "ctutils", ] [[package]] @@ -983,23 +946,24 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "ecdsa" -version = "0.16.9" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +checksum = "c0681a4fc24c767085329728d8dfba959af91228aa4610cca4f8ce317ba46ae0" dependencies = [ "der", - "digest 0.10.7", + "digest", "elliptic-curve", "rfc6979", "signature", "spki", + "zeroize", ] [[package]] name = "ed25519" -version = "2.2.3" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +checksum = "29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980a" dependencies = [ "pkcs8", "signature", @@ -1007,14 +971,15 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.2.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +checksum = "6ebaa1a2bf1290ab3bfe5a7b771d050ebffab2711c19a81691c683a5144a25de" dependencies = [ "curve25519-dalek", "ed25519", "serde", "sha2", + "signature", "subtle", "zeroize", ] @@ -1027,33 +992,25 @@ checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "elliptic-curve" -version = "0.13.8" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +checksum = "9d65aa39b3a5c1c9c1b745c9a019234bb7a21b77abcb4f4d266d706e2d577d65" dependencies = [ "base16ct", "crypto-bigint", - "digest 0.10.7", + "crypto-common", + "digest", "ff", - "generic-array", "group", "hkdf", + "hybrid-array", "pkcs8", - "rand_core 0.6.4", + "rand_core 0.10.1", "sec1", "subtle", "zeroize", ] -[[package]] -name = "ena" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1" -dependencies = [ - "log", -] - [[package]] name = "env_filter" version = "2.0.0" @@ -1124,19 +1081,19 @@ checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "ff" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +checksum = "a1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489f" dependencies = [ - "rand_core 0.6.4", + "rand_core 0.10.1", "subtle", ] [[package]] name = "fiat-crypto" -version = "0.2.9" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" [[package]] name = "figment" @@ -1184,13 +1141,10 @@ dependencies = [ [[package]] name = "flume" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" dependencies = [ - "futures-core", - "futures-sink", - "nanorand", "spin 0.9.8", ] @@ -1334,17 +1288,6 @@ dependencies = [ "windows-result", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - [[package]] name = "getrandom" version = "0.2.17" @@ -1352,10 +1295,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] [[package]] @@ -1379,8 +1320,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", + "wasm-bindgen", ] [[package]] @@ -1409,12 +1353,12 @@ dependencies = [ [[package]] name = "group" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4" dependencies = [ "ff", - "rand_core 0.6.4", + "rand_core 0.10.1", "subtle", ] @@ -1481,20 +1425,20 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hkdf" -version = "0.12.4" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" dependencies = [ "hmac", ] [[package]] name = "hmac" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -1544,11 +1488,13 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hybrid-array" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" dependencies = [ + "subtle", "typenum", + "zeroize", ] [[package]] @@ -1660,11 +1606,20 @@ checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" [[package]] name = "inout" -version = "0.1.4" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" dependencies = [ - "generic-array", + "hybrid-array", +] + +[[package]] +name = "inventory" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" +dependencies = [ + "rustversion", ] [[package]] @@ -1742,25 +1697,16 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +checksum = "93f50113171a713f4a4231ef82eb26703607139b35dcb56241f0ceab2ae1f7d8" dependencies = [ - "cfg-if", + "cpubits", "ecdsa", "elliptic-curve", - "once_cell", + "primeorder", "sha2", - "signature", -] - -[[package]] -name = "keccak" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" -dependencies = [ - "cpufeatures 0.2.17", + "wnaf", ] [[package]] @@ -1773,36 +1719,6 @@ dependencies = [ "cpufeatures 0.3.0", ] -[[package]] -name = "lalrpop" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4ebbd48ce411c1d10fb35185f5a51a7bfa3d8b24b4e330d30c9e3a34129501" -dependencies = [ - "ascii-canvas", - "bit-set", - "ena", - "itertools", - "lalrpop-util", - "petgraph 0.7.1", - "regex", - "regex-syntax", - "sha3 0.10.9", - "string_cache", - "term", - "unicode-xid", - "walkdir", -] - -[[package]] -name = "lalrpop-util" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733" -dependencies = [ - "rustversion", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1975,9 +1891,9 @@ checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "miden-ace-codegen" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd45076fe4fef71f0f8b30aa0f018eb39c3086eeb5f3cafc0e12d60cd28339e" +checksum = "b96b70c94b9dbb4a5dd9b03c52bf59d2fae254eb3fee4d9338889d8b5b82b9d9" dependencies = [ "miden-core", "miden-crypto", @@ -1986,12 +1902,14 @@ dependencies = [ [[package]] name = "miden-agglayer" -version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" +version = "0.16.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3154439a4406647fd457ac657bfaf260abc406cf2aa3743356c0199dbe2280" dependencies = [ "alloy-sol-types", "fs-err", "miden-assembly", + "miden-assembly-syntax", "miden-core", "miden-crypto", "miden-protocol", @@ -2007,14 +1925,13 @@ dependencies = [ [[package]] name = "miden-air" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f1a80330b3e3d3f98e08817dc6a5e3d90d11ab5e88aa9c0dad5d3b4202598b" +checksum = "1f1fc5bffab96b788524758d72c3457726357d1397874fac5ea841c6753b28e9" dependencies = [ "miden-ace-codegen", "miden-core", "miden-crypto", - "miden-lifted-stark", "miden-utils-indexing", "proptest", "thiserror", @@ -2023,9 +1940,9 @@ dependencies = [ [[package]] name = "miden-assembly" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8582d184360be35eb2111a99245f556f43e1066ed09192fbcd0f218c466862a5" +checksum = "0d08306e2cc2fe3fa50aef600d434ad67a780807374cbda60fe4450ca4dad37e" dependencies = [ "env_logger", "log", @@ -2041,15 +1958,13 @@ dependencies = [ [[package]] name = "miden-assembly-syntax" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffa307bc2cbd1f0cb74ed58981823f400a433900fb8f963762331fbb8389d5dc" +checksum = "2609e82bffb5d9e0d37591db837d659224dec4e82e13575daa6203280ba6c14f" dependencies = [ - "aho-corasick", "env_logger", - "lalrpop", - "lalrpop-util", "log", + "miden-assembly-syntax-cst", "miden-core", "miden-debug-types", "miden-utils-diagnostics", @@ -2064,10 +1979,23 @@ dependencies = [ "thiserror", ] +[[package]] +name = "miden-assembly-syntax-cst" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b442514653b58191f4ed0fd9ed14853d977bd44702cf04064e0eca7cc5c77b79" +dependencies = [ + "miden-debug-types", + "miden-rowan", + "miden-utils-diagnostics", + "thiserror", +] + [[package]] name = "miden-block-prover" -version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" +version = "0.16.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616b8fa0652e25c5711bcd01f6f1f50e803a01db962e5af6b4a2bd7612dfde78" dependencies = [ "miden-protocol", "thiserror", @@ -2075,7 +2003,7 @@ dependencies = [ [[package]] name = "miden-client" -version = "0.15.4" +version = "0.16.0-alpha.1" dependencies = [ "anyhow", "async-trait", @@ -2085,7 +2013,6 @@ dependencies = [ "gloo-timers", "hex", "miden-agglayer", - "miden-debug", "miden-node-proto-build", "miden-note-transport-proto-build", "miden-processor", @@ -2097,7 +2024,7 @@ dependencies = [ "miette", "prost", "prost-types", - "rand 0.9.4", + "rand 0.10.1", "serde", "serde_json", "tempfile", @@ -2114,7 +2041,7 @@ dependencies = [ [[package]] name = "miden-client-bench" -version = "0.15.4" +version = "0.16.0-alpha.1" dependencies = [ "anyhow", "assert_cmd", @@ -2123,14 +2050,14 @@ dependencies = [ "miden-client", "miden-client-sqlite-store", "predicates", - "rand 0.9.4", - "rand_chacha", + "rand 0.10.1", + "rand_chacha 0.10.0", "tokio", ] [[package]] name = "miden-client-cli" -version = "0.15.4" +version = "0.16.0-alpha.1" dependencies = [ "anyhow", "assert_cmd", @@ -2144,7 +2071,7 @@ dependencies = [ "midenc-hir-type", "miette", "predicates", - "rand 0.9.4", + "rand 0.10.1", "regex", "serde", "serial_test", @@ -2157,7 +2084,7 @@ dependencies = [ [[package]] name = "miden-client-integration-tests" -version = "0.15.4" +version = "0.16.0-alpha.1" dependencies = [ "anyhow", "assert_matches", @@ -2168,7 +2095,7 @@ dependencies = [ "miden-client-sqlite-store", "miden-protocol", "num_cpus", - "rand 0.9.4", + "rand 0.10.1", "regex", "serde", "serde_json", @@ -2180,7 +2107,7 @@ dependencies = [ [[package]] name = "miden-client-sqlite-store" -version = "0.15.4" +version = "0.16.0-alpha.1" dependencies = [ "anyhow", "async-trait", @@ -2198,23 +2125,23 @@ dependencies = [ [[package]] name = "miden-client-unit-tests" -version = "0.15.4" +version = "0.16.0-alpha.1" dependencies = [ "miden-client", "miden-client-sqlite-store", "miden-protocol", "miden-standards", "miden-testing", - "rand 0.9.4", + "rand 0.10.1", "rstest", "tokio", ] [[package]] name = "miden-core" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80657c32850817f5f67dcf114866495a4b055531778b7c26d0602646ce777eb8" +checksum = "93203254d349bb0d7b4302853331c57b66a38bb4289fd2f56bee0c624b05e1a1" dependencies = [ "derive_more", "log", @@ -2224,25 +2151,24 @@ dependencies = [ "miden-utils-core-derive", "miden-utils-indexing", "miden-utils-sync", - "num-derive", - "num-traits", "proptest", - "proptest-derive", "serde", "thiserror", ] [[package]] name = "miden-core-lib" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16410655f32f98537afc9ddf57b71cb6d1ca9d980da5f4eea164cafcaf891b2e" +checksum = "7d062b282d71d2f847bf372a070929f8238c5199b9756e36d4d11f22acd58ad1" dependencies = [ "env_logger", "fs-err", "miden-assembly", + "miden-assembly-syntax", "miden-core", "miden-crypto", + "miden-mast-package", "miden-package-registry", "miden-processor", "miden-utils-sync", @@ -2251,9 +2177,9 @@ dependencies = [ [[package]] name = "miden-crypto" -version = "0.25.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35198bebd353cddc25ad4aafb5f4ef9e71b283d71c787b8938c575c16974135d" +checksum = "afd70d37ade91ce0f37a0ad7b9f1709391657243857d5d209ec62a100e358d4d" dependencies = [ "blake3", "cc", @@ -2280,14 +2206,12 @@ dependencies = [ "p3-maybe-rayon", "p3-symmetric", "p3-util", - "rand 0.9.4", - "rand_chacha", - "rand_core 0.9.5", - "rand_hc", + "rand 0.10.1", + "rand_chacha 0.10.0", "rayon", "serde", "sha2", - "sha3 0.10.9", + "sha3 0.12.0", "subtle", "thiserror", "x25519-dalek", @@ -2295,9 +2219,9 @@ dependencies = [ [[package]] name = "miden-crypto-derive" -version = "0.25.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9068c6554db0e051f62913575de9949841a46b96ae92d4b7d28e1fed5d8f052b" +checksum = "9197f2837af387b5a9b60ef3c30670cf34a453c3a8e81f9cf68df84ca3122253" dependencies = [ "quote", "syn 2.0.118", @@ -2305,9 +2229,9 @@ dependencies = [ [[package]] name = "miden-debug" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c581bad965d49515f04ca370b4a20e8987f8c6051e39ac290a3128844e602ac" +checksum = "d3f9a821ae2e7f30b007ac13a747b0713fe0d5b5c23c7fc81320de827fa25b10" dependencies = [ "clap", "compact_str", @@ -2319,7 +2243,10 @@ dependencies = [ "miden-debug-engine", "miden-debug-types", "miden-mast-package", + "miden-package-registry", "miden-processor", + "miden-project", + "pyo3-build-config", "thiserror", "tokio", "tokio-util", @@ -2327,9 +2254,9 @@ dependencies = [ [[package]] name = "miden-debug-dap" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15af4ae2bc3fa05c24447260849ed4a314ec314f4009202d086e4df22076b9e8" +checksum = "f76e7d4b6570ae61af7bd5e6bd3da052dcd6ae47c87ff212175c2be618f77eec" dependencies = [ "serde", "serde_json", @@ -2338,12 +2265,13 @@ dependencies = [ [[package]] name = "miden-debug-engine" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67dd805d5456e0c00c8d89bd9ce16105f55a55ca3bee8b67459e554588d0dbb2" +checksum = "27c7cdb32831b608fa8f23a37d935e18ff84dee10d54c6e0d0f162f7fba8eb5a" dependencies = [ "clap", "glob", + "inventory", "log", "miden-assembly", "miden-assembly-syntax", @@ -2351,9 +2279,12 @@ dependencies = [ "miden-debug-dap", "miden-debug-types", "miden-mast-package", + "miden-package-registry", "miden-processor", + "miden-project", "num-traits", "rustc-demangle", + "rustc-hash", "serde", "serde_json", "smallvec", @@ -2364,9 +2295,9 @@ dependencies = [ [[package]] name = "miden-debug-types" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956708ccb2f643db398b4b3d4f8d0baf199b1bfb5e34c8be1cd1bc811c005e8e" +checksum = "0f75a72da10ddadeb9a56542b675431b9cb93f5f25ea180ff8f0c311aafee922" dependencies = [ "memchr", "miden-crypto", @@ -2383,9 +2314,9 @@ dependencies = [ [[package]] name = "miden-field" -version = "0.25.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379a39db52cd932a95d4017a18b712ee53ed0f86cfedf8c63ed72d687a18a191" +checksum = "45929f2e6b18f6535cfa25a55f5dac8be556410a4fc5c7e003a147b678af9e18" dependencies = [ "miden-serde-utils", "num-bigint", @@ -2411,11 +2342,12 @@ dependencies = [ [[package]] name = "miden-lifted-air" -version = "0.25.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "789e0e469d1731012d8a018057317f31580611535c20d2a47c022213228cb733" +checksum = "320d7e0f1ac4a668682146cce69585ddc8dfe1fdf6ba5dac1986233e3e48d4df" dependencies = [ "p3-air", + "p3-challenger", "p3-field", "p3-matrix", "p3-util", @@ -2424,9 +2356,9 @@ dependencies = [ [[package]] name = "miden-lifted-stark" -version = "0.25.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f62cca91182917b22a47e150028b7c785df620a15b2974a39c64e2b1b7a889d3" +checksum = "8075f037bb02eb763223bc2f762a55c793f3795677f88fc06e58641091fd69e0" dependencies = [ "miden-lifted-air", "miden-stark-transcript", @@ -2447,10 +2379,11 @@ dependencies = [ [[package]] name = "miden-mast-package" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f37c21836b40785ce297d363c57740d4e33edcc411f84185a524eddadd5f53c7" +checksum = "942d744f2606ad9d146fc9d817fccc5913d9cc8b8b9e53666779f6c415a174ce" dependencies = [ + "log", "miden-assembly-syntax", "miden-core", "miden-debug-types", @@ -2496,8 +2429,8 @@ dependencies = [ [[package]] name = "miden-node-proto-build" -version = "0.15.1" -source = "git+https://github.com/0xMiden/node.git?branch=next#ad3b1f8cdc8a8120ffb6419b42c3fcd18811475c" +version = "0.16.0-alpha.1" +source = "git+https://github.com/0xMiden/node.git?tag=v0.16.0-alpha.1#895091d36d4bfdb7ee8040dad449bb5db5219272" dependencies = [ "build-rs", "codegen", @@ -2521,9 +2454,9 @@ dependencies = [ [[package]] name = "miden-package-registry" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ece6064beb0582d1c64ba30d0c548c4b7a45f87abae6d69e22fd49c8b343258" +checksum = "01202970e634f258502ed082d776c906289b6394eb5ac6b8067be0d7261a55a3" dependencies = [ "miden-assembly-syntax", "miden-core", @@ -2537,14 +2470,15 @@ dependencies = [ [[package]] name = "miden-processor" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea972ca9e45dbf26aa396367e8508db0f7292adea6f6ddf8d39d0e334285fe2b" +checksum = "d2e4776a16e18c5317ae31cfb149f30a5b454a25b6e6fefd3ccee943227514db" dependencies = [ "itertools", "miden-air", "miden-core", "miden-debug-types", + "miden-mast-package", "miden-utils-diagnostics", "miden-utils-indexing", "paste", @@ -2555,9 +2489,9 @@ dependencies = [ [[package]] name = "miden-project" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5320e7e5b562359bd6161ac752dfe43dd4f69bb06e87f94a21a27bb656e5a20d" +checksum = "7de67303b1f197ad49f023fad38ca1c4be8fb55ef6098c95f254b09a8ad6b040" dependencies = [ "miden-assembly-syntax", "miden-core", @@ -2572,12 +2506,13 @@ dependencies = [ [[package]] name = "miden-protocol" -version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" +version = "0.16.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b5fd2fec04e0651a9ad7886541afc1bd6d8ee0de91a2850e9f4ead829c0fe3f" dependencies = [ "bech32", "fs-err", - "getrandom 0.3.4", + "getrandom 0.4.3", "miden-assembly", "miden-assembly-syntax", "miden-core", @@ -2585,11 +2520,12 @@ dependencies = [ "miden-crypto", "miden-crypto-derive", "miden-mast-package", + "miden-package-registry", "miden-processor", "miden-utils-sync", "miden-verifier", - "rand 0.9.4", - "rand_chacha", + "rand 0.10.1", + "rand_chacha 0.10.0", "rand_xoshiro", "regex", "semver 1.0.28", @@ -2601,24 +2537,35 @@ dependencies = [ [[package]] name = "miden-prover" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a91bcc00840b01126cd54e3b68ce3235def2ed48803f2eeb36a035d6951fbc1" +checksum = "a18511f219eeb6decc384f8a39d1428ed6d3eb313b3ff778a988635f61b84ece" dependencies = [ - "bincode", "miden-air", "miden-core", "miden-crypto", "miden-processor", "serde", + "serde-wincode", "tracing", + "wincode", +] + +[[package]] +name = "miden-rowan" +version = "0.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13695bf99aabaa21d6572b807c66bb26251aa3d9b75e828b3c99b97a3b1ce7e" +dependencies = [ + "hashbrown 0.17.1", + "rustc-hash", ] [[package]] name = "miden-serde-utils" -version = "0.25.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78cd1d4fcad937312e544f7d53423485e453598aa4fb989d2b6374027a8c136" +checksum = "6bf6872ac0f2de384ac65968e9bdc24656b31c487ea3bb1665871095c10f11ee" dependencies = [ "p3-field", "p3-goldilocks", @@ -2626,15 +2573,19 @@ dependencies = [ [[package]] name = "miden-standards" -version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" +version = "0.16.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f431c2bcd8b63ee45d12155565a23a9abc78240663d275e0e95eb475c9ac6438" dependencies = [ "bon", "fs-err", "miden-assembly", "miden-core-lib", + "miden-mast-package", + "miden-package-registry", + "miden-project", "miden-protocol", - "rand 0.9.4", + "rand 0.10.1", "regex", "thiserror", "walkdir", @@ -2642,9 +2593,9 @@ dependencies = [ [[package]] name = "miden-stark-transcript" -version = "0.25.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05901db2e30d3954243960fe21cea7fbec39f97c27774b56fd5031c28c4881ba" +checksum = "13f2f5cc37a6b24ef2ab4c8d4dfb874a8f55add5147bfad47fde901f03165a51" dependencies = [ "p3-challenger", "p3-field", @@ -2654,9 +2605,9 @@ dependencies = [ [[package]] name = "miden-stateful-hasher" -version = "0.25.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faeb47a90c55c5d45051d23cf691588804dd531995b4582c79108b64e445a905" +checksum = "93a95d46e93b94d82e0fcc6cec67ea362a548c1b6209bfdd1211433537314b83" dependencies = [ "p3-field", "p3-symmetric", @@ -2664,8 +2615,9 @@ dependencies = [ [[package]] name = "miden-testing" -version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" +version = "0.16.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610ba28ab9d73da0fcde6aa2389b2029e096df0c7248fde558d7d44be67ec8f7" dependencies = [ "anyhow", "itertools", @@ -2677,15 +2629,16 @@ dependencies = [ "miden-standards", "miden-tx", "miden-tx-batch", - "rand 0.9.4", - "rand_chacha", + "rand 0.10.1", + "rand_chacha 0.10.0", "thiserror", ] [[package]] name = "miden-tx" -version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" +version = "0.16.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eff35d682e8e4266bbe314e3b95f98ce5fea515bdc80573b907daaba937e8c1" dependencies = [ "miden-processor", "miden-protocol", @@ -2696,8 +2649,9 @@ dependencies = [ [[package]] name = "miden-tx-batch" -version = "0.16.0" -source = "git+https://github.com/0xMiden/protocol.git?branch=next#96878f9fba1648380556996a38978ce81ac625f0" +version = "0.16.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bec9ae6f93ad985a86999e81cb90a7e11090a7ea25eed661f85e384bee2bb9" dependencies = [ "miden-processor", "miden-protocol", @@ -2708,9 +2662,9 @@ dependencies = [ [[package]] name = "miden-utils-core-derive" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0b1ee4662beb049a824e11bb21f95a79746c52874967983c9999f1b19a2f471" +checksum = "dd084f8c9b3fbd173536a794850e1f29789eaeaff0f87e7852505f0fa9aa8556" dependencies = [ "proc-macro2", "quote", @@ -2719,11 +2673,10 @@ dependencies = [ [[package]] name = "miden-utils-diagnostics" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fdc1cd4eda372e1c4b99b9c3677e9b1f87a4d2e362a9f4b8f904273d395efc9" +checksum = "048bbc18d446d1bc29b5e3ceac69000f1488a0ea1b15b18f48a0c702c2c1e706" dependencies = [ - "miden-crypto", "miden-debug-types", "miden-miette", "tracing", @@ -2731,9 +2684,9 @@ dependencies = [ [[package]] name = "miden-utils-indexing" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31444125649f4dad9cde647f614309b6be4f918fed276ada4eb99c01e8b9ca7" +checksum = "98a713931678d5cdc23ab388cda048cc055079767780799b1d56f35b14ae6957" dependencies = [ "miden-crypto", "proptest", @@ -2743,9 +2696,9 @@ dependencies = [ [[package]] name = "miden-utils-sync" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "807c8ae625b7652ae7246b225c907c05da72927c31a0fd71c835c4f80931e92e" +checksum = "0a7fe110d6c5a90a27747d051c6d20cbbbc2d425c4276e96ce7bf82270d23cc3" dependencies = [ "lock_api", "loom", @@ -2755,24 +2708,25 @@ dependencies = [ [[package]] name = "miden-verifier" -version = "0.23.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec5556dac919a1c13edeb2bd7181fc6a4c2ce52764a3e518bcdcd9ed48e5b38e" +checksum = "c5bbb6c5af707266e4844460f5b55fc3c80d96a6a80b5e2a479d3cc95ad6d483" dependencies = [ - "bincode", "miden-air", "miden-core", "miden-crypto", "serde", + "serde-wincode", "thiserror", "tracing", + "wincode", ] [[package]] name = "midenc-hir-type" -version = "0.6.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff0511aa2201f7098995e38a3c97a319d379c3b2d26fb83677b21b71f61a7b4" +checksum = "f67fe32b429d499d0ae713e044b593d866b1ab51540de6ca8881413aba42e858" dependencies = [ "miden-formatting", "miden-serde-utils", @@ -2838,21 +2792,6 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" -[[package]] -name = "nanorand" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" -dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -2901,17 +2840,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - [[package]] name = "num-integer" version = "0.1.46" @@ -2988,12 +2916,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "openssl-probe" version = "0.2.1" @@ -3014,9 +2936,9 @@ checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" [[package]] name = "p3-air" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c824e8d7c7ddf208b742eac8d48e0b2d52d22fa013578a7762bf6931dbab1f46" +checksum = "a3e18b22bf85451382098e54da526733c7f390f83d4141b3b8b93e98e805df55" dependencies = [ "p3-field", "p3-matrix", @@ -3025,9 +2947,9 @@ dependencies = [ [[package]] name = "p3-blake3" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2733229a713bd83ccf5eb749e8f8e7380c1052674394a25c0422a772204a20af" +checksum = "1be4f62b2e8cc5c9b2bc0bfb0b1a98dcaaa2f5c25b167f04c8ac6123537a604b" dependencies = [ "blake3", "p3-symmetric", @@ -3036,9 +2958,9 @@ dependencies = [ [[package]] name = "p3-challenger" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8972ccd1d5dc90e46cdb1f2ab4ee2bae49b3917e5e98aa533f0c2b779c010445" +checksum = "272b2cfdcd3cf1affeab292aa7888ed7e4c156e0d5ffc19612c86553941db178" dependencies = [ "p3-field", "p3-maybe-rayon", @@ -3050,24 +2972,24 @@ dependencies = [ [[package]] name = "p3-dft" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17771aca44632f9cc11f2718d7ea7ec06794946c4190ef3a985bfc893f14c18a" +checksum = "1dc420990b39aa4cdb78ac248d5defe97f760c1056aeceb160dcf3e62baf4eb7" dependencies = [ "itertools", "p3-field", "p3-matrix", "p3-maybe-rayon", "p3-util", - "spin 0.10.0", + "spin 0.12.1", "tracing", ] [[package]] name = "p3-field" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3eb24d0591fd4d282d89cbe4e4efba5571c699375006f80b2cbf53ce83461c" +checksum = "ab37408c6c964e7e18c2305a15e31bc784aa20eb82eb513b4850aaa13ee445d3" dependencies = [ "itertools", "num-bigint", @@ -3081,9 +3003,9 @@ dependencies = [ [[package]] name = "p3-goldilocks" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5751c6591a0d2397d726620c2c29a7436ec6c5e19d2ed74ca5d078d4fbb18eb5" +checksum = "cdf8c89d8e833f93c488bd207786cd2d525a9f93de7f00363bca5652c88110a4" dependencies = [ "num-bigint", "p3-challenger", @@ -3101,9 +3023,9 @@ dependencies = [ [[package]] name = "p3-keccak" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a7df174ff0c19a8742eb4698eaa1667c5f858d018e2faf09c55f1f24a6f9c3" +checksum = "1868cb9c763e5786f89d531e15b9899f9dd3ba96768645294b99fd9df861b530" dependencies = [ "p3-symmetric", "p3-util", @@ -3112,9 +3034,9 @@ dependencies = [ [[package]] name = "p3-matrix" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea9c94c0714944e7b8a9a62e6340b1e3e1d3f8ecfd3e35c08798360200e73eff" +checksum = "a7c5e32dd61fa6341f8ce7abe2a97e9c3f8a93d9ea17bf4f675a2f6c987b658e" dependencies = [ "itertools", "p3-field", @@ -3127,18 +3049,18 @@ dependencies = [ [[package]] name = "p3-maybe-rayon" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebc233a34b1ab0273f35b4052fa2eeb3114b22ba4575bd7da00716e878ffb77" +checksum = "49c17330c94d04221bafe310d2c13afedd9d26bb57a63ec088b68b6cfe64b357" dependencies = [ "rayon", ] [[package]] name = "p3-mds" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b5441fa8116246ec9e6c835f15273cb27777ca572960ec87476b67fef13e01e" +checksum = "09ea6f54d4436d44f14841f78238b79199b83ea983a4a2fa3c4d16202f28f119" dependencies = [ "p3-dft", "p3-field", @@ -3149,9 +3071,9 @@ dependencies = [ [[package]] name = "p3-monty-31" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8724f330ea6d19dd4f2436aa0f88b5fcbf88f0f55ca7fccd3fea8b736dbcddad" +checksum = "12d3acd0d5f0ba2e8a1c903e80c92e778368b68c2e551a6c30fc3982a4be023e" dependencies = [ "itertools", "num-bigint", @@ -3167,26 +3089,27 @@ dependencies = [ "paste", "rand 0.10.1", "serde", - "spin 0.10.0", + "spin 0.12.1", "tracing", ] [[package]] name = "p3-poseidon1" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e2a562fea210baae390a32f9ecf0dd8724ae3f4352d1c8e413077b6f00a162" +checksum = "9cccfe593f85f87681f885c32f1447ea7afe12b3c9b11b4149c4534edacacb51" dependencies = [ "p3-field", + "p3-mds", "p3-symmetric", "rand 0.10.1", ] [[package]] name = "p3-poseidon2" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06394851c161d17e4aa4ad2aad5557d32f14cadd1dc838f965d8e1821a63b8c5" +checksum = "d28bb6596f38c493d7a590e3b7d30eae86cbcfc325fb44f652ec3540933bcaa0" dependencies = [ "p3-field", "p3-mds", @@ -3197,9 +3120,9 @@ dependencies = [ [[package]] name = "p3-symmetric" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac1a276d421f8ef3361bb7d8c39a02c93c6b3f10eeaa559cc4c50222f9a5b82" +checksum = "284db1f284c4007eeb65cef4656426c93192af9bd9703b7f3afbf7faab274500" dependencies = [ "itertools", "p3-field", @@ -3209,9 +3132,9 @@ dependencies = [ [[package]] name = "p3-util" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d08a58162a4c264269ef454f0b28dcda89939490eecacb2b2cf5b00f719b80f6" +checksum = "f241f0faa735a03b1af8c923b4728a9fac5e4a26e573a087656c354a784dfcae" dependencies = [ "rayon", "serde", @@ -3247,6 +3170,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pastey" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" + [[package]] name = "pear" version = "0.2.9" @@ -3276,16 +3205,6 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" -[[package]] -name = "petgraph" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" -dependencies = [ - "fixedbitset", - "indexmap", -] - [[package]] name = "petgraph" version = "0.8.3" @@ -3297,15 +3216,6 @@ dependencies = [ "indexmap", ] -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", -] - [[package]] name = "pin-project" version = "1.1.13" @@ -3334,9 +3244,9 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkcs8" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7" dependencies = [ "der", "spki", @@ -3350,12 +3260,11 @@ checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "poly1305" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +checksum = "6e2d0073b297041425c7c3df6eb4792d598a15323fe63346852b092eca02904c" dependencies = [ - "cpufeatures 0.2.17", - "opaque-debug", + "cpufeatures 0.3.0", "universal-hash", ] @@ -3383,12 +3292,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - [[package]] name = "predicates" version = "3.1.4" @@ -3429,6 +3332,32 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "primefield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c555a6e4eb7d4e158fcb028c835c3b8642206ddc279b5c6b202ef9a8bdb592f4" +dependencies = [ + "crypto-bigint", + "crypto-common", + "ff", + "rand_core 0.10.1", + "subtle", + "zeroize", +] + +[[package]] +name = "primeorder" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c9f42978c78a00e3d68f69fc03e57a234debae69da4020a4fb588fcdcd07b06" +dependencies = [ + "elliptic-curve", + "primefield", + "serdect", + "wnaf", +] + [[package]] name = "primitive-types" version = "0.14.0" @@ -3512,7 +3441,7 @@ dependencies = [ "bitflags 2.13.0", "num-traits", "rand 0.9.4", - "rand_chacha", + "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", "unarray", @@ -3549,7 +3478,7 @@ dependencies = [ "itertools", "log", "multimap", - "petgraph 0.8.3", + "petgraph", "prettyplease", "prost", "prost-types", @@ -3655,6 +3584,15 @@ dependencies = [ "pulldown-cmark", ] +[[package]] +name = "pyo3-build-config" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e368e7ddfdeb98c9bca7f8383be1648fd84ab466bf2bc015e94008db6d35611e" +dependencies = [ + "target-lexicon", +] + [[package]] name = "quote" version = "1.0.46" @@ -3691,7 +3629,7 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ - "rand_chacha", + "rand_chacha 0.9.0", "rand_core 0.9.5", ] @@ -3701,6 +3639,8 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ + "chacha20", + "getrandom 0.4.3", "rand_core 0.10.1", ] @@ -3714,14 +3654,21 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand_chacha" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e6af7f3e25ded52c41df4e0b1af2d047e45896c2f3281792ed68a1c243daedb" +dependencies = [ + "ppv-lite86", + "rand_core 0.10.1", +] + [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] [[package]] name = "rand_core" @@ -3738,15 +3685,6 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" -[[package]] -name = "rand_hc" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b363d4f6370f88d62bf586c80405657bde0f0e1b8945d47d2ad59b906cb4f54" -dependencies = [ - "rand_core 0.6.4", -] - [[package]] name = "rand_xorshift" version = "0.4.0" @@ -3758,11 +3696,11 @@ dependencies = [ [[package]] name = "rand_xoshiro" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" +checksum = "662effc7698e08ea324d3acccf8d9d7f7bf79b9785e270a174ea36e56900c91d" dependencies = [ - "rand_core 0.9.5", + "rand_core 0.10.1", ] [[package]] @@ -3842,12 +3780,12 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "rfc6979" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +checksum = "b4a459cddafb3fe76b31fd8f1108007566c40301feb64dc7b54656eb7388172b" dependencies = [ + "crypto-bigint", "hmac", - "subtle", ] [[package]] @@ -4073,14 +4011,14 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sec1" -version = "0.7.3" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d" dependencies = [ "base16ct", + "ctutils", "der", - "generic-array", - "pkcs8", + "hybrid-array", "subtle", "zeroize", ] @@ -4155,6 +4093,17 @@ dependencies = [ "typeid", ] +[[package]] +name = "serde-wincode" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa9d3a86c66cf10ce79df36f555a5a4c8d72a82515d9ea8ca420e02c925c30f" +dependencies = [ + "serde", + "thiserror", + "wincode", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -4217,6 +4166,16 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serdect" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "serial_test" version = "3.5.0" @@ -4245,33 +4204,34 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.9" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", - "digest 0.10.7", + "cpufeatures 0.3.0", + "digest", ] [[package]] name = "sha3" -version = "0.10.9" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" dependencies = [ - "digest 0.10.7", - "keccak 0.1.6", + "digest", + "keccak", ] [[package]] name = "sha3" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" +checksum = "bc9bad02c26382724b2d2692c6f179285e4b54eeecd7968f52a50059c3c11759" dependencies = [ - "digest 0.11.3", - "keccak 0.2.0", + "digest", + "keccak", + "sponge-cursor", ] [[package]] @@ -4291,20 +4251,14 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signature" -version = "2.2.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", + "digest", + "rand_core 0.10.1", ] -[[package]] -name = "siphasher" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" - [[package]] name = "slab" version = "0.4.12" @@ -4347,23 +4301,29 @@ dependencies = [ [[package]] name = "spin" -version = "0.10.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +checksum = "bd5231412d905519dca6a5deb0327d407be68d6c941feec004533401d3a0a715" dependencies = [ "lock_api", ] [[package]] name = "spki" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f" dependencies = [ "base64ct", "der", ] +[[package]] +name = "sponge-cursor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a0219bd7d979d58245a4f41f695e1ac9f8befdffadd7f61f1bae9e39abc6620" + [[package]] name = "static_assertions" version = "1.1.0" @@ -4376,18 +4336,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" -[[package]] -name = "string_cache" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" -dependencies = [ - "new_debug_unreachable", - "parking_lot", - "phf_shared", - "precomputed-hash", -] - [[package]] name = "strip-ansi-escapes" version = "0.2.1" @@ -4470,6 +4418,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +[[package]] +name = "target-lexicon" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + [[package]] name = "target-triple" version = "1.0.0" @@ -4483,21 +4437,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.3", + "getrandom 0.3.4", "once_cell", "rustix", "windows-sys 0.61.2", ] -[[package]] -name = "term" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "termcolor" version = "1.4.1" @@ -4525,14 +4470,14 @@ checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "test-node-genesis" -version = "0.15.4" +version = "0.16.0-alpha.1" dependencies = [ "anyhow", "miden-agglayer", "miden-protocol", "miden-standards", - "rand 0.9.4", - "rand_chacha", + "rand 0.10.1", + "rand_chacha 0.10.0", ] [[package]] @@ -5088,12 +5033,12 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "universal-hash" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +checksum = "f4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96" dependencies = [ - "crypto-common 0.1.7", - "subtle", + "crypto-common", + "ctutils", ] [[package]] @@ -5308,6 +5253,18 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "wincode" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d967db7705dc29120bb6e8ce5b5a2e27734ed5976d1c904e95bd238d1c3c5a" +dependencies = [ + "pastey", + "proc-macro2", + "quote", + "thiserror", +] + [[package]] name = "windows-core" version = "0.62.2" @@ -5473,14 +5430,25 @@ version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +[[package]] +name = "wnaf" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab12e7090f27e2ffd9322651492942d50c2926094af30601e1964337db39daf1" +dependencies = [ + "ff", + "group", + "hybrid-array", +] + [[package]] name = "x25519-dalek" -version = "2.0.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +checksum = "e7e8131a03190127fb2263afc72b322ecadae46b6ff8c6f399ff5d02f5559af6" dependencies = [ "curve25519-dalek", - "rand_core 0.6.4", + "rand_core 0.10.1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 84d3d9238c..3d18068be5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,8 +24,8 @@ authors = ["miden contributors"] edition = "2024" license = "MIT" repository = "https://github.com/0xMiden/rust-sdk" -rust-version = "1.93" -version = "0.15.4" +rust-version = "1.96" +version = "0.16.0-alpha.1" [profile.dev] codegen-units = 16 @@ -36,24 +36,24 @@ opt-level = 1 [workspace.dependencies] # Workspace crates -miden-client = { default-features = false, path = "crates/rust-client", version = "0.15.3" } -miden-client-sqlite-store = { default-features = false, path = "crates/sqlite-store", version = "0.15.3" } +miden-client = { default-features = false, path = "crates/rust-client", version = "0.16.0-alpha.1" } +miden-client-sqlite-store = { default-features = false, path = "crates/sqlite-store", version = "0.16.0-alpha.1" } # Miden protocol dependencies -miden-agglayer = { branch = "next", default-features = false, git = "https://github.com/0xMiden/protocol.git" } -miden-protocol = { branch = "next", default-features = false, git = "https://github.com/0xMiden/protocol.git" } -miden-standards = { branch = "next", default-features = false, git = "https://github.com/0xMiden/protocol.git" } -miden-testing = { branch = "next", default-features = false, git = "https://github.com/0xMiden/protocol.git" } -miden-tx = { branch = "next", default-features = false, git = "https://github.com/0xMiden/protocol.git" } -miden-tx-batch = { branch = "next", default-features = false, git = "https://github.com/0xMiden/protocol.git" } +miden-agglayer = { default-features = false, version = "=0.16.0-alpha.2" } +miden-protocol = { default-features = false, version = "=0.16.0-alpha.2" } +miden-standards = { default-features = false, version = "=0.16.0-alpha.2" } +miden-testing = { default-features = false, version = "=0.16.0-alpha.2" } +miden-tx = { default-features = false, version = "=0.16.0-alpha.2" } +miden-tx-batch = { default-features = false, version = "=0.16.0-alpha.2" } # Miden node dependencies -miden-node-proto-build = { branch = "next", default-features = false, git = "https://github.com/0xMiden/node.git" } +miden-node-proto-build = { default-features = false, git = "https://github.com/0xMiden/node.git", tag = "v0.16.0-alpha.1" } # TODO: replace with crates.io on node has proper release miden-note-transport-proto-build = { default-features = false, version = "0.4.1" } # Miden debug dependency -miden-debug = { default-features = false, features = ["dap", "std"], version = "0.8" } -miden-processor = { default-features = false, version = "0.23" } +miden-debug = { default-features = false, features = ["dap", "std"], version = "0.9" } +miden-processor = { default-features = false, version = "0.25" } # External dependencies anyhow = { default-features = false, version = "1.0" } @@ -66,8 +66,8 @@ hex = { version = "0.4" } miette = { features = ["fancy"], version = "7.6" } predicates = { version = "3.0" } prost = { default-features = false, version = "0.14" } -rand = { version = "0.9" } -rand_chacha = { version = "0.9" } +rand = { version = "0.10" } +rand_chacha = { version = "0.10" } regex = { version = "1.0" } rstest = { version = "0.25" } serde = { features = ["derive"], version = "1.0" } diff --git a/bin/integration-tests/src/tests/agglayer/agglayer_bridge_in_out.rs b/bin/integration-tests/src/tests/agglayer/agglayer_bridge_in_out.rs index 1cb1751d8b..b3480b8dff 100644 --- a/bin/integration-tests/src/tests/agglayer/agglayer_bridge_in_out.rs +++ b/bin/integration-tests/src/tests/agglayer/agglayer_bridge_in_out.rs @@ -36,7 +36,7 @@ use miden_agglayer::{ }; use miden_client::Felt; use miden_client::account::AccountType; -use miden_client::asset::{Asset, AssetCallbackFlag, FungibleAsset}; +use miden_client::asset::{Asset, FungibleAsset}; use miden_client::auth::RPO_FALCON_SCHEME_ID; use miden_client::crypto::FeltRng; use miden_client::note::NoteAssets; @@ -297,7 +297,6 @@ pub async fn test_agglayer_bridge_in_out(client_config: ClientConfig) -> Result< // callbacks-disabled key and fails to remove it from the vault. let bridge_asset: Asset = FungibleAsset::new(agglayer_faucet_id, BRIDGE_OUT_AMOUNT) .map_err(|e| anyhow::anyhow!("{e}"))? - .with_callbacks(AssetCallbackFlag::Enabled) .into(); let b2agg_note = B2AggNote::create( L1_NETWORK_ID, diff --git a/bin/integration-tests/src/tests/client.rs b/bin/integration-tests/src/tests/client.rs index c34298971a..fc7b84d55b 100644 --- a/bin/integration-tests/src/tests/client.rs +++ b/bin/integration-tests/src/tests/client.rs @@ -65,12 +65,9 @@ pub async fn test_client_builder_initializes_client_with_endpoint( .grpc_client(&endpoint, Some(10_000)) .filesystem_keystore(auth_path)? .sqlite_store(store_config) - .in_debug_mode(miden_client::DebugMode::Enabled) .build() .await?; - assert!(client.in_debug_mode()); - let sync_summary = client.sync_state().await?; assert!(sync_summary.block_num.as_u32() > 0); diff --git a/bin/integration-tests/src/tests/config.rs b/bin/integration-tests/src/tests/config.rs index ac7bce5f94..6a429afb8f 100644 --- a/bin/integration-tests/src/tests/config.rs +++ b/bin/integration-tests/src/tests/config.rs @@ -15,9 +15,9 @@ use miden_client::note_transport::{ }; use miden_client::rpc::{Endpoint, GrpcClient}; use miden_client::testing::common::{FilesystemKeyStore, TestClient, create_test_store_path}; -use miden_client::{DebugMode, Felt, RemoteTransactionProver}; +use miden_client::{Felt, RemoteTransactionProver}; use miden_client_sqlite_store::ClientBuilderSqliteExt; -use rand::Rng; +use rand::RngExt; use uuid::Uuid; const NETWORK_DEVNET: &str = "devnet"; @@ -150,7 +150,6 @@ impl ClientConfig { .rng(Box::new(rng)) .sqlite_store(store_config) .authenticator(Arc::new(keystore.clone())) - .in_debug_mode(DebugMode::Disabled) .tx_discard_delta(None); if let Some(prover_url) = &self.prover_endpoint { diff --git a/bin/integration-tests/src/tests/network_transaction.rs b/bin/integration-tests/src/tests/network_transaction.rs index 19151c275b..eed7f327f9 100644 --- a/bin/integration-tests/src/tests/network_transaction.rs +++ b/bin/integration-tests/src/tests/network_transaction.rs @@ -24,7 +24,7 @@ use miden_client::account::{ StorageSlot, StorageSlotName, }; -use miden_client::assembly::{CodeBuilder, Library, Module, ModuleKind, Path, SourceManagerSync}; +use miden_client::assembly::{CodeBuilder, SourceManagerSync}; use miden_client::asset::{AssetAmount, FungibleAsset, TokenSymbol}; use miden_client::auth::RPO_FALCON_SCHEME_ID; use miden_client::crypto::FeltRng; @@ -59,9 +59,9 @@ use miden_client::testing::common::{ wait_for_blocks, wait_for_tx, }; -use miden_client::transaction::{TransactionKernel, TransactionRequestBuilder}; +use miden_client::transaction::TransactionRequestBuilder; use miden_client::{Felt, Word, ZERO}; -use rand::{Rng, RngCore}; +use rand::{Rng, RngExt}; use crate::tests::config::ClientConfig; @@ -366,10 +366,10 @@ fn build_non_standard_mint( ) .details_commitment(); - let mint_storage = MintNoteStorage::new_public( + let mint_storage = MintNoteStorage::new_fungible_public( recipient, expected_asset, - NoteTag::with_account_target(target).into(), + NoteTag::with_account_target(target), )?; let target_ntx = NetworkAccountTarget::new(faucet.id(), NoteExecutionHint::Always)?; let mint_note: Note = MintNote::builder() @@ -692,10 +692,10 @@ pub async fn test_ntx_mint_produces_public_p2id(client_config: ClientConfig) -> ) .details_commitment(); - let mint_storage = MintNoteStorage::new_public( + let mint_storage = MintNoteStorage::new_fungible_public( bob_recipient, expected_asset, - NoteTag::with_account_target(bob.id()).into(), + NoteTag::with_account_target(bob.id()), )?; let target_ntx = NetworkAccountTarget::new(faucet.id(), NoteExecutionHint::Always)?; @@ -841,25 +841,6 @@ pub async fn test_ntx_mint_produces_public_note_with_non_standard_script( Ok(()) } -/// Compiles the counter contract library using the provided source manager so that all source -/// spans are registered in the same manager used by the client's executor. -pub(crate) fn counter_contract_library(source_manager: Arc) -> Arc { - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); - let module = Module::parser(ModuleKind::Library) - .parse_str( - Path::new("external_contract::counter_contract"), - COUNTER_CONTRACT, - source_manager.clone(), - ) - .map_err(|err| anyhow!(err)) - .unwrap(); - assembler - .clone() - .assemble_library([module]) - .map_err(|err| anyhow!(err)) - .unwrap() -} - /// Compiles a note script (linked against the counter contract library) and returns its script /// root, used to populate a network account's note-script allowlist. The root must match the note /// the account is expected to consume, so this compiles the script exactly as @@ -868,8 +849,8 @@ pub(crate) fn note_script_root( script: &str, source_manager: Arc, ) -> Result { - let script = CodeBuilder::with_source_manager(source_manager.clone()) - .with_dynamically_linked_library(counter_contract_library(source_manager))? + let script = CodeBuilder::with_source_manager(source_manager) + .with_linked_module("external_contract::counter_contract", COUNTER_CONTRACT)? .compile_note_script(script)?; Ok(script.root()) } @@ -902,8 +883,8 @@ pub(crate) fn get_network_note_with_script( let partial_metadata = PartialNoteMetadata::new(sender, NoteType::Public) .with_tag(NoteTag::with_account_target(network_account)); - let script = CodeBuilder::with_source_manager(source_manager.clone()) - .with_dynamically_linked_library(counter_contract_library(source_manager))? + let script = CodeBuilder::with_source_manager(source_manager) + .with_linked_module("external_contract::counter_contract", COUNTER_CONTRACT)? .compile_note_script(script)?; let recipient = NoteRecipient::new( Word::new([ diff --git a/bin/integration-tests/src/tests/onchain.rs b/bin/integration-tests/src/tests/onchain.rs index c05e583b54..80995e8a39 100644 --- a/bin/integration-tests/src/tests/onchain.rs +++ b/bin/integration-tests/src/tests/onchain.rs @@ -27,7 +27,7 @@ use miden_client::transaction::{ TransactionStatus, }; use miden_client::{ClientError, EMPTY_WORD, Word}; -use rand::RngCore; +use rand::Rng; use tracing::info; use crate::tests::config::ClientConfig; diff --git a/bin/integration-tests/src/tests/pass_through.rs b/bin/integration-tests/src/tests/pass_through.rs index 42facd314e..fb8cfe7bf2 100644 --- a/bin/integration-tests/src/tests/pass_through.rs +++ b/bin/integration-tests/src/tests/pass_through.rs @@ -37,7 +37,7 @@ use miden_client::store::{InputNoteState, TransactionFilter}; use miden_client::testing::common::*; use miden_client::transaction::TransactionRequestBuilder; use miden_client::{Client, ClientRng, Word}; -use rand::RngCore; +use rand::Rng; use tracing::info; use crate::tests::config::ClientConfig; @@ -242,7 +242,7 @@ fn create_pass_through_note( ) -> Result<(Note, NoteDetails)> { let note_script = get_pass_through_note_script(); - let asset_key: Word = asset.to_key_word(); + let asset_key: Word = asset.to_id_word(); let asset_value: Word = asset.to_value_word(); let target_recipient = P2idNoteStorage::new(target).into_recipient(rng.draw_word()); diff --git a/bin/miden-bench/src/benchmarks/transaction.rs b/bin/miden-bench/src/benchmarks/transaction.rs index 119d328dc8..4a1aa7e7ed 100644 --- a/bin/miden-bench/src/benchmarks/transaction.rs +++ b/bin/miden-bench/src/benchmarks/transaction.rs @@ -303,7 +303,7 @@ async fn benchmark_tx_full( // Measure full transaction time (execute + prove + submit) let (_, duration) = Box::pin(measure_time_async(|| async { - client.submit_new_transaction(account_id, tx_request).await + Box::pin(client.submit_new_transaction(account_id, tx_request)).await })) .await; diff --git a/bin/miden-bench/src/config.rs b/bin/miden-bench/src/config.rs index cb4e96663c..ad8a808f51 100644 --- a/bin/miden-bench/src/config.rs +++ b/bin/miden-bench/src/config.rs @@ -5,9 +5,9 @@ use miden_client::builder::ClientBuilder; use miden_client::crypto::RandomCoin; use miden_client::keystore::FilesystemKeyStore; use miden_client::rpc::{Endpoint, GrpcClient}; -use miden_client::{Client, DebugMode, Felt}; +use miden_client::{Client, Felt}; use miden_client_sqlite_store::ClientBuilderSqliteExt; -use rand::Rng; +use rand::RngExt; /// Default store directory name, created in the current working directory. pub const DEFAULT_STORE_DIR: &str = "miden-bench-store"; @@ -52,7 +52,6 @@ pub async fn create_client( .rng(Box::new(rng_coin)) .sqlite_store(sqlite_path) .filesystem_keystore(keystore_path.to_str().expect("keystore path should be valid UTF-8"))? - .in_debug_mode(DebugMode::Disabled) .tx_discard_delta(None) .build() .await?; diff --git a/bin/miden-bench/src/deploy.rs b/bin/miden-bench/src/deploy.rs index 12e9d69e97..4d56a98140 100644 --- a/bin/miden-bench/src/deploy.rs +++ b/bin/miden-bench/src/deploy.rs @@ -19,7 +19,7 @@ use miden_client::auth::{Approver, AuthSchemeId, AuthSecretKey, AuthSingleSig}; use miden_client::keystore::{FilesystemKeyStore, Keystore}; use miden_client::transaction::TransactionRequestBuilder; use miden_client::{Client, Serializable}; -use rand::Rng; +use rand::RngExt; use rand_chacha::ChaCha20Rng; use rand_chacha::rand_core::SeedableRng; diff --git a/bin/miden-bench/src/expand.rs b/bin/miden-bench/src/expand.rs index b311830b0f..73589c4910 100644 --- a/bin/miden-bench/src/expand.rs +++ b/bin/miden-bench/src/expand.rs @@ -158,8 +158,15 @@ pub async fn expand_storage( let entries = generate_entries(map_idx, offset, count); let expansion_code = generate_expansion_component_code(num_maps); - submit_expansion_batches(client, account_id, map_idx, offset, &entries, &expansion_code) - .await?; + Box::pin(submit_expansion_batches( + client, + account_id, + map_idx, + offset, + &entries, + &expansion_code, + )) + .await?; println!(); println!("Total expand time: {:.2?}", total_t.elapsed()); diff --git a/bin/miden-bench/src/generators/large_account.rs b/bin/miden-bench/src/generators/large_account.rs index 4a181313fd..15bdfde181 100644 --- a/bin/miden-bench/src/generators/large_account.rs +++ b/bin/miden-bench/src/generators/large_account.rs @@ -3,7 +3,7 @@ use std::fmt::Write; use miden_client::Felt; -use rand::Rng; +use rand::{Rng, RngExt}; use rand_chacha::ChaCha20Rng; use rand_chacha::rand_core::SeedableRng; diff --git a/bin/miden-cli/Cargo.toml b/bin/miden-cli/Cargo.toml index abcffba87c..8a3465523c 100644 --- a/bin/miden-cli/Cargo.toml +++ b/bin/miden-cli/Cargo.toml @@ -48,7 +48,7 @@ miden-client = { features = ["std"], workspace = true } anyhow = { workspace = true } assert_cmd = { workspace = true } miden-client = { features = ["testing"], workspace = true } -midenc-hir-type = "0.6" +midenc-hir-type = "0.9" predicates = { workspace = true } regex = { workspace = true } serial_test = { features = ["file_locks"], version = "3.0" } diff --git a/bin/miden-cli/build.rs b/bin/miden-cli/build.rs index 7c68d3d855..9525f6bf9b 100644 --- a/bin/miden-cli/build.rs +++ b/bin/miden-cli/build.rs @@ -1,5 +1,4 @@ use std::path::PathBuf; -use std::sync::Arc; use std::{env, fs}; use miden_client::account::component::{ @@ -17,7 +16,6 @@ use miden_client::utils::Serializable; use miden_client::vm::{ Package, PackageExport, - PackageManifest, ProcedureExport, QualifiedProcedureName, Section, @@ -30,18 +28,13 @@ const PACKAGE_DIR: &str = "packages"; fn main() { // Basic wallet (no storage schema) let basic_wallet_metadata = BasicWallet::component_metadata(); - build_package( - "basic-wallet", - BasicWallet::code().as_library().clone(), - &basic_wallet_metadata, - None, - ); + build_package("basic-wallet", BasicWallet::code().as_library(), &basic_wallet_metadata, None); // Basic fungible faucet let basic_faucet_metadata = FungibleFaucet::component_metadata(); build_package( "basic-fungible-faucet", - FungibleFaucet::code().as_library().clone(), + FungibleFaucet::code().as_library(), &basic_faucet_metadata, None, ); @@ -51,7 +44,7 @@ fn main() { build_package( "basic-auth", - AuthSingleSig::code().as_library().clone(), + AuthSingleSig::code().as_library(), &singlesig_metadata, Some("auth"), ); @@ -59,7 +52,7 @@ fn main() { // ECDSA auth (same component, different package name for discoverability) build_package( "ecdsa-auth", - AuthSingleSig::code().as_library().clone(), + AuthSingleSig::code().as_library(), &singlesig_metadata, Some("auth"), ); @@ -67,32 +60,27 @@ fn main() { // No authentication component. Nonce is incremented on first transaction and when the account // state is changed. Provides no cryptographic authentication. let no_auth_metadata = NoAuth::component_metadata(); - build_package("no-auth", NoAuth::code().as_library().clone(), &no_auth_metadata, Some("auth")); + build_package("no-auth", NoAuth::code().as_library(), &no_auth_metadata, Some("auth")); // Multisig auth let multisig_metadata = AuthMultisig::component_metadata(); build_package( "multisig-auth", - AuthMultisig::code().as_library().clone(), + AuthMultisig::code().as_library(), &multisig_metadata, Some("auth"), ); // ACL auth let acl_metadata = AuthSingleSigAcl::component_metadata(); - build_package( - "acl-auth", - AuthSingleSigAcl::code().as_library().clone(), - &acl_metadata, - Some("auth"), - ); + build_package("acl-auth", AuthSingleSigAcl::code().as_library(), &acl_metadata, Some("auth")); } /// Builds a package and stores it under `{OUT_DIR}/{PACKAGE_DIR}` or /// `{OUT_DIR}/{PACKAGE_DIR}/{subdirectory}` if a subdirectory is provided. pub fn build_package( package_name: &str, - library: Library, + library: &Library, metadata: &AccountComponentMetadata, subdirectory: Option<&str>, ) { @@ -105,6 +93,8 @@ pub fn build_package( let name = QualifiedProcedureName::new(module_info.path(), proc_info.name.clone()); let export = ProcedureExport { path: name.into_inner(), + node: None, + source_node: None, digest: proc_info.digest, signature: proc_info.signature.as_deref().cloned(), attributes: proc_info.attributes.clone(), @@ -113,22 +103,22 @@ pub fn build_package( } } - let mast = Arc::new(library); - - let manifest = PackageManifest::new(exports).expect("manifest validation failed"); + let mast = library.mast_forest().clone(); let account_component_metadata_section = Section::new(SectionId::ACCOUNT_COMPONENT_METADATA, metadata.to_bytes()); - let package = Package { - name: metadata.name().to_string().into(), - version: metadata.version().clone(), - description: Some(metadata.description().to_string()), + let mut package = Package::create( + metadata.name().to_string().into(), + metadata.version().clone(), + TargetType::AccountComponent, mast, - manifest, - sections: vec![account_component_metadata_section], - kind: TargetType::AccountComponent, - }; + exports, + [], + ) + .expect("package creation failed"); + package.description = Some(metadata.description().to_string()); + package.sections = vec![account_component_metadata_section]; let out_dir = env::var("OUT_DIR").expect("OUT_DIR environment variable not set"); diff --git a/bin/miden-cli/src/commands/call.rs b/bin/miden-cli/src/commands/call.rs index 5a36ca19cd..e0ead5e86e 100644 --- a/bin/miden-cli/src/commands/call.rs +++ b/bin/miden-cli/src/commands/call.rs @@ -98,8 +98,7 @@ impl CallCmd { // it at compile time to resolve `call.` to a known procedure — otherwise it // emits a "phantom target" warning. Dynamic linking provides that resolution without // embedding the library bytes in the script. - let linked_builder = - client.code_builder().with_dynamically_linked_library(package.mast.as_ref())?; + let linked_builder = client.code_builder().with_dynamically_linked_library(&package)?; // 1) Read-only execution to get return values. If `result_count` is unknown we skip // the drop sequence and let `print_output_stack` auto-detect results from the stack. diff --git a/bin/miden-cli/src/commands/new_account.rs b/bin/miden-cli/src/commands/new_account.rs index 166da8c2a8..f0c5dadae4 100644 --- a/bin/miden-cli/src/commands/new_account.rs +++ b/bin/miden-cli/src/commands/new_account.rs @@ -30,7 +30,7 @@ use miden_client::keystore::Keystore; use miden_client::transaction::TransactionRequestBuilder; use miden_client::utils::Deserializable; use miden_client::vm::{Package, SectionId}; -use rand::RngCore; +use rand::Rng; use serde::Deserialize; use tracing::debug; @@ -340,7 +340,7 @@ fn build_fungible_faucet_component( /// `Package.name` field stores the component's full canonical name from `FungibleFaucet::NAME`.) fn drop_basic_fungible_faucet_packages(packages: &mut Vec) -> bool { let before = packages.len(); - packages.retain(|pkg| pkg.name.to_string() != FungibleFaucet::NAME); + packages.retain(|pkg| pkg.name != FungibleFaucet::NAME); packages.len() != before } diff --git a/bin/miden-cli/src/config.rs b/bin/miden-cli/src/config.rs index fa25743dd7..3b805caed4 100644 --- a/bin/miden-cli/src/config.rs +++ b/bin/miden-cli/src/config.rs @@ -172,7 +172,7 @@ impl CliConfig { /// /// Or for client initialization: /// ```ignore - /// CliClient::new(debug_mode).await? + /// CliClient::new().await? /// ``` /// /// ## When to use this method @@ -246,7 +246,7 @@ impl CliConfig { /// For standard CLI-like behavior: /// ```ignore /// CliConfig::load() // Respects local → global fallback - /// CliClient::new(debug_mode).await? + /// CliClient::new().await? /// ``` /// /// ## When to use this method @@ -288,7 +288,7 @@ impl CliConfig { /// For standard CLI-like behavior: /// ```ignore /// CliConfig::load() // Respects local → global priority - /// CliClient::new(debug_mode).await? + /// CliClient::new().await? /// ``` /// /// ## When to use this method @@ -358,7 +358,7 @@ impl CliConfig { /// let config = CliConfig::load()?; /// /// // Or even better, use CliClient directly: - /// // let client = CliClient::new(DebugMode::Disabled).await?; + /// // let client = CliClient::new().await?; /// # Ok(()) /// # } /// ``` diff --git a/bin/miden-cli/src/lib.rs b/bin/miden-cli/src/lib.rs index 122bf948d7..937cf0cfc7 100644 --- a/bin/miden-cli/src/lib.rs +++ b/bin/miden-cli/src/lib.rs @@ -1,4 +1,3 @@ -use std::env; use std::ffi::OsString; use std::ops::{Deref, DerefMut}; use std::sync::Arc; @@ -45,12 +44,12 @@ pub type CliKeyStore = FilesystemKeyStore; /// # Examples /// /// ```no_run +/// use miden_client_cli::CliClient; /// use miden_client_cli::transaction::TransactionRequestBuilder; -/// use miden_client_cli::{CliClient, DebugMode}; /// /// # async fn example() -> Result<(), Box> { /// // Create a CLI-configured client -/// let mut client = CliClient::new(DebugMode::Disabled).await?; +/// let mut client = CliClient::new().await?; /// /// // All Client methods work automatically via Deref /// client.sync_state().await?; @@ -90,7 +89,7 @@ impl CliClient { /// /// For standard client initialization that matches CLI behavior, use: /// ```ignore - /// CliClient::new(debug_mode).await? + /// CliClient::new().await? /// ``` /// /// This method **does not** follow the CLI's configuration priority logic (local → global). @@ -99,7 +98,6 @@ impl CliClient { /// # Arguments /// /// * `config` - The CLI configuration to use (bypasses standard config discovery) - /// * `debug_mode` - The debug mode setting ([`DebugMode::Enabled`] or [`DebugMode::Disabled`]) /// /// # Returns /// @@ -117,23 +115,20 @@ impl CliClient { /// ```no_run /// use std::path::PathBuf; /// - /// use miden_client_cli::{CliClient, CliConfig, DebugMode}; + /// use miden_client_cli::{CliClient, CliConfig}; /// /// # async fn example() -> Result<(), Box> { /// // BEWARE: This bypasses standard config discovery! /// // Only use if you know what you're doing. /// let config = CliConfig::from_dir(&PathBuf::from("/path/to/.miden"))?; - /// let client = CliClient::from_config(config, DebugMode::Disabled).await?; + /// let client = CliClient::from_config(config).await?; /// /// // Prefer this for standard CLI-like behavior: - /// let client = CliClient::new(DebugMode::Disabled).await?; + /// let client = CliClient::new().await?; /// # Ok(()) /// # } /// ``` - pub async fn from_config( - config: CliConfig, - debug_mode: miden_client::DebugMode, - ) -> Result { + pub async fn from_config(config: CliConfig) -> Result { // Create keystore let keystore = CliKeyStore::new(config.secret_keys_directory.clone()).map_err(CliError::KeyStore)?; @@ -148,7 +143,6 @@ impl CliClient { .sqlite_store(config.store_filepath.clone()) .rpc(rpc_client) .authenticator(Arc::new(keystore)) - .in_debug_mode(debug_mode) .tx_discard_delta(Some(TX_DISCARD_DELTA)); // Add optional max_block_number_delta @@ -191,10 +185,6 @@ impl CliClient { /// - Transaction graceful blocks delta /// - Optional max block number delta /// - /// # Arguments - /// - /// * `debug_mode` - The debug mode setting ([`DebugMode::Enabled`] or [`DebugMode::Disabled`]). - /// /// # Returns /// /// A configured [`CliClient`] instance. @@ -211,15 +201,15 @@ impl CliClient { /// # Examples /// /// ```no_run + /// use miden_client_cli::CliClient; /// use miden_client_cli::transaction::TransactionRequestBuilder; - /// use miden_client_cli::{CliClient, DebugMode}; /// /// # async fn example() -> Result<(), Box> { /// // Create a client with default settings (debug disabled) - /// let mut client = CliClient::new(DebugMode::Disabled).await?; + /// let mut client = CliClient::new().await?; /// /// // Or with debug mode enabled - /// let mut client = CliClient::new(DebugMode::Enabled).await?; + /// let mut client = CliClient::new().await?; /// /// // Use it like a regular Client /// client.sync_state().await?; @@ -233,7 +223,7 @@ impl CliClient { /// # Ok(()) /// # } /// ``` - pub async fn new(debug_mode: miden_client::DebugMode) -> Result { + pub async fn new() -> Result { // Check if client is not yet initialized => silently initialize the client if !config_file_exists()? { let init_cmd = InitCmd::default(); @@ -244,7 +234,7 @@ impl CliClient { let config = CliConfig::load()?; // Create client using the loaded configuration - Self::from_config(config, debug_mode).await + Self::from_config(config).await } /// Unwraps the `CliClient` to get the inner `Client`. @@ -254,10 +244,10 @@ impl CliClient { /// # Examples /// /// ```no_run - /// use miden_client_cli::{CliClient, DebugMode}; + /// use miden_client_cli::CliClient; /// /// # async fn example() -> Result<(), Box> { - /// let cli_client = CliClient::new(DebugMode::Disabled).await?; + /// let cli_client = CliClient::new().await?; /// let inner_client = cli_client.into_inner(); /// # Ok(()) /// # } @@ -374,11 +364,6 @@ enum Behavior { #[derive(Parser, Debug)] #[command(name = "miden-client")] pub struct Cli { - /// Activates the executor's debug mode, which enables debug output for scripts - /// that were compiled and executed with this mode. - #[arg(short, long, default_value_t = false)] - debug: bool, - #[command(subcommand)] action: Command, @@ -443,13 +428,6 @@ impl Cli { init_cmd.execute()?; } - // Define whether we want to use the executor's debug mode based on the env var and - // the flag override - let in_debug_mode = match env::var("MIDEN_DEBUG") { - Ok(value) if value.to_lowercase() == "true" => miden_client::DebugMode::Enabled, - _ => miden_client::DebugMode::Disabled, - }; - // Load configuration let cli_config = CliConfig::load()?; @@ -458,7 +436,7 @@ impl Cli { .map_err(CliError::KeyStore)?; // Create the client - let cli_client = CliClient::from_config(cli_config, in_debug_mode).await?; + let cli_client = CliClient::from_config(cli_config).await?; // Extract the inner client for command execution let client = cli_client.into_inner(); diff --git a/bin/miden-cli/src/utils.rs b/bin/miden-cli/src/utils.rs index df86925cd5..4e6cc63adb 100644 --- a/bin/miden-cli/src/utils.rs +++ b/bin/miden-cli/src/utils.rs @@ -186,7 +186,7 @@ pub async fn print_executed_transaction( } } - for vault_key in patch.vault().removed_asset_keys() { + for vault_key in patch.vault().removed_asset_ids() { table.add_row(vec![ "Removed Asset", &vault_key.faucet_id().prefix().to_hex(), diff --git a/bin/miden-cli/tests/cli.rs b/bin/miden-cli/tests/cli.rs index cd05af9a7b..ba10d65c84 100644 --- a/bin/miden-cli/tests/cli.rs +++ b/bin/miden-cli/tests/cli.rs @@ -1,6 +1,6 @@ use std::env::{self, temp_dir}; use std::fs::{self, File}; -use std::io::{Read, Write}; +use std::io::Read; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -10,22 +10,11 @@ use assert_cmd::cargo::cargo_bin_cmd; use miden_client::account::component::FungibleFaucet; use miden_client::account::{AccountId, AccountType, FaucetMetadata}; use miden_client::address::{Address, NetworkId}; -use miden_client::auth::{RPO_FALCON_SCHEME_ID, TransactionAuthenticator}; +use miden_client::auth::TransactionAuthenticator; use miden_client::builder::ClientBuilder; -use miden_client::crypto::{FeltRng, RandomCoin}; +use miden_client::crypto::RandomCoin; use miden_client::keystore::Keystore; -use miden_client::note::standards::NoteSyncHint; -use miden_client::note::{ - Note, - NoteAssets, - NoteFile, - NoteId, - NoteRecipient, - NoteStorage, - NoteTag, - NoteType, - PartialNoteMetadata, -}; +use miden_client::note::NoteId; use miden_client::note_transport::NOTE_TRANSPORT_TESTNET_ENDPOINT; use miden_client::rpc::Endpoint; use miden_client::testing::account_id::ACCOUNT_ID_PRIVATE_SENDER; @@ -33,17 +22,14 @@ use miden_client::testing::common::{ ACCOUNT_ID_REGULAR, FilesystemKeyStore, create_test_store_path, - execute_tx_and_sync, - insert_new_wallet, }; -use miden_client::transaction::TransactionRequestBuilder; use miden_client::utils::Serializable; -use miden_client::{self, Client, DebugMode, Felt}; +use miden_client::{self, Client, Felt}; use miden_client_cli::MIDEN_DIR; use miden_client_cli::config::Network; use miden_client_sqlite_store::SqliteStore; use predicates::str::contains; -use rand::Rng; +use rand::RngExt; // CLI TESTS // ================================================================================================ @@ -944,93 +930,6 @@ async fn init_with_testnet() -> Result<()> { Ok(()) } -#[tokio::test] -#[serial_test::file_serial] -async fn debug_mode_outputs_logs() -> Result<()> { - // This test tries to execute a transaction with debug mode enabled and checks that the stack - // state is printed. We need to use the CLI for this because the debug logs are always printed - // to stdout and we can't capture them in a [`Client`] only test. - // We use the [`Client`] to create a custom note that will print the stack state and consume it - // using the CLI to check the stdout. - const NOTE_FILENAME: &str = "test_note.mno"; - unsafe { - env::set_var("MIDEN_DEBUG", "true"); - } - - // Create a Client and a custom note - let (store_path, _, endpoint) = init_cli(); - let (mut client, authenticator) = - create_rust_client_with_store_path(&store_path, endpoint).await?; - let (account, ..) = - insert_new_wallet(&mut client, AccountType::Private, &authenticator, RPO_FALCON_SCHEME_ID) - .await?; - - // Create the custom note with a script that will print the stack state - let note_script = " - @note_script - pub proc main - debug.stack - assert_eq - end - "; - let note_script = client.code_builder().compile_note_script(note_script).unwrap(); - let inputs = NoteStorage::new(vec![]).unwrap(); - let serial_num = client.rng().draw_word(); - let note_metadata = PartialNoteMetadata::new(account.id(), NoteType::Private) - .with_tag(NoteTag::with_account_target(account.id())); - let note_assets = NoteAssets::new(vec![]).unwrap(); - let note_recipient = NoteRecipient::new(serial_num, note_script, inputs); - let note = Note::new(note_assets, note_metadata, note_recipient); - - // Send transaction and wait for it to be committed - client.sync_state().await?; - let transaction_request = - TransactionRequestBuilder::new().own_output_notes(vec![note.clone()]).build()?; - execute_tx_and_sync(&mut client, account.id(), transaction_request).await?; - - // Export the note - let note_file: NoteFile = NoteFile::ExpectedNote { - details: note.clone().into(), - sync_hint: NoteSyncHint::new(0.into(), note.metadata().tag()), - }; - - // Import the note into the CLI - let (_, temp_dir, _) = init_cli(); - - // Serialize the note - let note_path = temp_dir.join(NOTE_FILENAME); - let mut file = File::create(note_path.clone()).unwrap(); - file.write_all(¬e_file.to_bytes()).unwrap(); - - // Import the note - let mut import_cmd = cargo_bin_cmd!("miden-client"); - import_cmd.args(["import", note_path.to_str().unwrap()]); - import_cmd.current_dir(&temp_dir).assert().success(); - - sync_cli(&temp_dir); - - // Create wallet account - let wallet_account_id = new_wallet_cli(&temp_dir, AccountType::Private); - - // Consume the note and check the output - let mut consume_note_cmd = cargo_bin_cmd!("miden-client"); - let note_id = note.id().to_hex(); - let mut cli_args = vec!["consume-notes", "--account", &wallet_account_id, "--force"]; - cli_args.extend_from_slice(vec![note_id.as_str()].as_slice()); - consume_note_cmd.args(&cli_args); - consume_note_cmd - .current_dir(&temp_dir) - .assert() - .success() - .stdout(contains("Stack state")); - - unsafe { - env::remove_var("MIDEN_DEBUG"); - } - - Ok(()) -} - // ADDRESSES TESTS // ================================================================================================ @@ -1333,7 +1232,6 @@ fn sync_cli(cli_path: &Path) -> SyncResult { /// successfully given account using the CLI given by `cli_path`. fn mint_cli(cli_path: &Path, target_account_id: &str, faucet_id: &str) -> String { let mut mint_cmd = cargo_bin_cmd!("miden-client"); - mint_cmd.env("MIDEN_DEBUG", "true"); mint_cmd.args([ "mint", "--target", @@ -1526,7 +1424,6 @@ async fn create_rust_client_with_store_path( .rng(rng) .store(store) .authenticator(Arc::new(keystore.clone())) - .in_debug_mode(DebugMode::Enabled) .build() .await?; @@ -1670,6 +1567,8 @@ fn call_test_exports( .map(|(_, sig)| sig.clone()); exports.push(PackageExport::Procedure(ProcedureExport { path: name.into_inner(), + node: None, + source_node: None, digest: proc_info.digest, signature: override_sig.or_else(|| proc_info.signature.as_deref().cloned()), attributes: proc_info.attributes.clone(), @@ -1690,7 +1589,7 @@ fn build_call_test_masp(out_path: &Path) { WordSchema, }; use miden_client::assembly::{CodeBuilder, Library}; - use miden_client::vm::{Package, PackageManifest, Section, SectionId, TargetType}; + use miden_client::vm::{Package, Section, SectionId, TargetType}; let call_test_code = r#" use miden::protocol::native_account @@ -1745,18 +1644,19 @@ fn build_call_test_masp(out_path: &Path) { let metadata = AccountComponentMetadata::new("call-test").with_storage_schema(storage_schema); let exports = call_test_exports(&library); - let manifest = PackageManifest::new(exports).expect("manifest validation failed"); let section = Section::new(SectionId::ACCOUNT_COMPONENT_METADATA, metadata.to_bytes()); - let package = Package { - name: metadata.name().to_string().into(), - version: metadata.version().clone(), - description: Some(metadata.description().to_string()), - mast: Arc::new(library), - manifest, - sections: vec![section], - kind: TargetType::AccountComponent, - }; + let mut package = Package::create( + metadata.name().to_string().into(), + metadata.version().clone(), + TargetType::AccountComponent, + library.mast_forest().clone(), + exports, + [], + ) + .expect("failed to create call-test package"); + package.description = Some(metadata.description().to_string()); + package.sections = vec![section]; fs::write(out_path, package.to_bytes()).expect("failed to write call-test .masp"); } @@ -2116,7 +2016,7 @@ async fn test_new_with_local_config() -> Result<()> { env::set_current_dir(&temp_dir)?; // Create a client using new - should pick up local config - let client_result = miden_client_cli::CliClient::new(DebugMode::Disabled).await; + let client_result = miden_client_cli::CliClient::new().await; // Restore original directory env::set_current_dir(original_dir)?; @@ -2159,7 +2059,7 @@ async fn test_new_silent_init() -> Result<()> { env::set_current_dir(&temp_dir)?; // Create a client - should succeed via silent initialization - let client_result = miden_client_cli::CliClient::new(DebugMode::Disabled).await; + let client_result = miden_client_cli::CliClient::new().await; // Restore original directory env::set_current_dir(original_dir)?; @@ -2215,7 +2115,7 @@ async fn test_load_local_priority() -> Result<()> { let config = miden_client_cli::CliConfig::from_dir(&local_miden_dir)?; // Create client with local config - let client = miden_client_cli::CliClient::from_config(config, DebugMode::Disabled).await; + let client = miden_client_cli::CliClient::from_config(config).await; // Assert client was created with local config assert!(client.is_ok(), "Failed to create client with local config: {:?}", client.err()); diff --git a/crates/rust-client/Cargo.toml b/crates/rust-client/Cargo.toml index 958b4d0a0d..1b2684bc2c 100644 --- a/crates/rust-client/Cargo.toml +++ b/crates/rust-client/Cargo.toml @@ -48,7 +48,7 @@ crate-type = ["lib"] # `std`; the duplication in the `std` feature list is intentional so # each consumer can pick the smallest feature set they need. concurrent = ["miden-tx/concurrent"] -dap = ["dep:miden-debug", "dep:miden-processor", "std"] +dap = ["dep:miden-processor", "std"] default = ["std"] std = [ "concurrent", @@ -74,7 +74,6 @@ tonic = [] [dependencies] # Miden dependencies miden-agglayer = { workspace = true } -miden-debug = { optional = true, workspace = true } miden-processor = { optional = true, workspace = true } miden-protocol = { workspace = true } miden-standards = { workspace = true } diff --git a/crates/rust-client/src/account/account_reader.rs b/crates/rust-client/src/account/account_reader.rs index 54947fa6a4..9bec0c67a3 100644 --- a/crates/rust-client/src/account/account_reader.rs +++ b/crates/rust-client/src/account/account_reader.rs @@ -11,7 +11,7 @@ use miden_protocol::account::{ StorageSlotName, }; use miden_protocol::address::Address; -use miden_protocol::asset::{Asset, AssetCallbackFlag, AssetVaultKey}; +use miden_protocol::asset::{Asset, AssetId}; use miden_protocol::{Felt, Word}; use crate::errors::ClientError; @@ -120,13 +120,11 @@ impl AccountReader { /// [`Client::get_account_vault`](crate::Client::get_account_vault). pub async fn get_balance(&self, faucet_id: AccountId) -> Result { let mut total = 0u64; - for callback_flag in [AssetCallbackFlag::Disabled, AssetCallbackFlag::Enabled] { - let vault_key = AssetVaultKey::new_fungible(faucet_id, callback_flag); - if let Some((Asset::Fungible(fungible_asset), _)) = - self.store.get_account_asset(self.account_id, vault_key).await? - { - total = total.saturating_add(u64::from(fungible_asset.amount())); - } + let vault_key = AssetId::new_fungible(faucet_id); + if let Some((Asset::Fungible(fungible_asset), _)) = + self.store.get_account_asset(self.account_id, vault_key).await? + { + total = total.saturating_add(u64::from(fungible_asset.amount())); } Ok(total) diff --git a/crates/rust-client/src/account/mod.rs b/crates/rust-client/src/account/mod.rs index ed233e72c0..2217e56c89 100644 --- a/crates/rust-client/src/account/mod.rs +++ b/crates/rust-client/src/account/mod.rs @@ -117,6 +117,10 @@ pub use account_reader::AccountReader; pub use miden_standards::account as standards; use miden_standards::account::auth::{Approver, AuthSingleSig}; use miden_standards::account::faucets::FungibleFaucet; +pub use miden_standards::account::inspection::{ + AccountBuilderSchemaCommitmentExt, + AccountSchemaCommitment, +}; // RE-EXPORTS // ================================================================================================ pub use miden_standards::account::interface::{ @@ -125,10 +129,6 @@ pub use miden_standards::account::interface::{ AccountInterface, AccountInterfaceExt, }; -pub use miden_standards::account::metadata::{ - AccountBuilderSchemaCommitmentExt, - AccountSchemaCommitment, -}; use miden_standards::account::wallets::BasicWallet; use super::Client; @@ -652,7 +652,7 @@ pub fn build_wallet_id( mod schema_commitment_tests { use miden_protocol::EMPTY_WORD; use miden_protocol::account::auth::AuthSecretKey; - use miden_standards::account::metadata::AccountSchemaCommitment; + use miden_standards::account::inspection::AccountSchemaCommitment; use super::{ AccountBuilder, diff --git a/crates/rust-client/src/builder.rs b/crates/rust-client/src/builder.rs index 044577d472..c637954bfc 100644 --- a/crates/rust-client/src/builder.rs +++ b/crates/rust-client/src/builder.rs @@ -8,7 +8,7 @@ use miden_protocol::block::BlockNumber; use miden_protocol::crypto::rand::RandomCoin; use miden_protocol::{Felt, MAX_TX_EXECUTION_CYCLES, MIN_TX_EXECUTION_CYCLES}; use miden_tx::{ExecutionOptions, LocalTransactionProver}; -use rand::Rng; +use rand::RngExt; #[cfg(any(feature = "tonic", feature = "std"))] use crate::alloc::string::ToString; @@ -20,7 +20,7 @@ use crate::pswap::PswapTransactionObserver; use crate::rpc::{Endpoint, NodeRpcClient}; use crate::store::{Store, StoreError}; use crate::transaction::{TransactionObserver, TransactionProver}; -use crate::{Client, ClientError, ClientRng, ClientRngBox, DebugMode, grpc_support}; +use crate::{Client, ClientError, ClientRng, ClientRngBox, grpc_support}; // CONSTANTS // ================================================================================================ @@ -98,9 +98,6 @@ pub trait StoreFactory { /// through the Miden note transport network. Configure via /// [`note_transport()`](Self::note_transport). /// -/// - **Debug mode**: Enables debug mode for transaction execution. Configure via -/// [`in_debug_mode()`](Self::in_debug_mode). -/// /// - **Transaction discard delta**: Number of blocks after which pending transactions are /// considered stale and discarded. Configure via [`tx_discard_delta()`](Self::tx_discard_delta). /// @@ -120,8 +117,6 @@ pub struct ClientBuilder { rng: Option, /// The authenticator provided by the user. authenticator: Option>, - /// A flag to enable debug mode. - in_debug_mode: DebugMode, /// Number of blocks after which pending transactions are considered stale and discarded. /// If `None`, there is no limit and transactions will be kept indefinitely. tx_discard_delta: Option, @@ -153,7 +148,6 @@ impl Default for ClientBuilder { store: None, rng: None, authenticator: None, - in_debug_mode: DebugMode::Disabled, tx_discard_delta: Some(TX_DISCARD_DELTA), irrelevant_block_prune_interval: Some(IRRELEVANT_BLOCK_PRUNE_INTERVAL), cache_partial_mmr_in_memory: CACHE_PARTIAL_MMR_IN_MEMORY, @@ -312,13 +306,6 @@ where Self::default() } - /// Enable or disable debug mode. - #[must_use] - pub fn in_debug_mode(mut self, debug: DebugMode) -> Self { - self.in_debug_mode = debug; - self - } - /// Sets a custom RPC client directly. #[must_use] pub fn rpc(mut self, client: Arc) -> Self { @@ -539,8 +526,6 @@ where Some(MAX_TX_EXECUTION_CYCLES), MIN_TX_EXECUTION_CYCLES, ExecutionOptions::DEFAULT_CORE_TRACE_FRAGMENT_SIZE, - false, - self.in_debug_mode.into(), ) .expect("Default executor's options should always be valid"), tx_discard_delta: self.tx_discard_delta, diff --git a/crates/rust-client/src/lib.rs b/crates/rust-client/src/lib.rs index cccd836fb3..25115019ce 100644 --- a/crates/rust-client/src/lib.rs +++ b/crates/rust-client/src/lib.rs @@ -58,7 +58,6 @@ //! ```rust,ignore //! use std::sync::Arc; //! -//! use miden_client::DebugMode; //! use miden_client::builder::ClientBuilder; //! use miden_client::keystore::FilesystemKeyStore; //! use miden_client::rpc::{Endpoint, GrpcClient}; @@ -80,7 +79,6 @@ //! .rpc(Arc::new(GrpcClient::new(&endpoint, 10_000))) //! .store(store) //! .authenticator(Arc::new(keystore)) -//! .in_debug_mode(DebugMode::Disabled) //! .build() //! .await?; //! @@ -184,6 +182,7 @@ pub mod asset { }; pub use miden_protocol::account::{ AccountStorageHeader, + AssetCallbackFlag, StorageMapWitness, StorageSlotContent, StorageSlotHeader, @@ -191,12 +190,10 @@ pub mod asset { pub use miden_protocol::asset::{ Asset, AssetAmount, - AssetCallbackFlag, AssetCallbacks, AssetComposition, AssetId, AssetVault, - AssetVaultKey, AssetWitness, FungibleAsset, NonFungibleAsset, @@ -346,12 +343,13 @@ pub mod testing { use alloc::sync::Arc; use alloc::vec::Vec; +use core::convert::Infallible; use miden_protocol::block::BlockNumber; use miden_protocol::crypto::merkle::mmr::PartialMmr; use miden_protocol::crypto::rand::FeltRng; use miden_tx::auth::TransactionAuthenticator; -use rand::RngCore; +use rand::TryRng; use rpc::NodeRpcClient; use store::Store; @@ -385,7 +383,7 @@ pub struct Client { authenticator: Option>, /// Shared source manager used to retain MASM source information for assembled programs. source_manager: Arc, - /// Options that control the transaction executor's runtime behaviour (e.g. debug mode). + /// Options that control the transaction executor's runtime behaviour (e.g. cycle limits). exec_options: ExecutionOptions, /// Number of blocks after which pending transactions are considered stale and discarded. tx_discard_delta: Option, @@ -455,11 +453,6 @@ impl Client where AUTH: TransactionAuthenticator, { - /// Returns true if the client is in debug mode. - pub fn in_debug_mode(&self) -> bool { - self.exec_options.enable_debugging() - } - /// Returns an instance of the `CodeBuilder` pub fn code_builder(&self) -> assembly::CodeBuilder { assembly::CodeBuilder::with_source_manager(self.source_manager.clone()) @@ -548,7 +541,7 @@ impl ClientFeltRng for T where T: FeltRng + Send + Sync {} /// Boxed RNG trait object used by the client. pub type ClientRngBox = Box; -/// A wrapper around a [`FeltRng`] that implements the [`RngCore`] trait. +/// A wrapper around a [`FeltRng`] that implements the [`TryRng`] trait. /// This allows the user to pass their own generic RNG so that it's used by the client. pub struct ClientRng(ClientRngBox); @@ -562,17 +555,20 @@ impl ClientRng { } } -impl RngCore for ClientRng { - fn next_u32(&mut self) -> u32 { - self.0.next_u32() +impl TryRng for ClientRng { + type Error = Infallible; + + fn try_next_u32(&mut self) -> Result { + Ok(self.0.next_u32()) } - fn next_u64(&mut self) -> u64 { - self.0.next_u64() + fn try_next_u64(&mut self) -> Result { + Ok(self.0.next_u64()) } - fn fill_bytes(&mut self, dest: &mut [u8]) { + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> { self.0.fill_bytes(dest); + Ok(()) } } @@ -586,32 +582,6 @@ impl FeltRng for ClientRng { } } -/// Indicates whether the client is operating in debug mode. -#[derive(Debug, Clone, Copy)] -pub enum DebugMode { - Enabled, - Disabled, -} - -impl From for bool { - fn from(debug_mode: DebugMode) -> Self { - match debug_mode { - DebugMode::Enabled => true, - DebugMode::Disabled => false, - } - } -} - -impl From for DebugMode { - fn from(debug_mode: bool) -> DebugMode { - if debug_mode { - DebugMode::Enabled - } else { - DebugMode::Disabled - } - } -} - #[cfg(test)] mod tests { use super::Client; diff --git a/crates/rust-client/src/pswap/lineage.rs b/crates/rust-client/src/pswap/lineage.rs index 9b8a7e81b9..a67f3e0502 100644 --- a/crates/rust-client/src/pswap/lineage.rs +++ b/crates/rust-client/src/pswap/lineage.rs @@ -96,7 +96,7 @@ impl PswapLineageRecord { current_tip_note_id: original_note_id, current_depth: 0, remaining_offered: pswap.offered_asset().amount(), - remaining_requested: pswap.storage().requested_asset().amount(), + remaining_requested: pswap.storage().min_requested_asset().amount(), state: PswapLineageState::Active, } } @@ -494,7 +494,7 @@ pub(crate) mod test_helpers { let offered = FungibleAsset::new(offered_faucet, offered_amount).unwrap(); let requested = FungibleAsset::new(requested_faucet, requested_amount).unwrap(); let storage = PswapNoteStorage::builder() - .requested_asset(requested) + .min_requested_asset(requested) .creator_account_id(creator) .build(); PswapNote::builder() diff --git a/crates/rust-client/src/pswap/mod.rs b/crates/rust-client/src/pswap/mod.rs index 45ca25ecf1..95734e6712 100644 --- a/crates/rust-client/src/pswap/mod.rs +++ b/crates/rust-client/src/pswap/mod.rs @@ -106,7 +106,7 @@ impl TransactionObserver for PswapTransactionObserver { tag: PswapNote::create_tag( pswap.note_type(), pswap.offered_asset(), - pswap.storage().requested_asset(), + pswap.storage().min_requested_asset(), ), source: NoteTagSource::Subscription(record.original_note_id.as_word()), }) diff --git a/crates/rust-client/src/pswap/store.rs b/crates/rust-client/src/pswap/store.rs index d7f65c7abd..ca7bed62fd 100644 --- a/crates/rust-client/src/pswap/store.rs +++ b/crates/rust-client/src/pswap/store.rs @@ -247,7 +247,7 @@ pub(crate) async fn apply_round( tag: PswapNote::create_tag( pswap.note_type(), pswap.offered_asset(), - pswap.storage().requested_asset(), + pswap.storage().min_requested_asset(), ), source: NoteTagSource::Subscription(new_record.original_note_id.as_word()), }) diff --git a/crates/rust-client/src/rpc/domain/account_vault.rs b/crates/rust-client/src/rpc/domain/account_vault.rs index e65d7f7008..20f5d45a3f 100644 --- a/crates/rust-client/src/rpc/domain/account_vault.rs +++ b/crates/rust-client/src/rpc/domain/account_vault.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; use miden_protocol::Word; use miden_protocol::account::AccountVaultPatch; -use miden_protocol::asset::{Asset, AssetVaultKey}; +use miden_protocol::asset::{Asset, AssetId}; use miden_protocol::block::BlockNumber; use crate::rpc::domain::MissingFieldHelper; @@ -13,7 +13,7 @@ use crate::rpc::{RpcConversionError, RpcError, generated as proto}; /// and its new asset (`None` if the asset was removed at that block). pub(crate) struct VaultUpdate { pub(crate) block_num: BlockNumber, - pub(crate) vault_key: AssetVaultKey, + pub(crate) vault_key: AssetId, pub(crate) asset: Option, } @@ -32,7 +32,7 @@ impl TryFrom for Asset { .value .ok_or(proto::primitives::Asset::missing_field(stringify!(value)))? .try_into()?; - Asset::from_key_value_words(key_word, value_word) + Asset::from_id_and_value_words(key_word, value_word) .map_err(|e| RpcConversionError::InvalidField(e.to_string())) } } @@ -94,13 +94,13 @@ fn vault_update_from_proto(value: proto::rpc::AccountVaultUpdate) -> Result for proto::blockchain::BlockHeader { nullifier_root: Some(header.nullifier_root().into()), note_root: Some(header.note_root().into()), tx_commitment: Some(header.tx_commitment().into()), - validator_key: Some(proto::blockchain::ValidatorPublicKey { - validator_key: header.validator_key().to_bytes(), - }), + validator_keys: header + .validator_keys() + .as_keys() + .iter() + .map(|key| proto::blockchain::ValidatorPublicKey { validator_key: key.to_bytes() }) + .collect(), tx_kernel_commitment: Some(header.tx_kernel_commitment().into()), fee_parameters: Some(header.fee_parameters().into()), timestamp: header.timestamp(), @@ -55,11 +61,13 @@ impl TryFrom for BlockHeader { type Error = RpcConversionError; fn try_from(value: proto::blockchain::BlockHeader) -> Result { - let validator_key_bytes = value - .validator_key - .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(validator_key)))? - .validator_key; - let validator_key = ecdsa_k256_keccak::PublicKey::read_from_bytes(&validator_key_bytes)?; + let validator_keys = value + .validator_keys + .into_iter() + .map(|key| ecdsa_k256_keccak::PublicKey::read_from_bytes(&key.validator_key)) + .collect::, _>>()?; + let validator_keys = ValidatorKeys::new(validator_keys) + .map_err(|err| RpcConversionError::InvalidField(err.to_string()))?; Ok(BlockHeader::new( value.version, @@ -96,7 +104,7 @@ impl TryFrom for BlockHeader { tx_kernel_commitment )))? .try_into()?, - validator_key, + validator_keys, value .fee_parameters .ok_or(proto::blockchain::BlockHeader::missing_field(stringify!(fee_parameters)))? diff --git a/crates/rust-client/src/store/data_store/mod.rs b/crates/rust-client/src/store/data_store/mod.rs index f4787f4245..fe2d8d4b45 100644 --- a/crates/rust-client/src/store/data_store/mod.rs +++ b/crates/rust-client/src/store/data_store/mod.rs @@ -13,15 +13,21 @@ use miden_protocol::account::{ StorageSlotContent, StorageSlotName, }; -use miden_protocol::asset::{AssetVaultKey, AssetWitness}; +use miden_protocol::asset::{AssetId, AssetWitness}; use miden_protocol::block::{BlockHeader, BlockNumber}; use miden_protocol::crypto::merkle::mmr::{InOrderIndex, MmrPeaks, PartialMmr}; use miden_protocol::crypto::merkle::{MerkleError, MerklePath}; use miden_protocol::note::{NoteScript, NoteScriptRoot}; use miden_protocol::transaction::{AccountInputs, PartialBlockchain}; use miden_protocol::vm::FutureMaybeSend; -use miden_protocol::{MastForest, Word, ZERO}; -use miden_tx::{DataStore, DataStoreError, MastForestStore, TransactionMastStore}; +use miden_protocol::{Word, ZERO}; +use miden_tx::{ + DataStore, + DataStoreError, + LoadedMastForest, + MastForestStore, + TransactionMastStore, +}; use super::{AccountStorageFilter, PartialBlockchainFilter, Store}; use crate::rpc::domain::account::{ @@ -277,10 +283,10 @@ impl DataStore for ClientDataStore { &self, account_id: AccountId, vault_root: Word, - vault_keys: BTreeSet, + asset_ids: BTreeSet, ) -> Result, DataStoreError> { let mut asset_witnesses = vec![]; - for vault_key in vault_keys { + for vault_key in asset_ids { match self.store.get_account_asset(account_id, vault_key).await { Ok(Some((_, asset_witness))) => asset_witnesses.push(asset_witness), Ok(None) | Err(StoreError::MerkleStoreError(MerkleError::RootNotInStore(_))) => { @@ -290,14 +296,7 @@ impl DataStore for ClientDataStore { return Err(DataStoreError::other("Vault root mismatch")); } - let asset_witness = AssetWitness::new( - vault.open(vault_key).into(), - [vault_key], - ) - .map_err(|err| { - DataStoreError::other_with_source("Failed to open vault asset tree", err) - })?; - asset_witnesses.push(asset_witness); + asset_witnesses.push(vault.open(vault_key)); }, Err(err) => { return Err(DataStoreError::other_with_source( @@ -430,7 +429,7 @@ impl DataStore for ClientDataStore { // ================================================================================================ impl MastForestStore for ClientDataStore { - fn get(&self, procedure_hash: &Word) -> Option> { + fn get(&self, procedure_hash: &Word) -> Option { self.cache.mast_store.get(procedure_hash) } } diff --git a/crates/rust-client/src/store/errors.rs b/crates/rust-client/src/store/errors.rs index f74b2d68e6..a19912191b 100644 --- a/crates/rust-client/src/store/errors.rs +++ b/crates/rust-client/src/store/errors.rs @@ -2,7 +2,7 @@ use alloc::string::String; use core::num::TryFromIntError; use miden_protocol::account::AccountId; -use miden_protocol::asset::AssetVaultKey; +use miden_protocol::asset::AssetId; use miden_protocol::block::BlockNumber; use miden_protocol::crypto::merkle::MerkleError; use miden_protocol::crypto::merkle::mmr::MmrError; @@ -94,8 +94,8 @@ pub enum StoreError { TransactionScriptError(#[from] TransactionScriptError), #[error("account vault data for root {0} not found")] VaultDataNotFound(Word), - #[error("vault key {0} (hashed to {1}) is not tracked in the vault")] - VaultKeyNotTracked(AssetVaultKey, Word), + #[error("vault key {0:?} (hashed to {1}) is not tracked in the vault")] + VaultKeyNotTracked(AssetId, Word), #[error("failed to parse word")] WordError(#[from] WordError), } diff --git a/crates/rust-client/src/store/mod.rs b/crates/rust-client/src/store/mod.rs index 9ba790ce81..bac2ac4edb 100644 --- a/crates/rust-client/src/store/mod.rs +++ b/crates/rust-client/src/store/mod.rs @@ -39,7 +39,7 @@ use miden_protocol::account::{ StorageSlotName, }; use miden_protocol::address::Address; -use miden_protocol::asset::{Asset, AssetVault, AssetVaultKey, AssetWitness}; +use miden_protocol::asset::{Asset, AssetId, AssetVault, AssetWitness}; use miden_protocol::block::{BlockHeader, BlockNumber}; use miden_protocol::crypto::merkle::mmr::{Forest, InOrderIndex, MmrPeaks, PartialMmr}; use miden_protocol::errors::AccountError; @@ -599,14 +599,14 @@ pub trait Store: Send + Sync { async fn get_account_asset( &self, account_id: AccountId, - vault_key: AssetVaultKey, + vault_key: AssetId, ) -> Result, StoreError> { let vault = self.get_account_vault(account_id).await?; - let Some(asset) = vault.assets().find(|a| a.vault_key() == vault_key) else { + let Some(asset) = vault.assets().find(|a| a.id() == vault_key) else { return Ok(None); }; - let witness = AssetWitness::new(vault.open(vault_key).into(), [vault_key])?; + let witness = vault.open(vault_key); Ok(Some((asset, witness))) } diff --git a/crates/rust-client/src/store/smt_forest.rs b/crates/rust-client/src/store/smt_forest.rs index 90670ef66f..2eb5ca0fc8 100644 --- a/crates/rust-client/src/store/smt_forest.rs +++ b/crates/rust-client/src/store/smt_forest.rs @@ -10,7 +10,7 @@ use miden_protocol::account::{ StorageMapWitness, StorageSlotContent, }; -use miden_protocol::asset::{Asset, AssetVault, AssetVaultKey, AssetWitness}; +use miden_protocol::asset::{Asset, AssetId, AssetVault, AssetWitness}; use miden_protocol::crypto::merkle::EmptySubtreeRoots; use miden_protocol::crypto::merkle::smt::{SMT_DEPTH, Smt, SmtForest}; use miden_protocol::{EMPTY_WORD, Word}; @@ -50,9 +50,8 @@ impl AccountSmtForest { pub fn get_asset_and_witness( &self, vault_root: Word, - vault_key: AssetVaultKey, + vault_key: AssetId, ) -> Result<(Asset, AssetWitness), StoreError> { - let vault_key_word: Word = vault_key.into(); let hashed_key: Word = vault_key.hash().into(); let proof = self.forest.open(vault_root, hashed_key)?; let asset_word = proof @@ -62,7 +61,7 @@ impl AccountSmtForest { return Err(StoreError::VaultKeyNotTracked(vault_key, hashed_key)); } - let asset = Asset::from_key_value_words(vault_key_word, asset_word)?; + let asset = Asset::from_id_and_value(vault_key, asset_word)?; let witness = AssetWitness::new(proof, [vault_key])?; Ok((asset, witness)) } @@ -156,11 +155,11 @@ impl AccountSmtForest { &mut self, root: Word, new_assets: impl Iterator, - removed_vault_keys: impl Iterator, + removed_vault_keys: impl Iterator, ) -> Result { let entries: Vec<(Word, Word)> = new_assets .map(|asset| { - let key: Word = asset.vault_key().hash().into(); + let key: Word = asset.id().hash().into(); let value = asset.to_value_word(); (key, value) }) @@ -198,7 +197,7 @@ impl AccountSmtForest { /// Inserts the asset vault SMT nodes to the SMT forest. pub fn insert_asset_nodes(&mut self, vault: &AssetVault) -> Result<(), StoreError> { let smt = Smt::with_entries(vault.assets().map(|asset| { - let key: Word = asset.vault_key().hash().into(); + let key: Word = asset.id().hash().into(); let value = asset.to_value_word(); (key, value) })) diff --git a/crates/rust-client/src/sync/state_sync.rs b/crates/rust-client/src/sync/state_sync.rs index ca486043cc..51af4031b9 100644 --- a/crates/rust-client/src/sync/state_sync.rs +++ b/crates/rust-client/src/sync/state_sync.rs @@ -1358,7 +1358,7 @@ mod tests { header.note_root(), header.tx_commitment(), header.tx_kernel_commitment(), - header.validator_key().clone(), + header.validator_keys().clone(), header.fee_parameters().clone(), header.timestamp(), ) @@ -1544,7 +1544,7 @@ mod tests { real_header.note_root(), real_header.tx_commitment(), real_header.tx_kernel_commitment(), - real_header.validator_key().clone(), + real_header.validator_keys().clone(), real_header.fee_parameters().clone(), real_header.timestamp(), ); @@ -2027,22 +2027,21 @@ mod tests { for i in 0..num_blocks { let amount = 100 + i; let source_manager = Arc::new(DefaultSourceManager::default()); - // Derive the asset key/value in MASM via `create_fungible_asset` (mirroring the - // protocol's own faucet tests) so the callback flag matches what `mint_and_send` - // derives internally. `add_existing_basic_faucet` registers transfer policies, so - // the faucet has callbacks enabled (`push.1`). The new `mint_and_send` signature is - // `[ASSET_KEY, ASSET_VALUE, tag, note_type, RECIPIENT, pad(2)]`. + // `mint_and_send` consumes the fungible asset's ID and value words directly: + // `[ASSET_ID, ASSET_VALUE, tag, note_type, RECIPIENT, pad(2)]`. Both words are derived + // in Rust from the faucet's `AssetId`, which intrinsically carries the callback flag. + let mint_asset = FungibleAsset::new(faucet_account.id(), amount).unwrap(); + let asset_id_word = mint_asset.id().to_word(); + let asset_value_word = mint_asset.to_value_word(); let tx_script_code = format!( " - begin + @transaction_script + pub proc main push.{recipient} push.{note_type} push.{tag} - push.{amount} - push.{faucet_id_prefix} - push.{faucet_id_suffix} - push.1 - exec.::miden::protocol::asset::create_fungible_asset + push.{asset_value} + push.{asset_id} call.::miden::standards::faucets::fungible::mint_and_send dropw dropw dropw dropw end @@ -2050,9 +2049,8 @@ mod tests { recipient = recipient, note_type = NoteType::Private as u8, tag = u32::from(tag), - amount = amount, - faucet_id_prefix = faucet_account.id().prefix().as_felt(), - faucet_id_suffix = faucet_account.id().suffix(), + asset_value = asset_value_word, + asset_id = asset_id_word, ); let tx_script = CodeBuilder::with_source_manager(source_manager.clone()) .compile_tx_script(tx_script_code) diff --git a/crates/rust-client/src/sync/state_sync_update.rs b/crates/rust-client/src/sync/state_sync_update.rs index cab2fc2a75..2aeeecdd0d 100644 --- a/crates/rust-client/src/sync/state_sync_update.rs +++ b/crates/rust-client/src/sync/state_sync_update.rs @@ -589,7 +589,7 @@ mod tests { .compute_account_patch(None) .unwrap(); - assert_eq!(patch.storage().get_value(&value_slot), Some(word(2))); + assert_eq!(patch.storage().updated_value(&value_slot), Some(word(2))); } #[test] @@ -602,9 +602,10 @@ mod tests { let patch = payload(2, vec![], map_entries).compute_account_patch(None).unwrap(); - let map = patch.storage().get_map(&map_slot).expect("patch should contain map slot"); - assert_eq!(map.entries().unwrap().as_map().len(), 1); - assert_eq!(*map.entries().unwrap().as_map().values().next().unwrap(), word(300)); + let entries = + patch.storage().updated_map(&map_slot).expect("patch should contain map slot"); + assert_eq!(entries.as_map().len(), 1); + assert_eq!(*entries.as_map().values().next().unwrap(), word(300)); } #[test] @@ -647,8 +648,7 @@ mod tests { .compute_account_patch(Some(AccountCode::mock())) .unwrap(); - let map = patch.storage().get_map(&map_slot).expect("patch should contain map slot"); - assert!(matches!(map, StorageMapPatch::Create { .. })); + assert!(patch.storage().created_map(&map_slot).is_some()); } /// An update to an existing account (final nonce > 1) emits map slots as `Update`, never @@ -662,7 +662,6 @@ mod tests { let patch = payload(2, vec![], map_entries).compute_account_patch(None).unwrap(); - let map = patch.storage().get_map(&map_slot).expect("patch should contain map slot"); - assert!(matches!(map, StorageMapPatch::Update { .. })); + assert!(patch.storage().updated_map(&map_slot).is_some()); } } diff --git a/crates/rust-client/src/test_utils/common.rs b/crates/rust-client/src/test_utils/common.rs index 35c58939cb..faeb4fdaf3 100644 --- a/crates/rust-client/src/test_utils/common.rs +++ b/crates/rust-client/src/test_utils/common.rs @@ -17,7 +17,7 @@ use miden_protocol::transaction::TransactionId; use miden_standards::account::auth::{Approver, AuthSingleSig}; use miden_standards::account::faucets::TokenName; use miden_standards::code_builder::CodeBuilder; -use rand::RngCore; +use rand::Rng; use tracing::{debug, info}; use uuid::Uuid; diff --git a/crates/rust-client/src/test_utils/mock.rs b/crates/rust-client/src/test_utils/mock.rs index d2eeed61d1..5e21e91d98 100644 --- a/crates/rust-client/src/test_utils/mock.rs +++ b/crates/rust-client/src/test_utils/mock.rs @@ -183,12 +183,12 @@ impl MockRpcApi { for asset in vault_patch.updated_assets() { updates.push(VaultUpdate { block_num: block_number, - vault_key: asset.vault_key(), + vault_key: asset.id(), asset: Some(asset), }); } - for vault_key in vault_patch.removed_asset_keys() { + for vault_key in vault_patch.removed_asset_ids() { updates.push(VaultUpdate { block_num: block_number, vault_key: *vault_key, diff --git a/crates/rust-client/src/transaction/batch/data_store.rs b/crates/rust-client/src/transaction/batch/data_store.rs index 5e44890c91..d5f65dad98 100644 --- a/crates/rust-client/src/transaction/batch/data_store.rs +++ b/crates/rust-client/src/transaction/batch/data_store.rs @@ -2,6 +2,7 @@ use alloc::collections::{BTreeMap, BTreeSet}; use alloc::sync::Arc; use alloc::vec::Vec; +use miden_protocol::Word; use miden_protocol::account::{ Account, AccountId, @@ -10,13 +11,18 @@ use miden_protocol::account::{ StorageMapWitness, StorageSlotContent, }; -use miden_protocol::asset::{AssetVaultKey, AssetWitness}; +use miden_protocol::asset::{AssetId, AssetWitness}; use miden_protocol::block::{BlockHeader, BlockNumber}; use miden_protocol::note::{NoteScript, NoteScriptRoot}; use miden_protocol::transaction::{AccountInputs, PartialBlockchain}; use miden_protocol::vm::FutureMaybeSend; -use miden_protocol::{MastForest, Word}; -use miden_tx::{DataStore, DataStoreError, MastForestStore, TransactionMastStore}; +use miden_tx::{ + DataStore, + DataStoreError, + LoadedMastForest, + MastForestStore, + TransactionMastStore, +}; use crate::store::data_store::ClientDataStore; @@ -97,7 +103,7 @@ impl DataStore for InMemoryBatchDataStore { &self, account_id: AccountId, vault_root: Word, - vault_keys: BTreeSet, + asset_ids: BTreeSet, ) -> Result, DataStoreError> { if let Some(account) = self.current_accounts.get(&account_id) { let vault = account.vault(); @@ -107,10 +113,10 @@ impl DataStore for InMemoryBatchDataStore { "vault root mismatch for account {account_id}: in-batch root = {in_batch_root:?}, requested root = {vault_root:?}", ))); } - let witnesses = vault_keys.into_iter().map(|key| vault.open(key)).collect(); + let witnesses = asset_ids.into_iter().map(|key| vault.open(key)).collect(); Ok(witnesses) } else { - self.inner.get_vault_asset_witnesses(account_id, vault_root, vault_keys).await + self.inner.get_vault_asset_witnesses(account_id, vault_root, asset_ids).await } } @@ -155,7 +161,7 @@ impl DataStore for InMemoryBatchDataStore { // ================================================================================================ impl MastForestStore for InMemoryBatchDataStore { - fn get(&self, procedure_hash: &Word) -> Option> { + fn get(&self, procedure_hash: &Word) -> Option { self.inner.get(procedure_hash) } } diff --git a/crates/rust-client/src/transaction/dap_executor.rs b/crates/rust-client/src/transaction/dap_executor.rs index b5d5bd0bd3..5a901a3766 100644 --- a/crates/rust-client/src/transaction/dap_executor.rs +++ b/crates/rust-client/src/transaction/dap_executor.rs @@ -18,8 +18,27 @@ use miden_processor::{ }; use miden_tx::ProgramExecutor; -/// [`ProgramExecutor`] adapter for [`miden_debug::DapExecutor`]. -pub struct DapProgramExecutor(miden_debug::DapExecutor); +/// [`ProgramExecutor`] adapter for the DAP debugging path. +/// +/// TODO: Restore interactive DAP debugging. `execute` currently returns an error instead of +/// starting a debug session, so `Client::execute_program_with_dap` is non-functional. The adapter +/// is retained only so the `dap` feature keeps compiling and the debug path fails explicitly rather +/// than silently. +/// +/// Why it is blocked: the DAP executor in `miden-debug` 0.9 drives execution from an +/// `Arc` (to expose package-owned source/debug info to the debugger), but +/// [`ProgramExecutor::execute`] only hands this adapter a bare [`Program`], and there is no +/// lossless way to reconstruct the owning package from a `Program` here. +/// +/// Required fix (needs an upstream change, pick one): +/// - `miden-debug` exposes a `Program`-based debug executor (an `execute_async` that accepts +/// `&Program` instead of `Arc`), OR +/// - `miden-tx`'s [`ProgramExecutor::execute`] passes the owning `Arc` (or its package +/// debug info) alongside the `Program` so this adapter can forward it to `DapExecutor`. +/// +/// Once either lands, replace the error stub below with a real `DapExecutor::execute_async(...)` +/// call and re-enable the `debug_mode_outputs_logs`-style coverage removed in the 0.16 bump. +pub struct DapProgramExecutor; impl ProgramExecutor for DapProgramExecutor { fn new( @@ -27,7 +46,8 @@ impl ProgramExecutor for DapProgramExecutor { advice_inputs: AdviceInputs, options: ExecutionOptions, ) -> Self { - Self(miden_debug::DapExecutor::new(stack_inputs, advice_inputs, options)) + let _ = (stack_inputs, advice_inputs, options); + Self } fn execute( @@ -35,6 +55,13 @@ impl ProgramExecutor for DapProgramExecutor { program: &Program, host: &mut H, ) -> impl FutureMaybeSend> { - self.0.execute_async(program, host) + // TODO: replace this stub with a real `DapExecutor::execute_async(...)` call once upstream + // exposes a `Program`-based debug executor (see the type-level docs for the required fix). + let _ = (program, host); + async { + Err(ExecutionError::Internal( + "DAP debugging is not supported with the current miden-debug backend", + )) + } } } diff --git a/crates/rust-client/src/transaction/prover.rs b/crates/rust-client/src/transaction/prover.rs index d6a6e93d54..eb04effd79 100644 --- a/crates/rust-client/src/transaction/prover.rs +++ b/crates/rust-client/src/transaction/prover.rs @@ -22,7 +22,7 @@ impl TransactionProver for LocalTransactionProver { &self, witness: TransactionInputs, ) -> Result { - LocalTransactionProver::prove(self, witness).await + LocalTransactionProver::prove(self, witness) } } diff --git a/crates/rust-client/src/transaction/request/builder.rs b/crates/rust-client/src/transaction/request/builder.rs index f6b188ef09..395216ae1a 100644 --- a/crates/rust-client/src/transaction/request/builder.rs +++ b/crates/rust-client/src/transaction/request/builder.rs @@ -457,7 +457,7 @@ impl TransactionRequestBuilder { rng: &mut ClientRng, ) -> Result { let storage = PswapNoteStorage::builder() - .requested_asset(pswap_data.requested_asset()) + .min_requested_asset(pswap_data.requested_asset()) .creator_account_id(pswap_data.creator_account_id()) .payback_note_type(payback_note_type) .build(); @@ -497,7 +497,7 @@ impl TransactionRequestBuilder { let pswap = PswapNote::try_from(pswap_note) .map_err(TransactionRequestError::NoteValidationError)?; - let requested_faucet_id = pswap.storage().requested_asset().faucet_id(); + let requested_faucet_id = pswap.storage().min_requested_asset().faucet_id(); let account_fill_asset = FungibleAsset::new(requested_faucet_id, account_fill_amount)?; let note_fill_asset = FungibleAsset::new(requested_faucet_id, note_fill_amount)?; diff --git a/crates/sqlite-store/src/account/accounts.rs b/crates/sqlite-store/src/account/accounts.rs index 93237034c6..bc07c1bf55 100644 --- a/crates/sqlite-store/src/account/accounts.rs +++ b/crates/sqlite-store/src/account/accounts.rs @@ -35,7 +35,7 @@ use miden_client::store::{ use miden_client::utils::{Deserializable, Serializable}; use miden_client::{AccountError, Felt, Word}; use miden_protocol::account::{AccountStorageHeader, StorageMapWitness, StorageSlotHeader}; -use miden_protocol::asset::{AssetVaultKey, PartialVault}; +use miden_protocol::asset::{AssetId, PartialVault}; use rusqlite::types::Value; use rusqlite::{Connection, OptionalExtension, Transaction, named_params, params}; @@ -245,7 +245,7 @@ impl SqliteStore { conn: &mut Connection, smt_forest: &Arc>, account_id: AccountId, - vault_key: AssetVaultKey, + vault_key: AssetId, ) -> Result, StoreError> { // Acquire forest lock before getting header in order to avoid concurrent writes to it. let smt_forest = smt_forest diff --git a/crates/sqlite-store/src/account/helpers.rs b/crates/sqlite-store/src/account/helpers.rs index cbf930f5ac..5465538ac5 100644 --- a/crates/sqlite-store/src/account/helpers.rs +++ b/crates/sqlite-store/src/account/helpers.rs @@ -216,7 +216,7 @@ pub(crate) fn query_vault_assets( let (vault_key_str, asset_str): (String, String) = result.into_store_error()?; let key_word = Word::try_from(vault_key_str)?; let value_word = Word::try_from(asset_str)?; - Ok(Asset::from_key_value_words(key_word, value_word)?) + Ok(Asset::from_id_and_value_words(key_word, value_word)?) }) .collect::, StoreError>>() } diff --git a/crates/sqlite-store/src/account/tests.rs b/crates/sqlite-store/src/account/tests.rs index e0739bb683..238b02b89e 100644 --- a/crates/sqlite-store/src/account/tests.rs +++ b/crates/sqlite-store/src/account/tests.rs @@ -34,9 +34,9 @@ use miden_protocol::account::{ StorageSlotPatch, StorageValuePatch, }; -use miden_protocol::asset::AssetCallbackFlag; use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, + ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_WITH_CALLBACKS, ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET, }; use miden_protocol::testing::constants::NON_FUNGIBLE_ASSET_DATA; @@ -51,8 +51,10 @@ use crate::transaction::with_forest_snapshot; #[tokio::test] async fn account_code_insertion_no_duplicates() -> anyhow::Result<()> { let store = create_test_store().await; - let component_code = CodeBuilder::default() - .compile_component_code("miden::testing::dummy_component", "pub proc dummy nop end")?; + let component_code = CodeBuilder::default().compile_component_code( + "miden::testing::dummy_component", + "@account_procedure\npub proc dummy nop end", + )?; let account_component = AccountComponent::new( component_code, vec![], @@ -245,10 +247,11 @@ async fn apply_account_patch_preserves_fungible_callback_flag() -> anyhow::Resul .await?; // A fungible asset that carries an *enabled* callback flag (as agglayer-minted assets do). - let callback_asset: Asset = - FungibleAsset::new(AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)?, 100)? - .with_callbacks(AssetCallbackFlag::Enabled) - .into(); + let callback_asset: Asset = FungibleAsset::new( + AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_WITH_CALLBACKS)?, + 100, + )? + .into(); // The account starts with an empty vault, so the absolute value of the added asset is the // asset itself. @@ -368,7 +371,7 @@ async fn apply_account_patch_removals() -> anyhow::Result<()> { // key is marked as removed. let mut vault_patch = AccountVaultPatch::default(); for asset in &assets { - vault_patch.remove_asset(asset.vault_key()); + vault_patch.remove_asset(asset.id()); } let patch = @@ -1574,7 +1577,7 @@ async fn undo_after_update_account_state_does_not_resurrect_removed_entries() -> )])?; // Y is removed, so its vault key is marked as removed (absolute final vault is {X}). let mut vault_patch_remove = AccountVaultPatch::default(); - vault_patch_remove.remove_asset(asset_y.vault_key()); + vault_patch_remove.remove_asset(asset_y.id()); let patch_remove = AccountPatch::new( account_id, storage_patch_remove, diff --git a/crates/sqlite-store/src/account/vault.rs b/crates/sqlite-store/src/account/vault.rs index e2449e50f6..ef5cc33ba9 100644 --- a/crates/sqlite-store/src/account/vault.rs +++ b/crates/sqlite-store/src/account/vault.rs @@ -7,7 +7,7 @@ use miden_client::Serializable; use miden_client::account::{AccountHeader, AccountId, AccountVaultPatch}; use miden_client::asset::Asset; use miden_client::store::{AccountSmtForest, StoreError}; -use miden_protocol::asset::AssetVaultKey; +use miden_protocol::asset::AssetId; use miden_protocol::crypto::merkle::MerkleError; use rusqlite::types::Value; use rusqlite::{OptionalExtension, Transaction, params}; @@ -37,7 +37,7 @@ impl SqliteStore { let account_id_bytes = account_id.to_bytes(); for asset in assets { - let vault_key_hex = asset.vault_key().to_string(); + let vault_key_hex = asset.id().to_word().to_hex(); let asset_hex = asset.to_value_word().to_hex(); latest_stmt @@ -70,8 +70,7 @@ impl SqliteStore { // or signed-amount arithmetic is needed, and the asset value word already encodes the // callback flag for both fungible and non-fungible assets. let updated_assets_values: Vec = vault_patch.updated_assets().collect(); - let removed_vault_keys: Vec = - vault_patch.removed_asset_keys().copied().collect(); + let removed_vault_keys: Vec = vault_patch.removed_asset_ids().copied().collect(); Self::persist_vault_delta( tx, @@ -102,7 +101,7 @@ impl SqliteStore { tx: &Transaction<'_>, account_id_bytes: &[u8], nonce_val: &rusqlite::types::Value, - removed_vault_keys: &[AssetVaultKey], + removed_vault_keys: &[AssetId], updated_assets: &[Asset], ) -> Result<(), StoreError> { const READ_OLD_ASSET: &str = @@ -123,7 +122,7 @@ impl SqliteStore { // Archive and delete removed assets for vault_key in removed_vault_keys { - let vault_key_hex = vault_key.to_string(); + let vault_key_hex = vault_key.to_word().to_hex(); // Read old asset value from latest (should exist since we're removing it) let old_asset: Option = tx @@ -151,7 +150,7 @@ impl SqliteStore { Rc::new( removed_vault_keys .iter() - .map(|k| Value::from(k.to_string())) + .map(|k| Value::from(k.to_word().to_hex())) .collect::>(), ), ], @@ -161,7 +160,7 @@ impl SqliteStore { // Archive old values and insert updated assets for asset in updated_assets { - let vault_key_hex = asset.vault_key().to_string(); + let vault_key_hex = asset.id().to_word().to_hex(); let asset_hex = asset.to_value_word().to_hex(); // Read old asset value from latest (NULL if asset is new) diff --git a/crates/sqlite-store/src/lib.rs b/crates/sqlite-store/src/lib.rs index 754e410a42..1866f305cc 100644 --- a/crates/sqlite-store/src/lib.rs +++ b/crates/sqlite-store/src/lib.rs @@ -54,7 +54,7 @@ use miden_client::sync::{NoteTagRecord, StateSyncUpdate}; use miden_client::transaction::{TransactionRecord, TransactionStoreUpdate}; use miden_protocol::Felt; use miden_protocol::account::StorageMapWitness; -use miden_protocol::asset::AssetVaultKey; +use miden_protocol::asset::AssetId; use rusqlite::Connection; use rusqlite::types::Value; use sql_error::SqlResultExt; @@ -496,7 +496,7 @@ impl Store for SqliteStore { async fn get_account_asset( &self, account_id: AccountId, - vault_key: AssetVaultKey, + vault_key: AssetId, ) -> Result, StoreError> { let smt_forest = self.smt_forest.clone(); self.interact_with_connection(move |conn| { diff --git a/crates/testing/miden-client-tests/src/tests.rs b/crates/testing/miden-client-tests/src/tests.rs index c14389eb57..35562773ed 100644 --- a/crates/testing/miden-client-tests/src/tests.rs +++ b/crates/testing/miden-client-tests/src/tests.rs @@ -6,6 +6,7 @@ use std::env::temp_dir; use std::println; use std::sync::Arc; +use miden_client::ClientError; use miden_client::account::{Address, AddressInterface}; use miden_client::address::RoutingParameters; use miden_client::assembly::CodeBuilder; @@ -60,7 +61,6 @@ use miden_client::transaction::{ TransactionStatus, }; use miden_client::utils::{Deserializable, Serializable}; -use miden_client::{ClientError, DebugMode}; use miden_client_sqlite_store::ClientBuilderSqliteExt; use miden_protocol::account::{ Account, @@ -72,21 +72,14 @@ use miden_protocol::account::{ AccountId, AccountIdVersion, AccountType, + AssetCallbackFlag, StorageMap, StorageMapKey, StorageSlot, StorageSlotContent, StorageSlotName, }; -use miden_protocol::asset::{ - Asset, - AssetAmount, - AssetCallbackFlag, - AssetVaultKey, - AssetWitness, - FungibleAsset, - TokenSymbol, -}; +use miden_protocol::asset::{Asset, AssetAmount, AssetId, FungibleAsset, TokenSymbol}; use miden_protocol::crypto::rand::{FeltRng, RandomCoin}; use miden_protocol::note::{ Note, @@ -131,7 +124,7 @@ use miden_standards::testing::note::NoteBuilder; use miden_standards::tx_script::SendNotesTransactionScriptError; use miden_testing::{MockChain, MockChainBuilder, TxContextInput}; use rand::rngs::StdRng; -use rand::{Rng, RngCore, SeedableRng}; +use rand::{Rng, RngExt, SeedableRng}; use rstest::rstest; mod batch; @@ -1007,6 +1000,11 @@ async fn import_processing_note_returns_error() { )); } +// TODO: fix - blocked by an upstream miden-standards bug (0.16.0-alpha.2). The +// `send_notes_script.rs::move_asset_to_note_body` helper only emits the `pad(21)->pad(16)` stack +// reduction inside the per-asset loop, so a zero-asset output note created from a basic wallet +// returns at stack depth 21 and the VM rejects the transaction with `InvalidStackDepthOnReturn`. +// Re-enable once the standards send-notes script handles zero-asset notes. #[tokio::test] async fn note_without_asset() { let (mut client, _rpc_api, keystore) = Box::pin(create_test_client()).await; @@ -1092,7 +1090,8 @@ async fn execute_program() { let code = " use miden::core::sys - begin + @transaction_script + pub proc main push.16 repeat.16 dup push.1 sub @@ -1372,7 +1371,6 @@ async fn input_note_reader_finds_externally_consumed_notes() { .rng(Box::new(rng)) .sqlite_store(create_test_store_path()) .authenticator(Arc::new(keystore)) - .in_debug_mode(DebugMode::Enabled) .tx_discard_delta(None) .build() .await @@ -1482,7 +1480,6 @@ async fn import_by_id_already_consumed_note_is_findable_by_id() { .rng(Box::new(rng)) .sqlite_store(create_test_store_path()) .authenticator(Arc::new(keystore)) - .in_debug_mode(DebugMode::Enabled) .tx_discard_delta(None) .build() .await @@ -1583,7 +1580,6 @@ async fn setup_prunable_block_scenario( .rng(Box::new(rng)) .sqlite_store(create_test_store_path()) .authenticator(Arc::new(keystore)) - .in_debug_mode(DebugMode::Enabled) .tx_discard_delta(None) .irrelevant_block_prune_interval(prune_interval) .build() @@ -2863,7 +2859,7 @@ async fn pswap_fill_test( assert_eq!( alice_account .vault() - .get_balance(AssetVaultKey::new_fungible(eth_faucet.id(), AssetCallbackFlag::Disabled,)) + .get_balance(AssetId::new_fungible(eth_faucet.id())) .unwrap() .as_u64(), MINT_AMOUNT - offered_amount, @@ -2888,7 +2884,7 @@ async fn pswap_fill_test( assert_eq!( bob_account .vault() - .get_balance(AssetVaultKey::new_fungible(usd_faucet.id(), AssetCallbackFlag::Disabled,)) + .get_balance(AssetId::new_fungible(usd_faucet.id())) .unwrap() .as_u64(), MINT_AMOUNT - account_fill_amount, @@ -2899,7 +2895,7 @@ async fn pswap_fill_test( assert_eq!( bob_account .vault() - .get_balance(AssetVaultKey::new_fungible(eth_faucet.id(), AssetCallbackFlag::Disabled,)) + .get_balance(AssetId::new_fungible(eth_faucet.id())) .unwrap() .as_u64(), expected_payout, @@ -2926,7 +2922,7 @@ async fn pswap_fill_test( "remainder offered amount should reflect the unfilled portion" ); assert_eq!( - remainder.storage().requested_asset_amount(), + remainder.storage().min_requested_amount(), rem_requested, "remainder requested amount should reflect the unfilled portion" ); @@ -2984,7 +2980,7 @@ async fn pswap_cancel_test() { assert_eq!( alice_account .vault() - .get_balance(AssetVaultKey::new_fungible(eth_faucet.id(), AssetCallbackFlag::Disabled,)) + .get_balance(AssetId::new_fungible(eth_faucet.id())) .unwrap() .as_u64(), MINT_AMOUNT - offered_amount, @@ -3007,7 +3003,7 @@ async fn pswap_cancel_test() { assert_eq!( alice_account .vault() - .get_balance(AssetVaultKey::new_fungible(eth_faucet.id(), AssetCallbackFlag::Disabled,)) + .get_balance(AssetId::new_fungible(eth_faucet.id())) .unwrap() .as_u64(), MINT_AMOUNT, @@ -3035,7 +3031,6 @@ async fn create_pswap_test_client( .rng(Box::new(rng)) .sqlite_store(create_test_store_path()) .authenticator(Arc::new(keystore.clone())) - .in_debug_mode(DebugMode::Enabled) .tx_discard_delta(None) .build() .await @@ -3222,7 +3217,7 @@ async fn pswap_chain_tracking_test(#[case] note_type: NoteType) { assert_eq!( alice_account .vault() - .get_balance(AssetVaultKey::new_fungible(eth_faucet.id(), AssetCallbackFlag::Disabled)) + .get_balance(AssetId::new_fungible(eth_faucet.id())) .unwrap() .as_u64(), 25, @@ -3234,7 +3229,7 @@ async fn pswap_chain_tracking_test(#[case] note_type: NoteType) { assert_eq!( alice_account .vault() - .get_balance(AssetVaultKey::new_fungible(btc_faucet.id(), AssetCallbackFlag::Disabled)) + .get_balance(AssetId::new_fungible(btc_faucet.id())) .unwrap() .as_u64(), MINT_AMOUNT - 50, @@ -3360,7 +3355,7 @@ async fn pswap_full_fill_chain_tracking_test(#[case] note_type: NoteType) { assert_eq!( alice_account .vault() - .get_balance(AssetVaultKey::new_fungible(eth_faucet.id(), AssetCallbackFlag::Disabled)) + .get_balance(AssetId::new_fungible(eth_faucet.id())) .unwrap() .as_u64(), requested_amount, @@ -3369,7 +3364,7 @@ async fn pswap_full_fill_chain_tracking_test(#[case] note_type: NoteType) { assert_eq!( alice_account .vault() - .get_balance(AssetVaultKey::new_fungible(btc_faucet.id(), AssetCallbackFlag::Disabled)) + .get_balance(AssetId::new_fungible(btc_faucet.id())) .unwrap() .as_u64(), MINT_AMOUNT - offered_amount, @@ -3518,7 +3513,7 @@ async fn pswap_multi_round_chain_tracking_test() { assert_eq!( alice_account .vault() - .get_balance(AssetVaultKey::new_fungible(eth_faucet.id(), AssetCallbackFlag::Disabled)) + .get_balance(AssetId::new_fungible(eth_faucet.id())) .unwrap() .as_u64(), 35, @@ -3528,7 +3523,7 @@ async fn pswap_multi_round_chain_tracking_test() { assert_eq!( alice_account .vault() - .get_balance(AssetVaultKey::new_fungible(btc_faucet.id(), AssetCallbackFlag::Disabled)) + .get_balance(AssetId::new_fungible(btc_faucet.id())) .unwrap() .as_u64(), MINT_AMOUNT - 70, @@ -3540,7 +3535,7 @@ async fn pswap_multi_round_chain_tracking_test() { assert_eq!( bob_account .vault() - .get_balance(AssetVaultKey::new_fungible(btc_faucet.id(), AssetCallbackFlag::Disabled)) + .get_balance(AssetId::new_fungible(btc_faucet.id())) .unwrap() .as_u64(), 70, @@ -3549,7 +3544,7 @@ async fn pswap_multi_round_chain_tracking_test() { assert_eq!( bob_account .vault() - .get_balance(AssetVaultKey::new_fungible(eth_faucet.id(), AssetCallbackFlag::Disabled)) + .get_balance(AssetId::new_fungible(eth_faucet.id())) .unwrap() .as_u64(), MINT_AMOUNT - 35, @@ -3722,6 +3717,7 @@ const BUMP_MAP_CODE: &str = r#" const MAP_SLOT = word("miden::testing::bump_map::map") + @account_procedure pub proc bump_map_item # map key push.{map_key} @@ -3788,7 +3784,8 @@ async fn storage_and_vault_proofs() { .unwrap() .compile_tx_script( "use external_contract::bump_item_contract - begin + @transaction_script + pub proc main call.bump_item_contract::bump_map_item end", ) @@ -3854,7 +3851,7 @@ async fn storage_and_vault_proofs() { assert_eq!(account_vault_root, vault.root()); // Check that specific asset proof matches the one in the vault - let vault_key = AssetVaultKey::new_fungible(faucet_account_id, AssetCallbackFlag::Disabled); + let vault_key = AssetId::new_fungible(faucet_account_id); let (asset, witness) = client .test_store() .get_account_asset(account_id, vault_key) @@ -3862,8 +3859,7 @@ async fn storage_and_vault_proofs() { .unwrap() .unwrap(); - let expected_witness = - AssetWitness::new(vault.open(asset.vault_key()).into(), [asset.vault_key()]).unwrap(); + let expected_witness = vault.open(asset.id()); assert_eq!(witness, expected_witness); // Check that specific map item proof matches the one in the storage @@ -3997,7 +3993,6 @@ async fn import_watched_account_by_id_rejects_already_tracked_native_account() { .rng(Box::new(rng)) .sqlite_store(create_test_store_path()) .authenticator(Arc::new(keystore)) - .in_debug_mode(DebugMode::Enabled) .build() .await .unwrap(); @@ -4034,6 +4029,11 @@ async fn import_watched_account_by_id_rejects_already_tracked_native_account() { assert!(!account_record.is_watched()); } +// TODO: fix - blocked by an upstream miden-standards bug (0.16.0-alpha.2). Creating the zero-asset +// output note from a basic wallet hits `send_notes_script.rs::move_asset_to_note_body`, whose +// `pad(21)->pad(16)` stack reduction only runs inside the per-asset loop; with no assets the tx +// script returns at stack depth 21 and the VM rejects it with `InvalidStackDepthOnReturn`. +// Re-enable once the standards send-notes script handles zero-asset notes. #[tokio::test] async fn consume_note_with_custom_script() { let (mut client, mock_rpc_api, keystore) = create_test_client().await; @@ -4056,9 +4056,10 @@ async fn consume_note_with_custom_script() { client.sync_state().await.unwrap(); let custom_note_script = " + use miden::core::sys @note_script pub proc main - nop + exec.sys::truncate_stack end "; let note_script = client.code_builder().compile_note_script(custom_note_script).unwrap(); @@ -4238,7 +4239,12 @@ async fn sync_stores_private_note_attachments() { // 1. Build a mock chain with a sender and a public target account (the attachment target must // be public). let mut mock_chain_builder = MockChainBuilder::new(); - let faucet_id = AccountId::dummy([7u8; 15], AccountIdVersion::Version1, AccountType::Public); + let faucet_id = AccountId::dummy( + [7u8; 15], + AccountIdVersion::Version1, + AccountType::Public, + AssetCallbackFlag::Disabled, + ); let note_asset = FungibleAsset::new(faucet_id, 100).unwrap(); let sender = mock_chain_builder .add_existing_mock_account_with_assets(miden_testing::Auth::IncrNonce, [note_asset.into()]) @@ -4297,7 +4303,6 @@ async fn sync_stores_private_note_attachments() { .rng(Box::new(rng)) .sqlite_store(create_test_store_path()) .authenticator(Arc::new(keystore)) - .in_debug_mode(DebugMode::Enabled) .tx_discard_delta(None) .build() .await @@ -4445,7 +4450,6 @@ async fn sync_large_public_account() { .rng(Box::new(rng)) .sqlite_store(create_test_store_path()) .authenticator(Arc::new(keystore)) - .in_debug_mode(DebugMode::Enabled) .build() .await .unwrap(); @@ -4565,7 +4569,6 @@ pub async fn create_test_client_builder() .rng(Box::new(rng)) .sqlite_store(create_test_store_path()) .authenticator(Arc::new(keystore.clone())) - .in_debug_mode(DebugMode::Enabled) .tx_discard_delta(None); (builder, rpc_api, keystore) @@ -4849,7 +4852,8 @@ async fn storage_and_vault_proofs_ecdsa() { .unwrap() .compile_tx_script( "use external_contract::bump_item_contract - begin + @transaction_script + pub proc main call.bump_item_contract::bump_map_item end", ) @@ -4915,7 +4919,7 @@ async fn storage_and_vault_proofs_ecdsa() { assert_eq!(account_vault_root, vault.root()); // Check that specific asset proof matches the one in the vault - let vault_key = AssetVaultKey::new_fungible(faucet_account_id, AssetCallbackFlag::Disabled); + let vault_key = AssetId::new_fungible(faucet_account_id); let (asset, witness) = client .test_store() .get_account_asset(account_id, vault_key) @@ -4923,8 +4927,7 @@ async fn storage_and_vault_proofs_ecdsa() { .unwrap() .unwrap(); - let expected_witness = - AssetWitness::new(vault.open(asset.vault_key()).into(), [asset.vault_key()]).unwrap(); + let expected_witness = vault.open(asset.id()); assert_eq!(witness, expected_witness); // Check that specific map item proof matches the one in the storage diff --git a/crates/testing/miden-client-tests/src/tests/batch.rs b/crates/testing/miden-client-tests/src/tests/batch.rs index cbf0e829b9..e5d3133508 100644 --- a/crates/testing/miden-client-tests/src/tests/batch.rs +++ b/crates/testing/miden-client-tests/src/tests/batch.rs @@ -1,6 +1,7 @@ use std::collections::BTreeSet; use std::sync::Arc; +use miden_client::ClientError; use miden_client::account::AccountType; use miden_client::asset::{Asset, FungibleAsset}; use miden_client::auth::RPO_FALCON_SCHEME_ID; @@ -24,7 +25,6 @@ use miden_client::transaction::{ TransactionRequestBuilder, TransactionStoreUpdate, }; -use miden_client::{ClientError, DebugMode}; use miden_client_sqlite_store::ClientBuilderSqliteExt; use miden_protocol::Felt; use miden_protocol::crypto::rand::RandomCoin; @@ -167,7 +167,6 @@ async fn apply_transaction_batch_rolls_back_on_mid_batch_failure() { .rng(Box::new(rng)) .sqlite_store(create_test_store_path()) .authenticator(Arc::new(keystore)) - .in_debug_mode(DebugMode::Enabled) .tx_discard_delta(None) .build() .await @@ -443,7 +442,6 @@ async fn batch_builder_submits_txs_across_multiple_accounts() { .rng(Box::new(rng)) .sqlite_store(create_test_store_path()) .authenticator(Arc::new(keystore)) - .in_debug_mode(DebugMode::Enabled) .tx_discard_delta(None) .build() .await diff --git a/crates/testing/miden-client-tests/src/tests/store.rs b/crates/testing/miden-client-tests/src/tests/store.rs index 621bdfd81f..c720f7970c 100644 --- a/crates/testing/miden-client-tests/src/tests/store.rs +++ b/crates/testing/miden-client-tests/src/tests/store.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; use std::collections::BTreeSet; use std::sync::Arc; -use miden_client::assembly::{CodeBuilder, Module, ModuleKind, Path, SourceManagerSync}; +use miden_client::assembly::{CodeBuilder, SourceManagerSync}; use miden_client::auth::{AuthSchemeId, AuthSecretKey, AuthSingleSig, PublicKeyCommitment}; use miden_client::keystore::Keystore; use miden_client::store::AccountStorageFilter; @@ -25,13 +25,12 @@ use miden_protocol::testing::account_id::{ ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, }; -use miden_protocol::transaction::TransactionKernel; use miden_protocol::{EMPTY_WORD, Felt, Word, ZERO}; use miden_standards::account::AccountBuilderSchemaCommitmentExt; use miden_standards::account::auth::Approver; use miden_standards::account::wallets::BasicWallet; use miden_standards::testing::mock_account::MockAccountExt; -use rand::RngCore; +use rand::Rng; use crate::tests::{create_test_client, insert_new_fungible_faucet, insert_new_wallet}; @@ -269,6 +268,7 @@ const SLOTS_COMPONENT_MASM: &str = r#" const SLOT_A = word("test::pruning::slot_a") const SLOT_B = word("test::pruning::slot_b") + @account_procedure pub proc set_a_to_10 push.0.0.0.10 push.SLOT_A[0..2] @@ -277,6 +277,7 @@ const SLOTS_COMPONENT_MASM: &str = r#" exec.sys::truncate_stack end + @account_procedure pub proc set_b_to_20 push.0.0.0.20 push.SLOT_B[0..2] @@ -349,22 +350,13 @@ fn compile_slot_tx_script( proc_name: &str, source_manager: Arc, ) -> miden_client::transaction::TransactionScript { - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); - let module = Module::parser(ModuleKind::Library) - .parse_str( - Path::new("external_contract::slots_contract"), - SLOTS_COMPONENT_MASM, - source_manager.clone(), - ) - .unwrap(); - let library = assembler.assemble_library([module]).unwrap(); - CodeBuilder::with_source_manager(source_manager) - .with_dynamically_linked_library(library) + .with_linked_module("external_contract::slots_contract", SLOTS_COMPONENT_MASM) .unwrap() .compile_tx_script(format!( "use external_contract::slots_contract - begin + @transaction_script + pub proc main call.slots_contract::{proc_name} end" )) diff --git a/crates/testing/miden-client-tests/src/tests/transaction.rs b/crates/testing/miden-client-tests/src/tests/transaction.rs index 26f5a10f6b..70c31b3ae5 100644 --- a/crates/testing/miden-client-tests/src/tests/transaction.rs +++ b/crates/testing/miden-client-tests/src/tests/transaction.rs @@ -107,7 +107,7 @@ async fn transaction_error_reports_source_line() { let failing_script = client .code_builder() - .compile_tx_script("begin push.0 push.2 assert_eq end") + .compile_tx_script("@transaction_script pub proc main push.0 push.2 assert_eq end") .unwrap(); let tx_request = @@ -180,7 +180,7 @@ async fn execute_transaction_failure_leaves_store_unchanged() { // succeeded. let failing_script = client .code_builder() - .compile_tx_script("begin push.0 push.2 assert_eq end") + .compile_tx_script("@transaction_script pub proc main push.0 push.2 assert_eq end") .unwrap(); let tx_request = TransactionRequestBuilder::new() @@ -306,6 +306,7 @@ async fn lazy_foreign_account_loading() { format!( r#" const STORAGE_MAP_SLOT = word("miden::testing::fpi::map") + @account_procedure pub proc get_map_item push.{map_key} push.STORAGE_MAP_SLOT[0..2] @@ -373,7 +374,8 @@ async fn lazy_foreign_account_loading() { .compile_tx_script(format!( " use miden::protocol::tx - begin + @transaction_script + pub proc main push.{proc_root} push.{prefix} push.{suffix} exec.tx::execute_foreign_procedure diff --git a/crates/testing/miden-client-tests/src/tests/transport.rs b/crates/testing/miden-client-tests/src/tests/transport.rs index 146ea84946..17c40f32fd 100644 --- a/crates/testing/miden-client-tests/src/tests/transport.rs +++ b/crates/testing/miden-client-tests/src/tests/transport.rs @@ -1,7 +1,6 @@ use std::env::temp_dir; use std::sync::Arc; -use miden_client::DebugMode; use miden_client::account::{Account, AccountType}; use miden_client::address::{Address, AddressInterface, RoutingParameters}; use miden_client::builder::ClientBuilder; @@ -19,7 +18,12 @@ use miden_client::testing::note_transport::{ use miden_client::utils::RwLock; use miden_client_sqlite_store::ClientBuilderSqliteExt; use miden_protocol::Felt; -use miden_protocol::account::{AccountId, AccountIdVersion, AccountType as ProtocolAccountType}; +use miden_protocol::account::{ + AccountId, + AccountIdVersion, + AccountType as ProtocolAccountType, + AssetCallbackFlag, +}; use miden_protocol::asset::{Asset, FungibleAsset}; use miden_protocol::block::BlockNumber; use miden_protocol::crypto::rand::RandomCoin; @@ -29,7 +33,7 @@ use miden_protocol::utils::serde::Serializable; use miden_standards::note::P2idNote; use miden_standards::testing::note::NoteBuilder; use miden_testing::{MockChainBuilder, TxContextInput}; -use rand::Rng; +use rand::RngExt; use crate::tests::{create_test_client_builder, insert_new_wallet}; @@ -321,7 +325,6 @@ async fn fetch_private_notes_finds_note_committed_at_sync_height() { .rng(Box::new(rng)) .sqlite_store(create_test_store_path()) .authenticator(Arc::new(keystore)) - .in_debug_mode(DebugMode::Enabled) .tx_discard_delta(None) .note_transport(Arc::new(transport_client)); @@ -645,8 +648,12 @@ async fn fetch_private_notes_without_floor_falls_back_to_lookback_window() { /// A dummy fungible asset for transport-layer notes. P2ID notes require at least one asset, and /// these notes are never consumed on-chain, so the issuing faucet only needs to be a valid ID. fn dummy_asset() -> Asset { - let faucet_id = - AccountId::dummy([7u8; 15], AccountIdVersion::Version1, ProtocolAccountType::Public); + let faucet_id = AccountId::dummy( + [7u8; 15], + AccountIdVersion::Version1, + ProtocolAccountType::Public, + AssetCallbackFlag::Disabled, + ); FungibleAsset::new(faucet_id, 100).unwrap().into() } @@ -750,7 +757,6 @@ async fn committed_private_note_recipient( .rng(Box::new(rng)) .sqlite_store(create_test_store_path()) .authenticator(Arc::new(keystore)) - .in_debug_mode(DebugMode::Enabled) .tx_discard_delta(None) .note_transport(Arc::new(transport_client)); diff --git a/crates/testing/test-node-genesis/src/agglayer.rs b/crates/testing/test-node-genesis/src/agglayer.rs index e0994f64b0..d00590a044 100644 --- a/crates/testing/test-node-genesis/src/agglayer.rs +++ b/crates/testing/test-node-genesis/src/agglayer.rs @@ -1,7 +1,7 @@ //! Builds the agglayer genesis accounts (bridge admin, GER manager, bridge, faucet) included in //! the genesis configuration when agglayer support is requested. -use ::rand::{Rng, random}; +use ::rand::{RngExt, random}; use anyhow::{Context, Result}; use miden_agglayer::{create_agglayer_faucet, create_bridge_account}; use miden_protocol::account::auth::{AuthScheme, AuthSecretKey}; diff --git a/crates/testing/test-node-genesis/src/lib.rs b/crates/testing/test-node-genesis/src/lib.rs index 18ea25b41c..ef6dd1b129 100644 --- a/crates/testing/test-node-genesis/src/lib.rs +++ b/crates/testing/test-node-genesis/src/lib.rs @@ -8,7 +8,7 @@ use std::fmt::Write as _; use std::path::Path; use std::time::{SystemTime, UNIX_EPOCH}; -use ::rand::{Rng, random}; +use ::rand::{RngExt, random}; use anyhow::{Context, Result}; use miden_protocol::account::auth::{AuthScheme, AuthSecretKey}; use miden_protocol::account::{ diff --git a/docs/external/src/rust-client/cli/cli-config.md b/docs/external/src/rust-client/cli/cli-config.md index ae323b1e11..28bf5d15bd 100644 --- a/docs/external/src/rust-client/cli/cli-config.md +++ b/docs/external/src/rust-client/cli/cli-config.md @@ -180,8 +180,6 @@ miden-client init --block-delta 256 Note that this only affects the **global** directory. If a local `./.miden` directory exists, it still takes precedence over the global one (whether default or overridden). -- `MIDEN_DEBUG`: When set to `true`, enables debug mode on the transaction executor. Any `debug.*` instructions in an executed script then print VM state to standard output to facilitate MASM debugging (see [MASM Debug Output](../debug-output.md)). This variable can be overridden by the `--debug` CLI flag. - ### Note Transport A `note-transport` section is used to configure the connection to the Miden Note Transport node used in the exchange of private notes. It contains the following fields: diff --git a/docs/external/src/rust-client/cli/cli-troubleshooting.md b/docs/external/src/rust-client/cli/cli-troubleshooting.md index 4c7472b3f3..10082b2166 100644 --- a/docs/external/src/rust-client/cli/cli-troubleshooting.md +++ b/docs/external/src/rust-client/cli/cli-troubleshooting.md @@ -14,22 +14,11 @@ This guide helps you troubleshoot common issues and understand the end-to-end li - Ensure you have a proper configuration setup: either a global config at `~/.miden/miden-client.toml` or a local config at `./.miden/miden-client.toml`. Local config takes priority if both exist. - If you need a clean local state, delete the SQLite store file referenced by `store_filepath` (default: `.miden/store.sqlite3`). It will be recreated automatically on the next command. - Verify your node RPC endpoint is reachable and correct in your configuration file (local `.miden/miden-client.toml` or global `~/.miden/miden-client.toml`). -- Run with debug output when troubleshooting: add `--debug` or set `MIDEN_DEBUG=true`. - Run `miden-client sync` to refresh local state after errors involving missing data or outdated heights. -### Enable debug output - -- CLI flag: `miden-client --debug ...` (overrides `MIDEN_DEBUG`) -- Environment variable: `MIDEN_DEBUG=true` - -When enabled, the transaction executor prints the output of any `debug.*` instructions in an executed script to standard output, which helps diagnose MASM-level issues. See [MASM Debug Output](../debug-output.md) for details. - -### Typical CLI outputs when debugging +### Typical CLI outputs ```sh -# Enable debug output for a command -miden-client --debug send --sender --target --asset 100:: - # Force non-interactive submission (e.g., CI) miden-client send --force ... @@ -81,7 +70,7 @@ Below are representative errors you may encounter, their likely causes, and sugg #### `ClientError.TransactionInputError` / `TransactionScriptError` - Cause: Invalid transaction inputs, script logic errors, or failing constraints. -- Fix: Run with `--debug` to collect execution logs. Validate input notes, foreign accounts, and script assumptions. +- Fix: Validate input notes, foreign accounts, and script assumptions. #### `ClientError.TransactionProvingError` - Cause: Local proving failed or remote prover returned an error. @@ -132,16 +121,14 @@ Key states the CLI surfaces: ### Recovery flow -1. Re-run with `--debug` or `MIDEN_DEBUG=true` for richer logs. -2. Verify `rpc.endpoint` connectivity and timeouts. -3. Run `miden-client sync` to refresh local headers/notes. -4. If local DB is inconsistent for development purposes, delete the store file (`.miden/store.sqlite3` in local config or `~/.miden/store.sqlite3` in global config) and retry. +1. Verify `rpc.endpoint` connectivity and timeouts. +2. Run `miden-client sync` to refresh local headers/notes. +3. If local DB is inconsistent for development purposes, delete the store file (`.miden/store.sqlite3` in local config or `~/.miden/store.sqlite3` in global config) and retry. 5. For configuration issues, use `miden-client clear-config` to reset config and `miden-client init` to recreate. 6. Adjust `max_block_number_delta` if strict recency checks block validation. 7. If proving errors persist with a remote prover, confirm `remote_prover_endpoint` and consider running locally to isolate the issue. ### References -- CLI debug flag and environment variable are documented in `CLI` and `Config` docs. - Common error enums originate from the client and RPC layers. - Protocol lifecycle: [Miden book — Transaction lifecycle](https://docs.miden.xyz/builder/smart-contracts/transactions/introduction#transaction-lifecycle) diff --git a/docs/external/src/rust-client/cli/index.md b/docs/external/src/rust-client/cli/index.md index 6e7ca0f962..659f6be50a 100644 --- a/docs/external/src/rust-client/cli/index.md +++ b/docs/external/src/rust-client/cli/index.md @@ -16,14 +16,6 @@ Call a command on the `miden-client` like this: miden-client ``` -Optionally, you can include the `--debug` flag to run the command with debug mode, which enables debug output logs from scripts that were compiled in this mode: - -```sh -miden-client --debug -``` - -Note that the debug flag overrides the `MIDEN_DEBUG` environment variable. - ## Commands ### `init` diff --git a/docs/external/src/rust-client/debug-output.md b/docs/external/src/rust-client/debug-output.md index e2f3679442..43adfce883 100644 --- a/docs/external/src/rust-client/debug-output.md +++ b/docs/external/src/rust-client/debug-output.md @@ -5,30 +5,39 @@ sidebar_position: 8 # MASM Debug Output -Miden Assembly's `debug.*` instructions (`debug.stack`, `debug.stack.`, `debug.mem`, -`debug.local..`, `debug.adv_stack.` — see the -[assembly reference](https://github.com/0xMiden/miden-vm/blob/next/docs/src/user_docs/assembly/debugging.md)) -print VM state to the client's standard output while a script runs. This is a lightweight -alternative to [interactive DAP debugging](./debugging.md). - -They only print when the client that **executes** the script is in debug mode — enable it with -`.in_debug_mode(DebugMode::Enabled)` on the builder, or run the CLI with `--debug` (or -`MIDEN_DEBUG=true`). Compilation is unaffected; the decorators are always retained in freshly -compiled scripts. Output goes to the client's standard output (not `tracing`/`RUST_LOG`, not the -node logs). +To print VM state to standard output while a script runs, call the `miden::core::debug` procedures +from your Miden Assembly. The client's transaction executor prints their output by default, so there +is nothing to enable. Debugging is opt-in per script: output is produced only where a script calls a +print procedure. + +## Procedures + +| Procedure | Prints | +| --------- | ------ | +| `print_stack` | the entire operand stack | +| `print_mem` | memory over a `[start, end)` range (consumes the two range arguments, at most 1024 addresses) | +| `print_mem_addr` | the memory cell at a single address | +| `print_mem_all` | every initialized memory cell of the current context | + +The operand-stack and memory printers are enabled by default. The advice-stack and advice-map +printers are not, because they can expose witness data. ## Example -Compile and execute a script containing `debug.stack` with a debug-mode client: +Compile and execute a script that prints the operand stack: ```rust -// Client built with `.in_debug_mode(DebugMode::Enabled)`. let tx_script = client.code_builder().compile_tx_script( " - begin + use miden::core::debug + use miden::core::sys + + @transaction_script + pub proc main push.1.2.3 - debug.stack.3 + exec.debug::print_stack drop drop drop + exec.sys::truncate_stack end ", )?; @@ -38,15 +47,15 @@ client .await?; ``` -Executing it prints the top three stack elements to the client's standard output (the step count -includes the transaction prologue that runs before the script): +Executing it prints the operand stack to standard output (the step count includes the transaction +prologue that runs before the script): ```text -Stack state in interval [0, 2] before step 2419: -├── 0: 3 -├── 1: 2 -├── 2: 1 -└── (16 more items) +Stack state before step 2506: +├── 0: 3 +├── 1: 2 +├── 2: 1 +└── (rest of the stack) ``` :::note diff --git a/docs/external/src/rust-client/debugging.md b/docs/external/src/rust-client/debugging.md index 14b9811453..3a096e512c 100644 --- a/docs/external/src/rust-client/debugging.md +++ b/docs/external/src/rust-client/debugging.md @@ -7,6 +7,16 @@ sidebar_position: 7 The Miden client supports interactive debugging via the [Debug Adapter Protocol (DAP)](https://microsoft.github.io/debug-adapter-protocol/). You can debug both raw Miden Assembly scripts and Rust programs compiled to Miden via `midenc`. This lets you step through execution, set breakpoints, and inspect stack/memory state using any DAP-compatible client (e.g. VS Code, the `miden-debug` TUI). +:::warning +Interactive DAP debugging is **temporarily unavailable in this release.** The `miden-debug` 0.9 +executor drives execution from a compiled package, but the VM's `ProgramExecutor` interface the +client integrates against provides only a bare program, and there is no lossless conversion between +the two. Starting a DAP session (`--start-debug-adapter`) therefore fails at execution time with an +explicit "DAP debugging is not supported with the current miden-debug backend" error. The `dap` +feature and CLI flag remain so the surface is preserved; support will return once the upstream +executor accepts a program directly. +::: + ## Feature flags Two feature flags control debugging support: diff --git a/docs/external/src/rust-client/library.md b/docs/external/src/rust-client/library.md index ccfc3ea76c..84842649e7 100644 --- a/docs/external/src/rust-client/library.md +++ b/docs/external/src/rust-client/library.md @@ -58,7 +58,6 @@ let client = ClientBuilder::new() .filesystem_keystore("path/to/keys")? // Optional: custom prover via .prover(Arc::new(prover)) // Optional: note transport via .note_transport(Arc::new(nt_client)) - // Optional: debug mode via .in_debug_mode(DebugMode::Enabled) // Optional: custom source manager via .source_manager(Arc::new(sm)) — only // needed when compiling scripts outside the client with an external // `Assembler`; pass the same `Arc` to both so source spans align. diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ee57280e6c..b462201c78 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.93" +channel = "1.96" components = ["clippy", "rust-src", "rustfmt"] profile = "minimal" From e971317bc52905e64c254aad9bce90db5ea786de Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Thu, 16 Jul 2026 14:31:54 -0300 Subject: [PATCH 12/20] chore: address comments --- CHANGELOG.md | 3 +- Cargo.lock | 45 ++-- Cargo.toml | 14 +- .../tests/agglayer/agglayer_bridge_in_out.rs | 4 +- bin/integration-tests/src/tests/batch.rs | 12 +- bin/integration-tests/src/tests/client.rs | 4 +- bin/integration-tests/src/tests/onchain.rs | 12 +- .../src/tests/pswap_transaction.rs | 29 ++- .../src/tests/swap_transaction.rs | 18 +- .../src/generators/large_account.rs | 231 +++++++++--------- bin/miden-cli/Cargo.toml | 2 +- bin/miden-cli/src/commands/account.rs | 6 +- .../src/commands/new_transactions.rs | 6 +- bin/miden-cli/src/utils.rs | 4 +- .../rust-client/src/account/account_reader.rs | 15 +- .../src/remote_prover/api_client.rs | 88 +++++++ .../src/remote_prover/generated.rs | 15 +- crates/rust-client/src/remote_prover/mod.rs | 1 + .../src/remote_prover/tx_prover.rs | 39 +-- .../src/rpc/domain/account_vault.rs | 59 ++--- .../rust-client/src/rpc/domain/storage_map.rs | 49 ++-- crates/rust-client/src/rpc/mod.rs | 1 + crates/rust-client/src/sync/mod.rs | 1 - crates/rust-client/src/sync/state_sync.rs | 26 +- .../rust-client/src/sync/state_sync_update.rs | 208 ++++++---------- crates/rust-client/src/test_utils/common.rs | 2 +- crates/rust-client/src/test_utils/mock.rs | 82 +++---- .../src/transaction/request/builder.rs | 16 +- crates/rust-client/src/utils.rs | 52 ++-- crates/sqlite-store/src/account/accounts.rs | 4 +- crates/sqlite-store/src/account/helpers.rs | 2 +- crates/sqlite-store/src/account/storage.rs | 7 +- crates/sqlite-store/src/sync.rs | 38 +-- .../testing/miden-client-tests/src/tests.rs | 132 +++++----- .../miden-client-tests/src/tests/batch.rs | 10 +- scripts/start-test-node.sh | 2 +- 36 files changed, 599 insertions(+), 640 deletions(-) create mode 100644 crates/rust-client/src/remote_prover/api_client.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 14ac0bd3ed..6c751ca98a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ ### Breaking Changes * [BREAKING][rust][cli] Removed the client debug-mode toggle: `DebugMode`, `ClientBuilder::in_debug_mode`, `Client::in_debug_mode`, the CLI `--debug` flag, and the `MIDEN_DEBUG` environment variable are gone, along with the `debug_mode` parameter of `CliClient::new`/`CliClient::from_config`. Miden VM 0.24 replaced the flag-gated `debug.*` MASM decorators with `miden::core::debug` procedures whose output the transaction executor prints by default, so the toggle no longer had anything to gate. ([#2290](https://github.com/0xMiden/rust-sdk/pull/2290)). -* [BREAKING][rust] Migrated to `miden-protocol` 0.16. Transaction-level fees were removed, block-level `FeeParameters` are unchanged. The relative `AccountDelta` model was replaced by the absolute `AccountPatch` model for account updates: `TransactionResult::account_delta` is now `account_patch`, `AccountUpdateDetails::Public` now carries an `AccountPatch`, account reconstruction is done via `Account::try_from(&AccountPatch)` or `Account::apply_patch` instead of previous `apply_delta`. Re-exports changed accordingly: `AccountStorageDelta` for `AccountStoragePatch`, `StorageMapDelta` for `StorageMapPatch`, and so on. Standards APIs were updated: `AuthMethod` was removed (use the concrete auth components), `create_fungible_faucet` became `create_user_fungible_faucet`. ([#2290](https://github.com/0xMiden/rust-sdk/pull/2290)). +* [BREAKING][rust] Migrated to `miden-protocol` 0.16. Transaction-level fees were removed, block-level `FeeParameters` are unchanged. The relative `AccountDelta` model was replaced by the absolute `AccountPatch` model for account updates: `TransactionResult::account_delta` is now `account_patch`, `AccountUpdateDetails::Public` now carries an `AccountPatch`, account reconstruction is done via `Account::try_from(&AccountPatch)` or `Account::apply_patch` instead of previous `apply_delta`. Re-exports changed accordingly: `AccountStorageDelta` for `AccountStoragePatch`, `StorageMapDelta` for `StorageMapPatch`, and so on. Standards APIs were updated: `AuthMethod` was removed (use the concrete auth components), `create_fungible_faucet` was replaced by auth-specific variants such as `create_singlesig_user_fungible_faucet`. ([#2290](https://github.com/0xMiden/rust-sdk/pull/2290)). +* [BREAKING][rust][cli] Fungible amounts in the public API now use `AssetAmount` instead of raw `u64`: `AccountReader::get_balance` returns `AssetAmount`, `TransactionRequestBuilder::build_pswap_consume` takes `AssetAmount` fill amounts, and `tokens_to_base_units`/`base_units_to_tokens` parse to and display from `AssetAmount` (amounts above `AssetAmount::MAX` are now rejected at parse time with `TokenParseError::InvalidAmount`) ([#2290](https://github.com/0xMiden/rust-sdk/pull/2290)). * [BREAKING][store] The SQLite store now stores account IDs as serialized `BLOB` columns instead of hex `TEXT` ([#2309](https://github.com/0xMiden/rust-sdk/pull/2309)). * [BREAKING][param][store] `Store::insert_block_header` now takes a `nodes` argument and persists the header with its MMR authentication nodes in a single transaction; the standalone `Store::insert_partial_blockchain_nodes` is removed. Header-only inserts (e.g. genesis) pass an empty slice ([#2294](https://github.com/0xMiden/rust-sdk/pull/2294)). * [BREAKING][behavior][store] The `ConsumedExternal` note-metadata layout added in [#2308](https://github.com/0xMiden/rust-sdk/pull/2308) is now the only supported serialized format. The backward-compatible decoding of the older metadata-less layout is removed, so existing stores are not compatible and must be recreated ([#2313](https://github.com/0xMiden/rust-sdk/pull/2313)). diff --git a/Cargo.lock b/Cargo.lock index cdba3a2225..39dc8f992e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1058,7 +1058,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1902,9 +1902,8 @@ dependencies = [ [[package]] name = "miden-agglayer" -version = "0.16.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3154439a4406647fd457ac657bfaf260abc406cf2aa3743356c0199dbe2280" +version = "0.16.0-alpha.3" +source = "git+https://github.com/0xMiden/protocol.git?tag=v0.16.0-alpha.3#47112cd2539b14e1d8a5955612dc833b4348b8f3" dependencies = [ "alloy-sol-types", "fs-err", @@ -1993,9 +1992,8 @@ dependencies = [ [[package]] name = "miden-block-prover" -version = "0.16.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616b8fa0652e25c5711bcd01f6f1f50e803a01db962e5af6b4a2bd7612dfde78" +version = "0.16.0-alpha.3" +source = "git+https://github.com/0xMiden/protocol.git?tag=v0.16.0-alpha.3#47112cd2539b14e1d8a5955612dc833b4348b8f3" dependencies = [ "miden-protocol", "thiserror", @@ -2506,9 +2504,8 @@ dependencies = [ [[package]] name = "miden-protocol" -version = "0.16.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5fd2fec04e0651a9ad7886541afc1bd6d8ee0de91a2850e9f4ead829c0fe3f" +version = "0.16.0-alpha.3" +source = "git+https://github.com/0xMiden/protocol.git?tag=v0.16.0-alpha.3#47112cd2539b14e1d8a5955612dc833b4348b8f3" dependencies = [ "bech32", "fs-err", @@ -2573,9 +2570,8 @@ dependencies = [ [[package]] name = "miden-standards" -version = "0.16.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f431c2bcd8b63ee45d12155565a23a9abc78240663d275e0e95eb475c9ac6438" +version = "0.16.0-alpha.3" +source = "git+https://github.com/0xMiden/protocol.git?tag=v0.16.0-alpha.3#47112cd2539b14e1d8a5955612dc833b4348b8f3" dependencies = [ "bon", "fs-err", @@ -2615,9 +2611,8 @@ dependencies = [ [[package]] name = "miden-testing" -version = "0.16.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "610ba28ab9d73da0fcde6aa2389b2029e096df0c7248fde558d7d44be67ec8f7" +version = "0.16.0-alpha.3" +source = "git+https://github.com/0xMiden/protocol.git?tag=v0.16.0-alpha.3#47112cd2539b14e1d8a5955612dc833b4348b8f3" dependencies = [ "anyhow", "itertools", @@ -2636,9 +2631,8 @@ dependencies = [ [[package]] name = "miden-tx" -version = "0.16.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eff35d682e8e4266bbe314e3b95f98ce5fea515bdc80573b907daaba937e8c1" +version = "0.16.0-alpha.3" +source = "git+https://github.com/0xMiden/protocol.git?tag=v0.16.0-alpha.3#47112cd2539b14e1d8a5955612dc833b4348b8f3" dependencies = [ "miden-processor", "miden-protocol", @@ -2649,9 +2643,8 @@ dependencies = [ [[package]] name = "miden-tx-batch" -version = "0.16.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bec9ae6f93ad985a86999e81cb90a7e11090a7ea25eed661f85e384bee2bb9" +version = "0.16.0-alpha.3" +source = "git+https://github.com/0xMiden/protocol.git?tag=v0.16.0-alpha.3#47112cd2539b14e1d8a5955612dc833b4348b8f3" dependencies = [ "miden-processor", "miden-protocol", @@ -3917,7 +3910,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4437,10 +4430,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5244,7 +5237,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3d18068be5..7028f3754a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,15 +40,15 @@ miden-client = { default-features = false, path = "crates/rust-clie miden-client-sqlite-store = { default-features = false, path = "crates/sqlite-store", version = "0.16.0-alpha.1" } # Miden protocol dependencies -miden-agglayer = { default-features = false, version = "=0.16.0-alpha.2" } -miden-protocol = { default-features = false, version = "=0.16.0-alpha.2" } -miden-standards = { default-features = false, version = "=0.16.0-alpha.2" } -miden-testing = { default-features = false, version = "=0.16.0-alpha.2" } -miden-tx = { default-features = false, version = "=0.16.0-alpha.2" } -miden-tx-batch = { default-features = false, version = "=0.16.0-alpha.2" } +miden-agglayer = { default-features = false, git = "https://github.com/0xMiden/protocol.git", tag = "v0.16.0-alpha.3" } # TODO: replace with crates.io once protocol has proper release +miden-protocol = { default-features = false, git = "https://github.com/0xMiden/protocol.git", tag = "v0.16.0-alpha.3" } # TODO: replace with crates.io once protocol has proper release +miden-standards = { default-features = false, git = "https://github.com/0xMiden/protocol.git", tag = "v0.16.0-alpha.3" } # TODO: replace with crates.io once protocol has proper release +miden-testing = { default-features = false, git = "https://github.com/0xMiden/protocol.git", tag = "v0.16.0-alpha.3" } # TODO: replace with crates.io once protocol has proper release +miden-tx = { default-features = false, git = "https://github.com/0xMiden/protocol.git", tag = "v0.16.0-alpha.3" } # TODO: replace with crates.io once protocol has proper release +miden-tx-batch = { default-features = false, git = "https://github.com/0xMiden/protocol.git", tag = "v0.16.0-alpha.3" } # TODO: replace with crates.io once protocol has proper release # Miden node dependencies -miden-node-proto-build = { default-features = false, git = "https://github.com/0xMiden/node.git", tag = "v0.16.0-alpha.1" } # TODO: replace with crates.io on node has proper release +miden-node-proto-build = { default-features = false, git = "https://github.com/0xMiden/node.git", tag = "v0.16.0-alpha.1" } # TODO: replace with crates.io once node has proper release miden-note-transport-proto-build = { default-features = false, version = "0.4.1" } # Miden debug dependency diff --git a/bin/integration-tests/src/tests/agglayer/agglayer_bridge_in_out.rs b/bin/integration-tests/src/tests/agglayer/agglayer_bridge_in_out.rs index b3480b8dff..6768d5b797 100644 --- a/bin/integration-tests/src/tests/agglayer/agglayer_bridge_in_out.rs +++ b/bin/integration-tests/src/tests/agglayer/agglayer_bridge_in_out.rs @@ -36,7 +36,7 @@ use miden_agglayer::{ }; use miden_client::Felt; use miden_client::account::AccountType; -use miden_client::asset::{Asset, FungibleAsset}; +use miden_client::asset::{Asset, AssetAmount, FungibleAsset}; use miden_client::auth::RPO_FALCON_SCHEME_ID; use miden_client::crypto::FeltRng; use miden_client::note::NoteAssets; @@ -275,7 +275,7 @@ pub async fn test_agglayer_bridge_in_out(client_config: ClientConfig) -> Result< .await?; println!("[bridge_in_out] Round {round}: destination balance: {}", dest_balance); assert!( - dest_balance > 0, + dest_balance > AssetAmount::ZERO, "destination should have positive balance after bridge-in round {round}" ); } diff --git a/bin/integration-tests/src/tests/batch.rs b/bin/integration-tests/src/tests/batch.rs index 6223ca600a..4f726a0b7d 100644 --- a/bin/integration-tests/src/tests/batch.rs +++ b/bin/integration-tests/src/tests/batch.rs @@ -1,7 +1,7 @@ use anyhow::{Context, Result}; use miden_client::Felt; use miden_client::account::AccountType; -use miden_client::asset::{Asset, FungibleAsset}; +use miden_client::asset::{Asset, AssetAmount, FungibleAsset}; use miden_client::auth::RPO_FALCON_SCHEME_ID; use miden_client::note::NoteType; use miden_client::store::TransactionFilter; @@ -146,7 +146,7 @@ pub async fn test_batch_builder_submits_two_p2id_on_one_account( assert_eq!( sender_balance, - MINT_AMOUNT - (TRANSFER_AMOUNT * 2), + AssetAmount::new(MINT_AMOUNT - (TRANSFER_AMOUNT * 2)).unwrap(), "sender balance should have decreased by exactly 2 * TRANSFER_AMOUNT — this proves \ BatchBuilder stacked account state correctly between pushes" ); @@ -287,12 +287,12 @@ pub async fn test_batch_builder_multiple_accounts(client_config: ClientConfig) - assert_eq!( a_balance, - MINT_AMOUNT - TRANSFER_AMOUNT, + AssetAmount::new(MINT_AMOUNT - TRANSFER_AMOUNT).unwrap(), "A's balance should be MINT_AMOUNT - TRANSFER_AMOUNT after sending" ); assert_eq!( b_balance, - MINT_AMOUNT + TRANSFER_AMOUNT, + AssetAmount::new(MINT_AMOUNT + TRANSFER_AMOUNT).unwrap(), "B's balance should be MINT_AMOUNT + TRANSFER_AMOUNT after consuming the in-batch note" ); @@ -432,12 +432,12 @@ pub async fn test_batch_builder_interleaved_pushes(client_config: ClientConfig) assert_eq!( a_balance, - MINT_AMOUNT - (TRANSFER_AMOUNT * 2), + AssetAmount::new(MINT_AMOUNT - (TRANSFER_AMOUNT * 2)).unwrap(), "A's balance should reflect two outbound P2ID notes" ); assert_eq!( b_balance, - MINT_AMOUNT - TRANSFER_AMOUNT, + AssetAmount::new(MINT_AMOUNT - TRANSFER_AMOUNT).unwrap(), "B's balance should reflect one outbound P2ID note" ); diff --git a/bin/integration-tests/src/tests/client.rs b/bin/integration-tests/src/tests/client.rs index fc7b84d55b..2c5afe492f 100644 --- a/bin/integration-tests/src/tests/client.rs +++ b/bin/integration-tests/src/tests/client.rs @@ -17,7 +17,7 @@ use miden_client::account::{ StorageSlotName, }; use miden_client::assembly::CodeBuilder; -use miden_client::asset::{Asset, FungibleAsset}; +use miden_client::asset::{Asset, AssetAmount, FungibleAsset}; use miden_client::auth::{AuthSchemeId, AuthSecretKey, AuthSingleSig, RPO_FALCON_SCHEME_ID}; use miden_client::builder::ClientBuilder; use miden_client::keystore::FilesystemKeyStore; @@ -195,7 +195,7 @@ pub async fn test_multiple_tx_on_same_block(client_config: ClientConfig) -> Resu .get_balance(faucet_account_id) .await .context("failed to find sender account after transactions")?; - assert_eq!(sender_balance, MINT_AMOUNT - (TRANSFER_AMOUNT * 2)); + assert_eq!(sender_balance, AssetAmount::new(MINT_AMOUNT - (TRANSFER_AMOUNT * 2)).unwrap()); Ok(()) } diff --git a/bin/integration-tests/src/tests/onchain.rs b/bin/integration-tests/src/tests/onchain.rs index 80995e8a39..5ef12966c9 100644 --- a/bin/integration-tests/src/tests/onchain.rs +++ b/bin/integration-tests/src/tests/onchain.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use anyhow::{Context, Result}; use miden_client::account::{AccountType, build_wallet_id}; -use miden_client::asset::{Asset, FungibleAsset}; +use miden_client::asset::{Asset, AssetAmount, FungibleAsset}; use miden_client::auth::RPO_FALCON_SCHEME_ID; use miden_client::keystore::Keystore; use miden_client::note::standards::NoteSyncHint; @@ -339,8 +339,14 @@ pub async fn test_onchain_accounts(client_config: ClientConfig) -> Result<()> { .await .context("failed to find to account after transfer")?; - assert_eq!(new_from_account_balance, from_account_balance - TRANSFER_AMOUNT); - assert_eq!(new_to_account_balance, to_account_balance + TRANSFER_AMOUNT); + assert_eq!( + new_from_account_balance, + (from_account_balance - AssetAmount::new(TRANSFER_AMOUNT).unwrap()).unwrap() + ); + assert_eq!( + new_to_account_balance, + (to_account_balance + AssetAmount::new(TRANSFER_AMOUNT).unwrap()).unwrap() + ); Ok(()) } diff --git a/bin/integration-tests/src/tests/pswap_transaction.rs b/bin/integration-tests/src/tests/pswap_transaction.rs index 60674c71d4..5b124f0a11 100644 --- a/bin/integration-tests/src/tests/pswap_transaction.rs +++ b/bin/integration-tests/src/tests/pswap_transaction.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Result}; use miden_client::account::AccountType; -use miden_client::asset::FungibleAsset; +use miden_client::asset::{AssetAmount, FungibleAsset}; use miden_client::auth::RPO_FALCON_SCHEME_ID; use miden_client::note::{Note, NoteType, PswapNote}; use miden_client::testing::common::*; @@ -105,8 +105,8 @@ pub async fn test_pswap_full_fill_onchain(client_config: ClientConfig) -> Result let consume_request = TransactionRequestBuilder::new().build_pswap_consume( &pswap_note, bob_account.id(), - REQUESTED_AMOUNT, - 0, + AssetAmount::new(REQUESTED_AMOUNT).unwrap(), + AssetAmount::ZERO, )?; // The consumer tracks neither output note: the payback settles to the creator and the // remainder is the order's next tip. Both are validated as the transaction's own outputs, but @@ -135,18 +135,21 @@ pub async fn test_pswap_full_fill_onchain(client_config: ClientConfig) -> Result let alice_account_reader = alice_client.account_reader(alice_account.id()); assert_eq!( alice_account_reader.get_balance(btc_faucet_account.id()).await?, - MINT_AMOUNT - OFFERED_AMOUNT + AssetAmount::new(MINT_AMOUNT - OFFERED_AMOUNT).unwrap() ); assert_eq!( alice_account_reader.get_balance(eth_faucet_account.id()).await?, - REQUESTED_AMOUNT + AssetAmount::new(REQUESTED_AMOUNT).unwrap() ); let bob_account_reader = bob_client.account_reader(bob_account.id()); - assert_eq!(bob_account_reader.get_balance(btc_faucet_account.id()).await?, OFFERED_AMOUNT); + assert_eq!( + bob_account_reader.get_balance(btc_faucet_account.id()).await?, + AssetAmount::new(OFFERED_AMOUNT).unwrap() + ); assert_eq!( bob_account_reader.get_balance(eth_faucet_account.id()).await?, - MINT_AMOUNT - REQUESTED_AMOUNT + AssetAmount::new(MINT_AMOUNT - REQUESTED_AMOUNT).unwrap() ); Ok(()) @@ -244,8 +247,8 @@ pub async fn test_pswap_partial_fill_onchain(client_config: ClientConfig) -> Res let consume_request = TransactionRequestBuilder::new().build_pswap_consume( &pswap_note, bob_account.id(), - ACCOUNT_FILL, - 0, + AssetAmount::new(ACCOUNT_FILL).unwrap(), + AssetAmount::ZERO, )?; // The consumer tracks neither output note. The payback settles to the creator and the @@ -265,12 +268,12 @@ pub async fn test_pswap_partial_fill_onchain(client_config: ClientConfig) -> Res let bob_account_reader = bob_client.account_reader(bob_account.id()); assert_eq!( bob_account_reader.get_balance(btc_faucet_account.id()).await?, - EXPECTED_PAYOUT, + AssetAmount::new(EXPECTED_PAYOUT).unwrap(), "Bob should have received a proportional share, not the full offered amount" ); assert_eq!( bob_account_reader.get_balance(eth_faucet_account.id()).await?, - MINT_AMOUNT - ACCOUNT_FILL, + AssetAmount::new(MINT_AMOUNT - ACCOUNT_FILL).unwrap(), "Bob should have spent only the partial fill amount" ); @@ -351,7 +354,7 @@ pub async fn test_pswap_cancel_onchain(client_config: ClientConfig) -> Result<() let alice_account_reader = alice_client.account_reader(alice_account.id()); assert_eq!( alice_account_reader.get_balance(btc_faucet_account.id()).await?, - MINT_AMOUNT - OFFERED_AMOUNT, + AssetAmount::new(MINT_AMOUNT - OFFERED_AMOUNT).unwrap(), "creating the PSWAP should debit the offered asset" ); @@ -363,7 +366,7 @@ pub async fn test_pswap_cancel_onchain(client_config: ClientConfig) -> Result<() let alice_account_reader = alice_client.account_reader(alice_account.id()); assert_eq!( alice_account_reader.get_balance(btc_faucet_account.id()).await?, - MINT_AMOUNT, + AssetAmount::new(MINT_AMOUNT).unwrap(), "cancelling should restore the offered asset to the creator" ); diff --git a/bin/integration-tests/src/tests/swap_transaction.rs b/bin/integration-tests/src/tests/swap_transaction.rs index ddcc4af4ba..6d75c759fb 100644 --- a/bin/integration-tests/src/tests/swap_transaction.rs +++ b/bin/integration-tests/src/tests/swap_transaction.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Result}; use miden_client::account::AccountType; -use miden_client::asset::{Asset, FungibleAsset}; +use miden_client::asset::{Asset, AssetAmount, FungibleAsset}; use miden_client::auth::RPO_FALCON_SCHEME_ID; use miden_client::note::standards::NoteSyncHint; use miden_client::note::{Note, NoteDetails, NoteFile, NoteType, SwapNote}; @@ -153,15 +153,15 @@ pub async fn test_swap_fully_onchain(client_config: ClientConfig) -> Result<()> let account_a_btc = account_a_reader.get_balance(btc_faucet_account.id()).await?; let account_a_eth = account_a_reader.get_balance(eth_faucet_account.id()).await?; - assert_eq!(account_a_btc, 999); - assert_eq!(account_a_eth, 25); + assert_eq!(account_a_btc, AssetAmount::new(999).unwrap()); + assert_eq!(account_a_eth, AssetAmount::new(25).unwrap()); let account_b_reader = client2.account_reader(account_b.id()); let account_b_btc = account_b_reader.get_balance(btc_faucet_account.id()).await?; let account_b_eth = account_b_reader.get_balance(eth_faucet_account.id()).await?; - assert_eq!(account_b_btc, 1); - assert_eq!(account_b_eth, 975); + assert_eq!(account_b_btc, AssetAmount::new(1).unwrap()); + assert_eq!(account_b_eth, AssetAmount::new(975).unwrap()); Ok(()) } @@ -310,15 +310,15 @@ pub async fn test_swap_private(client_config: ClientConfig) -> Result<()> { let account_a_btc = account_a_reader.get_balance(btc_faucet_account.id()).await?; let account_a_eth = account_a_reader.get_balance(eth_faucet_account.id()).await?; - assert_eq!(account_a_btc, 999); - assert_eq!(account_a_eth, 25); + assert_eq!(account_a_btc, AssetAmount::new(999).unwrap()); + assert_eq!(account_a_eth, AssetAmount::new(25).unwrap()); let account_b_reader = client2.account_reader(account_b.id()); let account_b_btc = account_b_reader.get_balance(btc_faucet_account.id()).await?; let account_b_eth = account_b_reader.get_balance(eth_faucet_account.id()).await?; - assert_eq!(account_b_btc, 1); - assert_eq!(account_b_eth, 975); + assert_eq!(account_b_btc, AssetAmount::new(1).unwrap()); + assert_eq!(account_b_eth, AssetAmount::new(975).unwrap()); Ok(()) } diff --git a/bin/miden-bench/src/generators/large_account.rs b/bin/miden-bench/src/generators/large_account.rs index 15bdfde181..9cfcea3ce1 100644 --- a/bin/miden-bench/src/generators/large_account.rs +++ b/bin/miden-bench/src/generators/large_account.rs @@ -19,46 +19,6 @@ pub struct SlotDescriptor { pub is_map: bool, } -#[cfg(test)] -use miden_client::account::component::AccountComponent; -#[cfg(test)] -use miden_client::account::{ - Account, - AccountBuilder, - AccountBuilderSchemaCommitmentExt, - AccountType, - StorageMap, - StorageMapKey, - StorageSlot, - StorageSlotName, -}; -#[cfg(test)] -use miden_client::assembly::CodeBuilder; -#[cfg(test)] -use miden_client::auth::Approver; -#[cfg(test)] -use miden_client::auth::{AuthSchemeId, AuthSecretKey, AuthSingleSig}; - -/// Configuration for generating large accounts (used in tests) -#[cfg(test)] -#[derive(Clone, Debug)] -struct LargeAccountConfig { - num_storage_map_entries: usize, - num_map_slots: usize, - seed: [u8; 32], -} - -#[cfg(test)] -impl LargeAccountConfig { - fn with_seed(maps: usize, entries_per_map: usize, seed: [u8; 32]) -> Self { - Self { - num_storage_map_entries: entries_per_map, - num_map_slots: maps, - seed, - } - } -} - /// Generates MASM code for a storage reader component with procedures for each slot. /// /// For map slots: generates `get_map_item_slot_N` (reads a key-value entry). @@ -109,64 +69,6 @@ end code } -/// Creates a large account with the specified configuration (used in tests) -#[cfg(test)] -fn create_large_account(config: &LargeAccountConfig) -> anyhow::Result<(Account, AuthSecretKey)> { - use miden_client::account::component::{AccountComponentMetadata, BasicWallet}; - - let sk = - AuthSecretKey::new_falcon512_poseidon2_with_rng(&mut ChaCha20Rng::from_seed(config.seed)); - - // Create storage map slots - let mut storage_slots = Vec::new(); - for i in 0..config.num_map_slots { - let slot_name = format!("miden::bench::map_slot_{i}"); - storage_slots.push(create_large_storage_slot( - slot_name.as_str(), - config.num_storage_map_entries, - i as u32, - )); - } - - // Reader component: owns storage slots and provides get_map_item_slot_N procedures - let descriptors: Vec = (0..config.num_map_slots) - .map(|i| SlotDescriptor { - name: format!("miden::bench::map_slot_{i}"), - is_map: true, - }) - .collect(); - let reader_code = generate_reader_component_code(&descriptors); - let reader_component_code = CodeBuilder::default() - .compile_component_code("miden::bench::storage_reader", &reader_code) - .map_err(|e| anyhow::anyhow!("Failed to compile reader component: {e}"))?; - let reader_component = AccountComponent::new( - reader_component_code, - storage_slots, - AccountComponentMetadata::new("miden::testing::storage_reader"), - ) - .map_err(|e| anyhow::anyhow!("Failed to create reader component: {e}"))?; - - // Wallet component: provides standard wallet operations (no storage slots) - let wallet_component = AccountComponent::new( - BasicWallet::code().as_library().clone(), - vec![], - BasicWallet::component_metadata(), - ) - .expect("basic wallet component should satisfy account component requirements"); - - let account = AccountBuilder::new(config.seed) - .with_auth_component(AuthSingleSig::new(Approver::new( - sk.public_key().to_commitment(), - AuthSchemeId::Falcon512Poseidon2, - ))) - .account_type(AccountType::Public) - .with_component(wallet_component) - .with_component(reader_component) - .build_with_schema_commitment()?; - - Ok((account, sk)) -} - /// Generates a random non-zero `[Felt; 4]` value suitable for storage map entries. /// /// Values must be non-zero because the SMT treats zero values as deletions. @@ -189,30 +91,123 @@ pub fn slot_rng(seed: u32) -> ChaCha20Rng { ChaCha20Rng::from_seed(rng_seed) } -/// Creates a storage slot with many map entries (used in tests) -#[cfg(test)] -fn create_large_storage_slot(name: &str, num_entries: usize, seed: u32) -> StorageSlot { - let mut rng = slot_rng(seed); - - let map_entries: Vec<_> = (0..num_entries as u32) - .map(|i| { - let key_val = seed.wrapping_mul(1000).wrapping_add(i); - let key = [Felt::new_unchecked(key_val as u64); 4]; - let value = random_word(&mut rng); - (StorageMapKey::new(key.into()), value.into()) - }) - .collect(); - - StorageSlot::with_map( - StorageSlotName::new(name).expect("slot name should be valid"), - StorageMap::with_entries(map_entries).expect("map entries should be valid"), - ) -} - #[cfg(test)] mod tests { + use miden_client::account::component::{ + AccountComponent, + AccountComponentMetadata, + BasicWallet, + }; + use miden_client::account::{ + Account, + AccountBuilder, + AccountBuilderSchemaCommitmentExt, + AccountType, + StorageMap, + StorageMapKey, + StorageSlot, + StorageSlotName, + }; + use miden_client::assembly::CodeBuilder; + use miden_client::auth::{Approver, AuthSchemeId, AuthSecretKey, AuthSingleSig}; + use super::*; + /// Configuration for generating large accounts. + #[derive(Clone, Debug)] + struct LargeAccountConfig { + num_storage_map_entries: usize, + num_map_slots: usize, + seed: [u8; 32], + } + + impl LargeAccountConfig { + fn with_seed(maps: usize, entries_per_map: usize, seed: [u8; 32]) -> Self { + Self { + num_storage_map_entries: entries_per_map, + num_map_slots: maps, + seed, + } + } + } + + /// Creates a large account with the specified configuration. + fn create_large_account( + config: &LargeAccountConfig, + ) -> anyhow::Result<(Account, AuthSecretKey)> { + let sk = AuthSecretKey::new_falcon512_poseidon2_with_rng(&mut ChaCha20Rng::from_seed( + config.seed, + )); + + // Create storage map slots + let mut storage_slots = Vec::new(); + for i in 0..config.num_map_slots { + let slot_name = format!("miden::bench::map_slot_{i}"); + storage_slots.push(create_large_storage_slot( + slot_name.as_str(), + config.num_storage_map_entries, + i as u32, + )); + } + + // Reader component: owns storage slots and provides get_map_item_slot_N procedures + let descriptors: Vec = (0..config.num_map_slots) + .map(|i| SlotDescriptor { + name: format!("miden::bench::map_slot_{i}"), + is_map: true, + }) + .collect(); + let reader_code = generate_reader_component_code(&descriptors); + let reader_component_code = CodeBuilder::default() + .compile_component_code("miden::bench::storage_reader", &reader_code) + .map_err(|e| anyhow::anyhow!("Failed to compile reader component: {e}"))?; + let reader_component = AccountComponent::new( + reader_component_code, + storage_slots, + AccountComponentMetadata::new("miden::testing::storage_reader"), + ) + .map_err(|e| anyhow::anyhow!("Failed to create reader component: {e}"))?; + + // Wallet component: provides standard wallet operations (no storage slots) + let wallet_component = AccountComponent::new( + BasicWallet::code().as_library().clone(), + vec![], + BasicWallet::component_metadata(), + ) + .expect("basic wallet component should satisfy account component requirements"); + + let account = AccountBuilder::new(config.seed) + .with_auth_component(AuthSingleSig::new(Approver::new( + sk.public_key().to_commitment(), + AuthSchemeId::Falcon512Poseidon2, + ))) + .account_type(AccountType::Public) + .with_component(wallet_component) + .with_component(reader_component) + .build_with_schema_commitment()?; + + Ok((account, sk)) + } + + /// Creates a storage slot with many map entries. + fn create_large_storage_slot(name: &str, num_entries: usize, seed: u32) -> StorageSlot { + let mut rng = slot_rng(seed); + + let map_entries: Vec<_> = (0..num_entries as u32) + .map(|i| { + let key_val = seed.wrapping_mul(1000).wrapping_add(i); + let key = [Felt::new_unchecked(key_val as u64); 4]; + let value = random_word(&mut rng); + (StorageMapKey::new(key.into()), value.into()) + }) + .collect(); + + StorageSlot::with_map( + StorageSlotName::new(name).expect("slot name should be valid"), + StorageMap::with_entries(map_entries).expect("map entries should be valid"), + ) + } + #[test] fn test_create_small_account() { let config = LargeAccountConfig::with_seed(1, 10, [0x01; 32]); diff --git a/bin/miden-cli/Cargo.toml b/bin/miden-cli/Cargo.toml index 8a3465523c..18b3134223 100644 --- a/bin/miden-cli/Cargo.toml +++ b/bin/miden-cli/Cargo.toml @@ -48,7 +48,7 @@ miden-client = { features = ["std"], workspace = true } anyhow = { workspace = true } assert_cmd = { workspace = true } miden-client = { features = ["testing"], workspace = true } -midenc-hir-type = "0.9" +midenc-hir-type = { version = "0.9" } predicates = { workspace = true } regex = { workspace = true } serial_test = { features = ["file_locks"], version = "3.0" } diff --git a/bin/miden-cli/src/commands/account.rs b/bin/miden-cli/src/commands/account.rs index f8d877daaf..f006fb77f8 100644 --- a/bin/miden-cli/src/commands/account.rs +++ b/bin/miden-cli/src/commands/account.rs @@ -172,13 +172,13 @@ async fn show_account( let (asset_type, faucet, amount) = match asset { Asset::Fungible(fungible_asset) => { let faucet_id = fungible_asset.faucet_id(); - let amount_u64 = fungible_asset.amount().as_u64(); + let asset_amount = fungible_asset.amount(); let (faucet, amount) = match get_faucet_component(client, faucet_id).await { Ok(faucet_component) => ( faucet_component.symbol().to_string(), - base_units_to_tokens(amount_u64, faucet_component.decimals()), + base_units_to_tokens(asset_amount, faucet_component.decimals()), ), - Err(_) => (faucet_id.prefix().to_hex(), amount_u64.to_string()), + Err(_) => (faucet_id.prefix().to_hex(), asset_amount.as_u64().to_string()), }; ("Fungible Asset", faucet, amount) }, diff --git a/bin/miden-cli/src/commands/new_transactions.rs b/bin/miden-cli/src/commands/new_transactions.rs index 16907c06fd..3cd795a359 100644 --- a/bin/miden-cli/src/commands/new_transactions.rs +++ b/bin/miden-cli/src/commands/new_transactions.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use clap::{Parser, Subcommand, ValueEnum}; use miden_client::account::AccountId; +use miden_client::asset::AssetAmount; use miden_client::keystore::Keystore; use miden_client::note::{ BlockNumber, @@ -494,10 +495,13 @@ impl PswapConsumeCmd { let consumer_id = parse_account_id(&client, &self.account).await?; let note = resolve_input_note(&client, &self.note).await?; + let fill_amount = AssetAmount::new(self.fill_amount) + .map_err(|err| CliError::Parse(err.into(), "Invalid fill amount".to_string()))?; + // The CLI does not yet support note-supplied fills (in-flight fills routed through // other notes), so pass 0 for `note_fill_amount`. let tx_request = TransactionRequestBuilder::new() - .build_pswap_consume(¬e, consumer_id, self.fill_amount, 0) + .build_pswap_consume(¬e, consumer_id, fill_amount, AssetAmount::ZERO) .map_err(|err| { CliError::Transaction( err.into(), diff --git a/bin/miden-cli/src/utils.rs b/bin/miden-cli/src/utils.rs index 4e6cc63adb..4ec24b2b81 100644 --- a/bin/miden-cli/src/utils.rs +++ b/bin/miden-cli/src/utils.rs @@ -383,7 +383,7 @@ impl FaucetMetadataResolver { asset: &FungibleAsset, ) -> Result<(String, String), CliError> { if let Some(meta) = self.resolve(client, asset.faucet_id()).await? { - return Ok((meta.symbol, base_units_to_tokens(asset.amount().as_u64(), meta.decimals))); + return Ok((meta.symbol, base_units_to_tokens(asset.amount(), meta.decimals))); } let network_id = client.network_id().await?; let address_str = Address::new(asset.faucet_id()).encode(network_id); @@ -432,7 +432,7 @@ impl FaucetMetadataResolver { let amount = tokens_to_base_units(amount, entry.decimals).map_err(|err| { CliError::Parse(err.into(), "Failed to parse tokens to base units".to_string()) })?; - (entry.account_id, amount) + (entry.account_id, amount.as_u64()) }; FungibleAsset::new(faucet_id, amount).map_err(CliError::Asset) diff --git a/crates/rust-client/src/account/account_reader.rs b/crates/rust-client/src/account/account_reader.rs index 9bec0c67a3..f5e44a540f 100644 --- a/crates/rust-client/src/account/account_reader.rs +++ b/crates/rust-client/src/account/account_reader.rs @@ -11,7 +11,7 @@ use miden_protocol::account::{ StorageSlotName, }; use miden_protocol::address::Address; -use miden_protocol::asset::{Asset, AssetId}; +use miden_protocol::asset::{Asset, AssetAmount, AssetId}; use miden_protocol::{Felt, Word}; use crate::errors::ClientError; @@ -113,21 +113,20 @@ impl AccountReader { /// Retrieves the balance of a fungible asset in the account's vault. /// - /// Returns `0` if the asset is not present in the vault or if the asset is not a fungible - /// asset. + /// Returns [`AssetAmount::ZERO`] if the asset is not present in the vault or if the asset is + /// not a fungible asset. /// /// To load the entire vault, use /// [`Client::get_account_vault`](crate::Client::get_account_vault). - pub async fn get_balance(&self, faucet_id: AccountId) -> Result { - let mut total = 0u64; + pub async fn get_balance(&self, faucet_id: AccountId) -> Result { let vault_key = AssetId::new_fungible(faucet_id); if let Some((Asset::Fungible(fungible_asset), _)) = self.store.get_account_asset(self.account_id, vault_key).await? { - total = total.saturating_add(u64::from(fungible_asset.amount())); + Ok(fungible_asset.amount()) + } else { + Ok(AssetAmount::ZERO) } - - Ok(total) } // STORAGE ACCESS diff --git a/crates/rust-client/src/remote_prover/api_client.rs b/crates/rust-client/src/remote_prover/api_client.rs new file mode 100644 index 0000000000..91df97e7b4 --- /dev/null +++ b/crates/rust-client/src/remote_prover/api_client.rs @@ -0,0 +1,88 @@ +use core::ops::{Deref, DerefMut}; + +pub(crate) use api_client_wrapper::{ApiClient, InnerClient}; + +// WEB CLIENT +// ================================================================================================ + +#[cfg(target_arch = "wasm32")] +pub(crate) mod api_client_wrapper { + use alloc::string::String; + use core::time::Duration; + + use crate::remote_prover::RemoteProverClientError; + use crate::remote_prover::generated::api_client::ApiClient as ProtoClient; + + pub type InnerClient = ProtoClient; + + /// Wrapper around the generated prover API client that hides the transport type. + #[derive(Clone)] + pub struct ApiClient(pub(crate) InnerClient); + + impl ApiClient { + /// Connects to the remote prover at the provided endpoint. + // Kept async for API parity with the native client; in WASM this is synchronous. + #[allow(clippy::unused_async)] + pub async fn new_client( + endpoint: String, + timeout: Duration, + ) -> Result { + let fetch_options = + tonic_web_wasm_client::options::FetchOptions::new().timeout(timeout); + let web_client = + tonic_web_wasm_client::Client::new_with_options(endpoint, fetch_options); + Ok(ApiClient(ProtoClient::new(web_client))) + } + } +} + +// NATIVE CLIENT +// ================================================================================================ + +#[cfg(not(target_arch = "wasm32"))] +pub(crate) mod api_client_wrapper { + use alloc::string::String; + use core::time::Duration; + + use crate::remote_prover::RemoteProverClientError; + use crate::remote_prover::generated::api_client::ApiClient as ProtoClient; + + pub type InnerClient = ProtoClient; + + /// Wrapper around the generated prover API client that hides the transport type. + #[derive(Clone)] + pub struct ApiClient(pub(crate) InnerClient); + + impl ApiClient { + /// Connects to the remote prover at the provided endpoint. + pub async fn new_client( + endpoint: String, + timeout: Duration, + ) -> Result { + let endpoint = tonic::transport::Endpoint::try_from(endpoint) + .map_err(|err| RemoteProverClientError::ConnectionFailed(err.into()))? + .timeout(timeout); + let channel = endpoint + .tls_config(tonic::transport::ClientTlsConfig::new().with_native_roots()) + .map_err(|err| RemoteProverClientError::ConnectionFailed(err.into()))? + .connect() + .await + .map_err(|err| RemoteProverClientError::ConnectionFailed(err.into()))?; + Ok(ApiClient(ProtoClient::new(channel))) + } + } +} + +impl Deref for ApiClient { + type Target = InnerClient; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ApiClient { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/crates/rust-client/src/remote_prover/generated.rs b/crates/rust-client/src/remote_prover/generated.rs index b716cb3a6f..90ec6b519f 100644 --- a/crates/rust-client/src/remote_prover/generated.rs +++ b/crates/rust-client/src/remote_prover/generated.rs @@ -1,17 +1,10 @@ -#[cfg(feature = "std")] #[rustfmt::skip] #[allow(dead_code)] -mod std_gen { +mod inner { + #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/remote_prover_std.rs")); -} -#[cfg(feature = "std")] -pub use std_gen::remote_prover::*; -#[cfg(not(feature = "std"))] -#[rustfmt::skip] -#[allow(dead_code)] -mod nostd_gen { + #[cfg(not(feature = "std"))] include!(concat!(env!("OUT_DIR"), "/remote_prover_nostd.rs")); } -#[cfg(not(feature = "std"))] -pub use nostd_gen::remote_prover::*; +pub use inner::remote_prover::*; diff --git a/crates/rust-client/src/remote_prover/mod.rs b/crates/rust-client/src/remote_prover/mod.rs index 44cbcb14e9..fea0e8e22c 100644 --- a/crates/rust-client/src/remote_prover/mod.rs +++ b/crates/rust-client/src/remote_prover/mod.rs @@ -4,6 +4,7 @@ use core::error::Error as CoreError; use thiserror::Error; +mod api_client; mod generated; mod tx_prover; diff --git a/crates/rust-client/src/remote_prover/tx_prover.rs b/crates/rust-client/src/remote_prover/tx_prover.rs index 3b7fa32561..8f7cdabd86 100644 --- a/crates/rust-client/src/remote_prover/tx_prover.rs +++ b/crates/rust-client/src/remote_prover/tx_prover.rs @@ -8,7 +8,7 @@ use miden_protocol::vm::FutureMaybeSend; use miden_tx::TransactionProverError; use tokio::sync::Mutex; -use super::generated::api_client::ApiClient; +use super::api_client::ApiClient; use super::{RemoteProverClientError, generated as proto}; // REMOTE TRANSACTION PROVER @@ -23,13 +23,13 @@ use super::{RemoteProverClientError, generated as proto}; /// The transport layer connection is established lazily when the first transaction is proven. #[derive(Clone)] pub struct RemoteTransactionProver { - #[cfg(target_arch = "wasm32")] - client: Arc>>>, - - #[cfg(not(target_arch = "wasm32"))] - client: Arc>>>, + /// Lazily initialized gRPC client, populated on the first proving request. + client: Arc>>, + /// Endpoint of the remote prover in the format `{protocol}://{hostname}:{port}`. endpoint: String, + + /// Timeout applied to each request sent to the remote prover. timeout: Duration, } @@ -60,32 +60,7 @@ impl RemoteTransactionProver { return Ok(()); } - #[cfg(target_arch = "wasm32")] - let new_client = { - let fetch_options = - tonic_web_wasm_client::options::FetchOptions::new().timeout(self.timeout); - let web_client = tonic_web_wasm_client::Client::new_with_options( - self.endpoint.clone(), - fetch_options, - ); - ApiClient::new(web_client) - }; - - #[cfg(not(target_arch = "wasm32"))] - let new_client = { - let endpoint = tonic::transport::Endpoint::try_from(self.endpoint.clone()) - .map_err(|err| RemoteProverClientError::ConnectionFailed(err.into()))? - .timeout(self.timeout); - let channel = endpoint - .tls_config(tonic::transport::ClientTlsConfig::new().with_native_roots()) - .map_err(|err| RemoteProverClientError::ConnectionFailed(err.into()))? - .connect() - .await - .map_err(|err| RemoteProverClientError::ConnectionFailed(err.into()))?; - ApiClient::new(channel) - }; - - *client = Some(new_client); + *client = Some(ApiClient::new_client(self.endpoint.clone(), self.timeout).await?); Ok(()) } diff --git a/crates/rust-client/src/rpc/domain/account_vault.rs b/crates/rust-client/src/rpc/domain/account_vault.rs index 20f5d45a3f..98d1c545d0 100644 --- a/crates/rust-client/src/rpc/domain/account_vault.rs +++ b/crates/rust-client/src/rpc/domain/account_vault.rs @@ -9,14 +9,6 @@ use miden_protocol::block::BlockNumber; use crate::rpc::domain::MissingFieldHelper; use crate::rpc::{RpcConversionError, RpcError, generated as proto}; -/// A single vault update as reported by the node: the block it occurred in, the affected vault key, -/// and its new asset (`None` if the asset was removed at that block). -pub(crate) struct VaultUpdate { - pub(crate) block_num: BlockNumber, - pub(crate) vault_key: AssetId, - pub(crate) asset: Option, -} - // ASSET CONVERSION // ================================================================================================ @@ -68,16 +60,28 @@ impl TryFrom for AccountVaultInfo { pagination_info )))?; - let updates = value + let mut updates = value .updates .into_iter() .map(vault_update_from_proto) .collect::, _>>()?; + // The node may report the same asset ID in more than one block, folding the updates in + // ascending block order lets the latest block win, with an absent asset (`None`) encoding + // a removal. + updates.sort_by_key(|(block_num, ..)| *block_num); + let mut vault_patch = AccountVaultPatch::default(); + for (_, asset_id, asset) in updates { + match asset { + Some(asset) => vault_patch.insert_asset(asset), + None => vault_patch.remove_asset(asset_id), + } + } + Ok(Self { chain_tip: pagination_info.chain_tip.into(), block_number: pagination_info.block_num.into(), - vault_patch: merge_vault_updates(updates), + vault_patch, }) } } @@ -85,44 +89,29 @@ impl TryFrom for AccountVaultInfo { // ACCOUNT VAULT UPDATE // ================================================================================================ -/// Converts a single proto vault update into a [`VaultUpdate`], validating that a present asset's -/// vault key matches the reported key. -fn vault_update_from_proto(value: proto::rpc::AccountVaultUpdate) -> Result { +/// Converts a single proto vault update into its block number, asset ID, and new asset (`None` if +/// the asset was removed at that block), validating that a present asset matches the reported key. +fn vault_update_from_proto( + value: proto::rpc::AccountVaultUpdate, +) -> Result<(BlockNumber, AssetId, Option), RpcError> { let block_num = BlockNumber::from(value.block_num); - let vault_key_inner: Word = value + let vault_key: Word = value .vault_key .ok_or(proto::rpc::SyncAccountVaultResponse::missing_field(stringify!(vault_key)))? .try_into()?; - let vault_key = - AssetId::try_from(vault_key_inner).map_err(|e| RpcError::InvalidResponse(e.to_string()))?; + let asset_id = + AssetId::try_from(vault_key).map_err(|e| RpcError::InvalidResponse(e.to_string()))?; let asset = value.asset.map(Asset::try_from).transpose()?; if let Some(ref asset) = asset - && asset.id() != vault_key + && asset.id() != asset_id { return Err(RpcError::InvalidResponse( "account vault update returned mismatched asset key".to_string(), )); } - Ok(VaultUpdate { block_num, vault_key, asset }) -} - -/// Merges per-block vault updates into an absolute [`AccountVaultPatch`]. -/// -/// The node may report the same vault key in more than one block; applying the updates in ascending -/// block order lets the latest block win, with an absent asset (`None`) encoding a removal. -pub(crate) fn merge_vault_updates(mut updates: Vec) -> AccountVaultPatch { - updates.sort_by_key(|update| update.block_num); - - let mut patch = AccountVaultPatch::default(); - for VaultUpdate { vault_key, asset, .. } in updates { - match asset { - Some(asset) => patch.insert_asset(asset), - None => patch.remove_asset(vault_key), - } - } - patch + Ok((block_num, asset_id, asset)) } diff --git a/crates/rust-client/src/rpc/domain/storage_map.rs b/crates/rust-client/src/rpc/domain/storage_map.rs index 5d661f963c..ae7a06855e 100644 --- a/crates/rust-client/src/rpc/domain/storage_map.rs +++ b/crates/rust-client/src/rpc/domain/storage_map.rs @@ -9,15 +9,6 @@ use miden_protocol::block::BlockNumber; use crate::rpc::domain::MissingFieldHelper; use crate::rpc::{RpcError, generated as proto}; -/// A single storage-map update as reported by the node: the block it occurred in, the affected slot -/// and key, and the new value. -pub(crate) struct StorageMapUpdate { - pub(crate) block_num: BlockNumber, - pub(crate) slot_name: StorageSlotName, - pub(crate) key: StorageMapKey, - pub(crate) value: Word, -} - // STORAGE MAP INFO // ================================================================================================ @@ -46,16 +37,25 @@ impl TryFrom for StorageMapInfo { proto::rpc::SyncAccountStorageMapsResponse::missing_field(stringify!(pagination_info)), )?; - let updates = value + let mut updates = value .updates .into_iter() .map(storage_map_update_from_proto) .collect::, _>>()?; + // The node may report the same `(slot, key)` in more than one block, folding the updates + // in ascending block order lets the latest block win, with an empty value encoding a + // cleared entry. + updates.sort_by_key(|(block_num, ..)| *block_num); + let mut map_entries: BTreeMap = BTreeMap::new(); + for (_, slot_name, key, value) in updates { + map_entries.entry(slot_name).or_default().insert(key, value); + } + Ok(Self { chain_tip: pagination_info.chain_tip.into(), block_number: pagination_info.block_num.into(), - map_entries: merge_storage_map_updates(updates), + map_entries, }) } } @@ -63,10 +63,10 @@ impl TryFrom for StorageMapInfo { // STORAGE MAP UPDATE // ================================================================================================ -/// Converts a single proto storage-map update into a [`StorageMapUpdate`]. +/// Converts a single proto storage-map update into its block number, slot name, key, and new value. fn storage_map_update_from_proto( value: proto::rpc::StorageMapUpdate, -) -> Result { +) -> Result<(BlockNumber, StorageSlotName, StorageMapKey, Word), RpcError> { let block_num = BlockNumber::from(value.block_num); let slot_name = StorageSlotName::new(value.slot_name) @@ -82,26 +82,5 @@ fn storage_map_update_from_proto( .ok_or(proto::rpc::StorageMapUpdate::missing_field(stringify!(value)))? .try_into()?; - Ok(StorageMapUpdate { - block_num, - slot_name, - key, - value: map_value, - }) -} - -/// Merges per-block map updates into the absolute changed entries per slot. -/// -/// The node may report the same `(slot, key)` in more than one block; applying the updates in -/// ascending block order lets the latest block win, with an empty value encoding a cleared entry. -pub(crate) fn merge_storage_map_updates( - mut updates: Vec, -) -> BTreeMap { - updates.sort_by_key(|update| update.block_num); - - let mut entries_by_slot: BTreeMap = BTreeMap::new(); - for StorageMapUpdate { slot_name, key, value, .. } in updates { - entries_by_slot.entry(slot_name).or_default().insert(key, value); - } - entries_by_slot + Ok((block_num, slot_name, key, map_value)) } diff --git a/crates/rust-client/src/rpc/mod.rs b/crates/rust-client/src/rpc/mod.rs index b3d273d0bd..5c46286568 100644 --- a/crates/rust-client/src/rpc/mod.rs +++ b/crates/rust-client/src/rpc/mod.rs @@ -418,6 +418,7 @@ pub trait NodeRpcClient: Send + Sync { continue; } // Syncing from genesis merges the full history of each slot into its absolute + // current entries, so the result is the complete map content. let entries: Vec = info .map_entries .get(&map_details.slot_name) diff --git a/crates/rust-client/src/sync/mod.rs b/crates/rust-client/src/sync/mod.rs index 21c5cadc8d..3f77cd8435 100644 --- a/crates/rust-client/src/sync/mod.rs +++ b/crates/rust-client/src/sync/mod.rs @@ -87,7 +87,6 @@ mod state_sync_update; pub use state_sync_update::{ AccountUpdates, PartialBlockchainUpdates, - PublicAccountPatch, PublicAccountUpdate, StateSyncUpdate, TransactionUpdateTracker, diff --git a/crates/rust-client/src/sync/state_sync.rs b/crates/rust-client/src/sync/state_sync.rs index 51af4031b9..7aed7582c2 100644 --- a/crates/rust-client/src/sync/state_sync.rs +++ b/crates/rust-client/src/sync/state_sync.rs @@ -13,12 +13,11 @@ use miden_protocol::crypto::merkle::mmr::{MmrDelta, PartialMmr}; use miden_protocol::note::{NoteAttachments, NoteId, NoteTag, NoteType, Nullifier}; use tracing::info; -use super::state_sync_update::TransactionUpdateTracker; +use super::state_sync_update::{TransactionUpdateTracker, build_account_patch}; use super::{ AccountUpdates, NoteObserver, PartialBlockchainUpdates, - PublicAccountPatch, PublicAccountUpdate, StateSyncUpdate, }; @@ -885,11 +884,11 @@ impl StateSync { details.storage_details.map_details.iter().any(|m| m.too_many_entries); // TODO: we can handle vault and storage-map oversize independently. Today any oversize - // routes the whole account through the incremental delta path, which always fetches + // routes the whole account through the incremental patch path, which always fetches // both `sync_storage_maps` and `sync_account_vault`, even if not needed. let public_update = if vault_oversized || any_map_oversized { // Some part of the account is oversized — use incremental endpoints. - self.build_delta_update(account_id, &details, block_from, proof_block_num) + self.build_patch_update(account_id, &details, block_from, proof_block_num) .await? } else { // The single response carries the full vault and every map's entries. @@ -942,9 +941,9 @@ impl StateSync { Ok(details.expect("node returned no details for a public account")) } - /// Builds a [`PublicAccountUpdate::Delta`] by fetching incremental storage map and vault - /// updates over the synced range. - async fn build_delta_update( + /// Builds a [`PublicAccountUpdate::Patch`] by fetching incremental storage map and vault + /// updates over the synced range and assembling the absolute [`AccountPatch`] from them. + async fn build_patch_update( &self, account_id: AccountId, details: &AccountDetails, @@ -972,12 +971,19 @@ impl StateSync { .await .map_err(ClientError::RpcError)?; - Ok(PublicAccountUpdate::Patch(PublicAccountPatch::new( - details.header.clone(), + let patch = build_account_patch( + &details.header, value_slot_updates, map_info.map_entries, vault_info.vault_patch, - ))) + details.code.clone(), + ) + .map_err(StoreError::AccountPatchError)?; + + Ok(PublicAccountUpdate::Patch { + new_header: details.header.clone(), + patch, + }) } /// Applies the changes received from the sync response to the notes and transactions tracked diff --git a/crates/rust-client/src/sync/state_sync_update.rs b/crates/rust-client/src/sync/state_sync_update.rs index 2aeeecdd0d..009e9bbaa0 100644 --- a/crates/rust-client/src/sync/state_sync_update.rs +++ b/crates/rust-client/src/sync/state_sync_update.rs @@ -342,17 +342,22 @@ impl TransactionUpdateTracker { /// - [`PublicAccountUpdate::Full`] carries the new [`Account`] state directly (used when no storage /// map is oversized and the vault fits in the response). The store applies it by replacing the /// local state. -/// - [`PublicAccountUpdate::Patch`] carries a [`PublicAccountPatch`] payload (new header plus -/// incremental updates from `sync_storage_maps` and `sync_account_vault`, used when any part of -/// the account is oversized). The store calls [`PublicAccountPatch::compute_account_patch`] to -/// build the absolute [`AccountPatch`] to apply. +/// - [`PublicAccountUpdate::Patch`] carries the new account header plus the absolute +/// [`AccountPatch`] built from the node's incremental endpoints (`sync_storage_maps` and +/// `sync_account_vault`, used when any part of the account is oversized). The header is included +/// because the patch does not carry the final commitments. #[derive(Debug, Clone)] pub enum PublicAccountUpdate { /// The account fits in a single proof response — the new full state is carried as-is. Full(Account), /// The account is oversized in some dimension. The new state is described by the absolute - /// patch built from the carried incremental updates. - Patch(PublicAccountPatch), + /// patch, which advances the local state to `new_header`. + Patch { + /// The new account header after applying the patch. + new_header: AccountHeader, + /// The absolute patch to apply. + patch: AccountPatch, + }, } impl PublicAccountUpdate { @@ -360,7 +365,7 @@ impl PublicAccountUpdate { pub fn id(&self) -> AccountId { match self { Self::Full(account) => account.id(), - Self::Patch(patch) => patch.id(), + Self::Patch { new_header, .. } => new_header.id(), } } @@ -368,105 +373,55 @@ impl PublicAccountUpdate { pub fn nonce(&self) -> Felt { match self { Self::Full(account) => account.nonce(), - Self::Patch(patch) => patch.new_header().nonce(), + Self::Patch { new_header, .. } => new_header.nonce(), } } } -/// Patch payload for a public account update. +/// Builds the absolute [`AccountPatch`] implied by the updates fetched from the node's incremental +/// endpoints: the value-slot values, the absolute changed map entries per slot, and the absolute +/// vault patch. /// -/// Carries the new account header plus the merged updates fetched from the node's incremental -/// endpoints (`sync_storage_maps` and `sync_account_vault`): the value-slot values, the absolute -/// changed map entries per slot, and the absolute vault patch. The store turns these into the -/// [`AccountPatch`] to apply via [`Self::compute_account_patch`]. -#[derive(Debug, Clone)] -pub struct PublicAccountPatch { - /// The new account header after applying these updates. - new_header: AccountHeader, - /// New value-slot values from the `get_account` storage header. Value slots are always - /// small enough to fit in the response. +/// The carried updates are already merged to the new absolute value of each changed storage slot, +/// map entry, and vault asset, so the patch is assembled directly from them with no need to load +/// the prior account state. +/// +/// An update of an existing account (final nonce > 1) yields a partial-state patch with no code. A +/// newly created account (final nonce 1) cannot be represented as a partial-state patch, so the +/// patch becomes a full-state patch carrying `code` (already validated against the on-chain code +/// commitment by the caller). +pub(crate) fn build_account_patch( + new_header: &AccountHeader, value_slot_updates: Vec<(StorageSlotName, Word)>, - /// Absolute changed entries per storage map slot, merged from `sync_storage_maps`. map_entries: BTreeMap, - /// Absolute vault patch merged from `sync_account_vault`. vault_patch: AccountVaultPatch, -} - -impl PublicAccountPatch { - /// Creates a new [`PublicAccountPatch`]. - pub fn new( - new_header: AccountHeader, - value_slot_updates: Vec<(StorageSlotName, Word)>, - map_entries: BTreeMap, - vault_patch: AccountVaultPatch, - ) -> Self { - Self { - new_header, - value_slot_updates, - map_entries, - vault_patch, - } - } - - /// Returns the account ID this patch applies to. - pub fn id(&self) -> AccountId { - self.new_header.id() - } - - /// Returns the new account header that this patch advances the local state to. - pub fn new_header(&self) -> &AccountHeader { - &self.new_header - } - - /// Builds the absolute [`AccountPatch`] implied by this payload. - /// - /// The carried updates are already merged to the new absolute value of each changed storage - /// slot, map entry, and vault asset, so the patch is assembled directly from them with no need - /// to load the prior account state. - /// - /// An update of an existing account (final nonce > 1) yields a partial-state patch with no - /// code. A newly created account (final nonce 1) cannot be represented as a partial-state - /// patch, so it must carry the account code: the caller supplies `code` (the account's code - /// is fixed at creation, so the locally-tracked code matches), and the patch becomes a - /// full-state patch. - pub fn compute_account_patch( - &self, - code: Option, - ) -> Result { - let account_id = self.new_header.id(); - - let is_full_state = self.new_header.nonce() == ONE; - - let value_entries = self.value_slot_updates.iter().map(|(slot_name, new_value)| { - let value_patch = if is_full_state { - StorageValuePatch::Create { value: *new_value } - } else { - StorageValuePatch::Update { value: *new_value } - }; - (slot_name.clone(), StorageSlotPatch::Value(value_patch)) - }); - - let map_entries = self.map_entries.iter().map(|(slot_name, entries)| { - let map_patch = if is_full_state { - StorageMapPatch::Create { entries: entries.clone() } - } else { - StorageMapPatch::Update { entries: entries.clone() } - }; - (slot_name.clone(), StorageSlotPatch::Map(map_patch)) - }); - - let storage = AccountStoragePatch::from_entries(value_entries.chain(map_entries))?; - - let code = if is_full_state { code } else { None }; - - AccountPatch::new( - account_id, - storage, - self.vault_patch.clone(), - code, - Some(self.new_header.nonce()), - ) - } + code: AccountCode, +) -> Result { + let is_full_state = new_header.nonce() == ONE; + + let value_entries = value_slot_updates.into_iter().map(|(slot_name, new_value)| { + let value_patch = if is_full_state { + StorageValuePatch::Create { value: new_value } + } else { + StorageValuePatch::Update { value: new_value } + }; + (slot_name, StorageSlotPatch::Value(value_patch)) + }); + + let map_entries = map_entries.into_iter().map(|(slot_name, entries)| { + let map_patch = if is_full_state { + StorageMapPatch::Create { entries } + } else { + StorageMapPatch::Update { entries } + }; + (slot_name, StorageSlotPatch::Map(map_patch)) + }); + + let storage = AccountStoragePatch::from_entries(value_entries.chain(map_entries))?; + + let code = is_full_state.then_some(code); + + AccountPatch::new(new_header.id(), storage, vault_patch, code, Some(new_header.nonce())) } // ACCOUNT UPDATES @@ -476,7 +431,7 @@ impl PublicAccountPatch { #[derive(Debug, Clone, Default)] #[allow(clippy::struct_field_names)] pub struct AccountUpdates { - /// Updated public accounts, either as full state replacements or incremental deltas. + /// Updated public accounts, either as full state replacements or incremental patches. updated_public_accounts: Vec, /// Account commitments received from the network that don't match the currently /// locally-tracked state of the private accounts. @@ -555,26 +510,23 @@ mod tests { ) } - fn payload( + fn build_patch( new_nonce: u64, value_slot_updates: Vec<(StorageSlotName, Word)>, map_entries: BTreeMap, - ) -> PublicAccountPatch { - PublicAccountPatch::new( - header_with_nonce(new_nonce), + ) -> Result { + build_account_patch( + &header_with_nonce(new_nonce), value_slot_updates, map_entries, AccountVaultPatch::default(), + AccountCode::mock(), ) } - fn empty_payload(new_header: AccountHeader) -> PublicAccountPatch { - PublicAccountPatch::new(new_header, vec![], BTreeMap::new(), AccountVaultPatch::default()) - } - #[test] - fn compute_patch_empty_payload_carries_only_nonce() { - let patch = empty_payload(header_with_nonce(4)).compute_account_patch(None).unwrap(); + fn build_patch_empty_payload_carries_only_nonce() { + let patch = build_patch(4, vec![], BTreeMap::new()).unwrap(); assert_eq!(patch.final_nonce(), Some(Felt::new_unchecked(4))); assert!(patch.storage().is_empty()); @@ -583,24 +535,22 @@ mod tests { } #[test] - fn compute_patch_sets_value_slot_absolutely() { + fn build_patch_sets_value_slot_absolutely() { let value_slot = slot_name("miden::test::value"); - let patch = payload(2, vec![(value_slot.clone(), word(2))], BTreeMap::new()) - .compute_account_patch(None) - .unwrap(); + let patch = build_patch(2, vec![(value_slot.clone(), word(2))], BTreeMap::new()).unwrap(); assert_eq!(patch.storage().updated_value(&value_slot), Some(word(2))); } #[test] - fn compute_patch_wraps_merged_map_entries() { + fn build_patch_wraps_merged_map_entries() { let map_slot = slot_name("miden::test::map"); let key = StorageMapKey::from_raw(word(42)); let mut entries = StorageMapPatchEntries::new(); entries.insert(key, word(300)); let map_entries = BTreeMap::from([(map_slot.clone(), entries)]); - let patch = payload(2, vec![], map_entries).compute_account_patch(None).unwrap(); + let patch = build_patch(2, vec![], map_entries).unwrap(); let entries = patch.storage().updated_map(&map_slot).expect("patch should contain map slot"); @@ -609,44 +559,32 @@ mod tests { } #[test] - fn compute_patch_rejects_zero_nonce() { - let result = empty_payload(header_with_nonce(0)).compute_account_patch(None); + fn build_patch_rejects_zero_nonce() { + let result = build_patch(0, vec![], BTreeMap::new()); assert!(result.is_err()); } /// A newly created account (final nonce 1) observed via the oversized sync path yields a /// full-state patch carrying the supplied code, rather than failing to build. #[test] - fn compute_patch_for_new_account_is_full_state() { + fn build_patch_for_new_account_is_full_state() { let value_slot = slot_name("miden::test::value"); - let patch = payload(1, vec![(value_slot, word(1))], BTreeMap::new()) - .compute_account_patch(Some(AccountCode::mock())) - .unwrap(); + let patch = build_patch(1, vec![(value_slot, word(1))], BTreeMap::new()).unwrap(); assert!(patch.is_full_state()); assert_eq!(patch.final_nonce(), Some(ONE)); } - /// A final-nonce-1 patch cannot be built without the account code (it must be a full-state - /// patch), so the missing-code case is reported rather than silently producing a wrong patch. - #[test] - fn compute_patch_for_new_account_without_code_errors() { - let result = payload(1, vec![], BTreeMap::new()).compute_account_patch(None); - assert!(result.is_err()); - } - /// A newly created account (final nonce 1, full-state) emits each map slot as a `Create`, which /// the store applies by starting the slot from an empty map. #[test] - fn compute_patch_emits_map_create_for_new_account() { + fn build_patch_emits_map_create_for_new_account() { let map_slot = slot_name("miden::test::map"); let mut entries = StorageMapPatchEntries::new(); entries.insert(StorageMapKey::from_raw(word(1)), word(100)); let map_entries = BTreeMap::from([(map_slot.clone(), entries)]); - let patch = payload(1, vec![], map_entries) - .compute_account_patch(Some(AccountCode::mock())) - .unwrap(); + let patch = build_patch(1, vec![], map_entries).unwrap(); assert!(patch.storage().created_map(&map_slot).is_some()); } @@ -654,13 +592,13 @@ mod tests { /// An update to an existing account (final nonce > 1) emits map slots as `Update`, never /// `Create`, so the sync path never asks the store to re-create a populated map. #[test] - fn compute_patch_emits_map_update_for_existing_account() { + fn build_patch_emits_map_update_for_existing_account() { let map_slot = slot_name("miden::test::map"); let mut entries = StorageMapPatchEntries::new(); entries.insert(StorageMapKey::from_raw(word(1)), word(100)); let map_entries = BTreeMap::from([(map_slot.clone(), entries)]); - let patch = payload(2, vec![], map_entries).compute_account_patch(None).unwrap(); + let patch = build_patch(2, vec![], map_entries).unwrap(); assert!(patch.storage().updated_map(&map_slot).is_some()); } diff --git a/crates/rust-client/src/test_utils/common.rs b/crates/rust-client/src/test_utils/common.rs index faeb4fdaf3..877fca29f9 100644 --- a/crates/rust-client/src/test_utils/common.rs +++ b/crates/rust-client/src/test_utils/common.rs @@ -503,7 +503,7 @@ pub async fn assert_account_has_single_asset( .get_balance(faucet_id) .await .expect("Account should have the asset"); - assert_eq!(balance, expected_amount); + assert_eq!(balance, AssetAmount::new(expected_amount).unwrap()); } /// Tries to consume the note and asserts that the expected error is returned. diff --git a/crates/rust-client/src/test_utils/mock.rs b/crates/rust-client/src/test_utils/mock.rs index 5e21e91d98..8e324fcebc 100644 --- a/crates/rust-client/src/test_utils/mock.rs +++ b/crates/rust-client/src/test_utils/mock.rs @@ -7,8 +7,11 @@ use miden_protocol::Word; use miden_protocol::account::{ AccountId, AccountUpdateDetails, + AccountVaultPatch, + StorageMapPatchEntries, StorageSlot, StorageSlotContent, + StorageSlotName, StorageSlotType, }; use miden_protocol::address::NetworkId; @@ -32,15 +35,11 @@ use crate::rpc::domain::account::{ StorageMapEntry, StorageMapFetch, }; -use crate::rpc::domain::account_vault::{AccountVaultInfo, VaultUpdate, merge_vault_updates}; +use crate::rpc::domain::account_vault::AccountVaultInfo; use crate::rpc::domain::note::{CommittedNote, FetchedNote, NoteSyncBlock}; use crate::rpc::domain::nullifier::NullifierUpdate; use crate::rpc::domain::status::NetworkNoteStatusInfo; -use crate::rpc::domain::storage_map::{ - StorageMapInfo, - StorageMapUpdate, - merge_storage_map_updates, -}; +use crate::rpc::domain::storage_map::StorageMapInfo; use crate::rpc::domain::sync::{ChainMmrInfo, SyncTarget}; use crate::rpc::domain::transaction::TransactionRecord; use crate::rpc::{AccountStateAt, NodeRpcClient, RpcError, RpcStatusInfo}; @@ -152,7 +151,7 @@ impl MockRpcApi { block_from: BlockNumber, block_to: BlockNumber, account_id: AccountId, - ) -> (BlockNumber, BlockNumber, Vec) { + ) -> (BlockNumber, BlockNumber, AccountVaultPatch) { let chain_tip = self.get_chain_tip_block_num(); let target_block = block_to.min(chain_tip); @@ -160,7 +159,8 @@ impl MockRpcApi { .min(target_block.as_u32()) .into(); - let mut updates = vec![]; + // Blocks are iterated in ascending order, so later blocks win per asset ID. + let mut vault_patch = AccountVaultPatch::default(); for block in self.mock_chain.read().proven_blocks() { let block_number = block.header().block_num(); // Only include blocks in range [block_from, page_end_block] @@ -178,27 +178,11 @@ impl MockRpcApi { continue; }; - let vault_patch = patch.vault(); - - for asset in vault_patch.updated_assets() { - updates.push(VaultUpdate { - block_num: block_number, - vault_key: asset.id(), - asset: Some(asset), - }); - } - - for vault_key in vault_patch.removed_asset_ids() { - updates.push(VaultUpdate { - block_num: block_number, - vault_key: *vault_key, - asset: None, - }); - } + vault_patch.merge(patch.vault().clone()); } } - (chain_tip, page_end_block, updates) + (chain_tip, page_end_block, vault_patch) } /// Retrieves transactions in a given block range that match the provided account IDs @@ -242,7 +226,7 @@ impl MockRpcApi { block_from: BlockNumber, block_to: BlockNumber, account_id: AccountId, - ) -> (BlockNumber, BlockNumber, Vec) { + ) -> (BlockNumber, BlockNumber, BTreeMap) { let chain_tip = self.get_chain_tip_block_num(); let target_block = block_to.min(chain_tip); @@ -250,7 +234,8 @@ impl MockRpcApi { .min(target_block.as_u32()) .into(); - let mut updates = vec![]; + // Blocks are iterated in ascending order, so later blocks win per `(slot, key)`. + let mut map_entries: BTreeMap = BTreeMap::new(); for block in self.mock_chain.read().proven_blocks() { let block_number = block.header().block_num(); // Only include blocks in range [block_from, page_end_block] @@ -268,24 +253,19 @@ impl MockRpcApi { continue; }; - let storage_patch = patch.storage(); - - for (slot_name, map_patch) in storage_patch.maps() { + for (slot_name, map_patch) in patch.storage().maps() { if let Some(entries) = map_patch.entries() { - for (key, value) in entries.as_map() { - updates.push(StorageMapUpdate { - block_num: block_number, - slot_name: slot_name.clone(), - key: *key, - value: *value, - }); - } + map_entries + .entry(slot_name.clone()) + .or_default() + .as_map_mut() + .extend(entries.as_map().clone()); } } } } - (chain_tip, page_end_block, updates) + (chain_tip, page_end_block, map_entries) } pub fn get_available_notes(&self) -> Vec { @@ -639,21 +619,27 @@ impl NodeRpcClient for MockRpcApi { block_to: BlockNumber, account_id: AccountId, ) -> Result { - let mut all_updates = Vec::new(); + let mut map_entries: BTreeMap = BTreeMap::new(); let mut current_block_from = block_from; let chain_tip = self.get_chain_tip_block_num(); let target_block = block_to.min(chain_tip); loop { - let (page_chain_tip, page_block_number, updates) = + let (page_chain_tip, page_block_number, page_entries) = self.get_sync_storage_maps_request(current_block_from, block_to, account_id); - all_updates.extend(updates); + for (slot_name, entries) in page_entries { + map_entries + .entry(slot_name) + .or_default() + .as_map_mut() + .extend(entries.into_map()); + } if page_block_number >= target_block { return Ok(StorageMapInfo { chain_tip: page_chain_tip, block_number: page_block_number, - map_entries: merge_storage_map_updates(all_updates), + map_entries, }); } @@ -667,21 +653,21 @@ impl NodeRpcClient for MockRpcApi { block_to: BlockNumber, account_id: AccountId, ) -> Result { - let mut all_updates = Vec::new(); + let mut vault_patch = AccountVaultPatch::default(); let mut current_block_from = block_from; let chain_tip = self.get_chain_tip_block_num(); let target_block = block_to.min(chain_tip); loop { - let (page_chain_tip, page_block_number, updates) = + let (page_chain_tip, page_block_number, page_patch) = self.get_sync_account_vault_request(current_block_from, block_to, account_id); - all_updates.extend(updates); + vault_patch.merge(page_patch); if page_block_number >= target_block { return Ok(AccountVaultInfo { chain_tip: page_chain_tip, block_number: page_block_number, - vault_patch: merge_vault_updates(all_updates), + vault_patch, }); } diff --git a/crates/rust-client/src/transaction/request/builder.rs b/crates/rust-client/src/transaction/request/builder.rs index 395216ae1a..935d6582be 100644 --- a/crates/rust-client/src/transaction/request/builder.rs +++ b/crates/rust-client/src/transaction/request/builder.rs @@ -4,7 +4,7 @@ use alloc::string::ToString; use alloc::vec::Vec; use miden_protocol::account::AccountId; -use miden_protocol::asset::{Asset, FungibleAsset}; +use miden_protocol::asset::{Asset, AssetAmount, FungibleAsset}; use miden_protocol::block::BlockNumber; use miden_protocol::crypto::merkle::InnerNodeInfo; use miden_protocol::crypto::merkle::store::MerkleStore; @@ -491,23 +491,25 @@ impl TransactionRequestBuilder { self, pswap_note: &Note, consumer_account_id: AccountId, - account_fill_amount: u64, - note_fill_amount: u64, + account_fill_amount: AssetAmount, + note_fill_amount: AssetAmount, ) -> Result { let pswap = PswapNote::try_from(pswap_note) .map_err(TransactionRequestError::NoteValidationError)?; let requested_faucet_id = pswap.storage().min_requested_asset().faucet_id(); - let account_fill_asset = FungibleAsset::new(requested_faucet_id, account_fill_amount)?; - let note_fill_asset = FungibleAsset::new(requested_faucet_id, note_fill_amount)?; + let account_fill_asset = + FungibleAsset::new(requested_faucet_id, account_fill_amount.as_u64())?; + let note_fill_asset = FungibleAsset::new(requested_faucet_id, note_fill_amount.as_u64())?; let (payback_note, remainder_pswap) = pswap .execute(consumer_account_id, Some(account_fill_asset), Some(note_fill_asset)) .map_err(TransactionRequestError::NoteExecutionError)?; - let note_args = PswapNote::create_args(account_fill_amount, note_fill_amount) - .map_err(TransactionRequestError::NoteArgError)?; + let note_args = + PswapNote::create_args(account_fill_amount.as_u64(), note_fill_amount.as_u64()) + .map_err(TransactionRequestError::NoteArgError)?; // Payback and remainder both settle to the creator, not the consumer. Declare them as // expected recipients so the transaction is validated against them, but don't register diff --git a/crates/rust-client/src/utils.rs b/crates/rust-client/src/utils.rs index d018808bf2..eb74ad051a 100644 --- a/crates/rust-client/src/utils.rs +++ b/crates/rust-client/src/utils.rs @@ -5,6 +5,8 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::num::ParseIntError; +use miden_protocol::asset::AssetAmount; +use miden_protocol::errors::AssetError; use miden_standards::account::faucets::FungibleFaucet; pub use miden_tx::utils::serde::{ ByteReader, @@ -21,8 +23,8 @@ use crate::alloc::borrow::ToOwned; /// Converts an amount in the faucet base units to the token's decimals. /// /// This is meant for display purposes only. -pub fn base_units_to_tokens(units: u64, decimals: u8) -> String { - let units_str = units.to_string(); +pub fn base_units_to_tokens(units: AssetAmount, decimals: u8) -> String { + let units_str = units.as_u64().to_string(); let len = units_str.len(); if decimals == 0 { @@ -52,11 +54,16 @@ pub enum TokenParseError { ParseU64(#[source] ParseIntError), #[error("Amount has more than {0} decimal places")] TooManyDecimals(u8), + #[error("Amount is not a valid asset amount")] + InvalidAmount(#[source] AssetError), } /// Converts a decimal number, represented as a string, into an integer by shifting /// the decimal point to the right by a specified number of decimal places. -pub fn tokens_to_base_units(decimal_str: &str, n_decimals: u8) -> Result { +pub fn tokens_to_base_units( + decimal_str: &str, + n_decimals: u8, +) -> Result { if n_decimals > FungibleFaucet::MAX_DECIMALS { return Err(TokenParseError::MaxDecimals(n_decimals)); } @@ -97,7 +104,9 @@ pub fn tokens_to_base_units(decimal_str: &str, n_decimals: u8) -> Result().map_err(TokenParseError::ParseU64) + let units = combined.parse::().map_err(TokenParseError::ParseU64)?; + + AssetAmount::new(units).map_err(TokenParseError::InvalidAmount) } // TESTS @@ -105,18 +114,22 @@ pub fn tokens_to_base_units(decimal_str: &str, n_decimals: u8) -> Result AssetAmount { + AssetAmount::new(units).unwrap() + } + #[test] fn convert_tokens_to_base_units() { - assert_eq!(tokens_to_base_units("18446744.073709551615", 12).unwrap(), u64::MAX); - assert_eq!(tokens_to_base_units("7531.2468", 8).unwrap(), 753_124_680_000); - assert_eq!(tokens_to_base_units("7531.2468", 4).unwrap(), 75_312_468); - assert_eq!(tokens_to_base_units("0", 3).unwrap(), 0); - assert_eq!(tokens_to_base_units("0", 3).unwrap(), 0); - assert_eq!(tokens_to_base_units("0", 3).unwrap(), 0); - assert_eq!(tokens_to_base_units("1234", 8).unwrap(), 123_400_000_000); - assert_eq!(tokens_to_base_units("1", 0).unwrap(), 1); + assert_eq!(tokens_to_base_units("9223372.034707292160", 12).unwrap(), AssetAmount::MAX); + assert_eq!(tokens_to_base_units("7531.2468", 8).unwrap(), amount(753_124_680_000)); + assert_eq!(tokens_to_base_units("7531.2468", 4).unwrap(), amount(75_312_468)); + assert_eq!(tokens_to_base_units("0", 3).unwrap(), AssetAmount::ZERO); + assert_eq!(tokens_to_base_units("1234", 8).unwrap(), amount(123_400_000_000)); + assert_eq!(tokens_to_base_units("1", 0).unwrap(), amount(1)); assert!(matches!( tokens_to_base_units("1.1", 0), Err(TokenParseError::TooManyDecimals(0)) @@ -127,15 +140,20 @@ mod tests { ),); assert!(matches!(tokens_to_base_units("123u3.23", 4), Err(TokenParseError::ParseU64(_))),); assert!(matches!(tokens_to_base_units("2.k3", 4), Err(TokenParseError::ParseU64(_))),); - assert_eq!(tokens_to_base_units("12.345000", 4).unwrap(), 123_450); + assert_eq!(tokens_to_base_units("12.345000", 4).unwrap(), amount(123_450)); assert!(tokens_to_base_units("0.0001.00000001", 12).is_err()); + // Parses as a u64 but exceeds the maximum representable asset amount. + assert!(matches!( + tokens_to_base_units("18446744.073709551615", 12), + Err(TokenParseError::InvalidAmount(_)) + ),); } #[test] fn convert_base_units_to_tokens() { - assert_eq!(base_units_to_tokens(u64::MAX, 12), "18446744.073709551615"); - assert_eq!(base_units_to_tokens(753_124_680_000, 8), "7531.24680000"); - assert_eq!(base_units_to_tokens(75_312_468, 4), "7531.2468"); - assert_eq!(base_units_to_tokens(75_312_468, 0), "75312468"); + assert_eq!(base_units_to_tokens(AssetAmount::MAX, 12), "9223372.034707292160"); + assert_eq!(base_units_to_tokens(amount(753_124_680_000), 8), "7531.24680000"); + assert_eq!(base_units_to_tokens(amount(75_312_468), 4), "7531.2468"); + assert_eq!(base_units_to_tokens(amount(75_312_468), 0), "75312468"); } } diff --git a/crates/sqlite-store/src/account/accounts.rs b/crates/sqlite-store/src/account/accounts.rs index bc07c1bf55..eb277247f7 100644 --- a/crates/sqlite-store/src/account/accounts.rs +++ b/crates/sqlite-store/src/account/accounts.rs @@ -430,7 +430,7 @@ impl SqliteStore { Ok(()) } - /// Applies the account delta to the account state, updating the vault and storage maps. + /// Applies the account patch to the account state, updating the vault and storage maps. /// /// Archives old values from latest to historical and updates latest via INSERT OR REPLACE. pub(crate) fn apply_account_patch( @@ -842,7 +842,7 @@ impl SqliteStore { Ok(()) } - /// Applies an incremental delta to a public account's state during sync. + /// Applies an incremental patch to a public account's state during sync. pub(crate) fn apply_sync_account_patch( tx: &Transaction<'_>, smt_forest: &mut AccountSmtForest, diff --git a/crates/sqlite-store/src/account/helpers.rs b/crates/sqlite-store/src/account/helpers.rs index 5465538ac5..90db069915 100644 --- a/crates/sqlite-store/src/account/helpers.rs +++ b/crates/sqlite-store/src/account/helpers.rs @@ -155,7 +155,7 @@ pub(crate) fn query_historical_account_headers( // TODO: this function will probably be refactored to receive more complex where clauses and // return multiple mast forests -pub(crate) fn query_account_code( +pub(super) fn query_account_code( conn: &Connection, commitment: Word, ) -> Result, StoreError> { diff --git a/crates/sqlite-store/src/account/storage.rs b/crates/sqlite-store/src/account/storage.rs index 2fc803660a..6828891f75 100644 --- a/crates/sqlite-store/src/account/storage.rs +++ b/crates/sqlite-store/src/account/storage.rs @@ -401,7 +401,12 @@ impl SqliteStore { .map(|(slot_name, value_patch)| { ( slot_name.clone(), - (value_patch.value().unwrap_or_default(), StorageSlotType::Value), + ( + value_patch + .value() + .expect("the protocol does not generate Remove value patches"), + StorageSlotType::Value, + ), ) }) .collect(); diff --git a/crates/sqlite-store/src/sync.rs b/crates/sqlite-store/src/sync.rs index 9b7ea9b08e..4c2b57db7c 100644 --- a/crates/sqlite-store/src/sync.rs +++ b/crates/sqlite-store/src/sync.rs @@ -8,18 +8,11 @@ use miden_client::Word; use miden_client::account::AccountId; use miden_client::note::{BlockNumber, NoteTag}; use miden_client::store::{AccountSmtForest, StoreError}; -use miden_client::sync::{ - NoteTagRecord, - NoteTagSource, - PublicAccountPatch, - PublicAccountUpdate, - StateSyncUpdate, -}; +use miden_client::sync::{NoteTagRecord, NoteTagSource, PublicAccountUpdate, StateSyncUpdate}; use miden_client::utils::{Deserializable, Serializable}; use rusqlite::{Connection, Transaction, params}; use super::SqliteStore; -use crate::account::helpers::query_account_code; use crate::note::apply_note_updates_tx; use crate::sql_error::SqlResultExt; use crate::transaction::{upsert_transaction_record, with_forest_snapshot}; @@ -195,8 +188,8 @@ impl SqliteStore { PublicAccountUpdate::Full(account) => { Self::update_account_state(tx, smt_forest, account)?; }, - PublicAccountUpdate::Patch(delta) => { - Self::apply_public_account_patch(tx, smt_forest, delta)?; + PublicAccountUpdate::Patch { new_header, patch } => { + Self::apply_sync_account_patch(tx, smt_forest, new_header, patch)?; }, } } @@ -208,31 +201,6 @@ impl SqliteStore { Ok(()) }) } - - /// Builds the absolute [`AccountPatch`] from `patch`'s incremental payload and applies it. - /// - /// The patch carries the new absolute value of every changed storage slot, map entry, and - /// vault asset, so no prior account storage or vault state needs to be loaded here. - fn apply_public_account_patch( - tx: &Transaction<'_>, - smt_forest: &mut AccountSmtForest, - patch: &PublicAccountPatch, - ) -> Result<(), StoreError> { - // A newly created account (final nonce 1) must be applied as a full-state patch carrying - // its code. The code is fixed at creation, so the locally-tracked code (persisted - // when the account was added) matches the new header's code commitment. - let account_id = patch.id(); - let code = if patch.new_header().nonce().as_canonical_u64() == 1 { - let code = query_account_code(tx, patch.new_header().code_commitment())? - .ok_or(StoreError::AccountDataNotFound(account_id))?; - Some(code) - } else { - None - }; - - let account_patch = patch.compute_account_patch(code)?; - Self::apply_sync_account_patch(tx, smt_forest, patch.new_header(), &account_patch) - } } pub(super) fn add_note_tag_tx(tx: &Transaction<'_>, tag: &NoteTagRecord) -> Result<(), StoreError> { diff --git a/crates/testing/miden-client-tests/src/tests.rs b/crates/testing/miden-client-tests/src/tests.rs index 35562773ed..2882f6dc90 100644 --- a/crates/testing/miden-client-tests/src/tests.rs +++ b/crates/testing/miden-client-tests/src/tests.rs @@ -1302,14 +1302,14 @@ async fn p2id_transfer() { .get_balance(faucet_account_id) .await .unwrap(); - assert_eq!(from_balance, MINT_AMOUNT - TRANSFER_AMOUNT); + assert_eq!(from_balance, AssetAmount::new(MINT_AMOUNT - TRANSFER_AMOUNT).unwrap()); let to_balance = client .account_reader(to_account_id) .get_balance(faucet_account_id) .await .unwrap(); - assert_eq!(to_balance, TRANSFER_AMOUNT); + assert_eq!(to_balance, AssetAmount::new(TRANSFER_AMOUNT).unwrap()); assert_note_cannot_be_consumed_twice( &mut client, @@ -1818,14 +1818,20 @@ async fn p2ide_transfer_consumed_by_target() { .get_balance(faucet_account_id) .await .unwrap(); - assert_eq!(new_from_balance, from_account_balance - TRANSFER_AMOUNT); + assert_eq!( + new_from_balance, + (from_account_balance - AssetAmount::new(TRANSFER_AMOUNT).unwrap()).unwrap() + ); let new_to_balance = client .account_reader(to_account_id) .get_balance(faucet_account_id) .await .unwrap(); - assert_eq!(new_to_balance, to_account_balance + TRANSFER_AMOUNT); + assert_eq!( + new_to_balance, + (to_account_balance + AssetAmount::new(TRANSFER_AMOUNT).unwrap()).unwrap() + ); assert_note_cannot_be_consumed_twice(&mut client, to_account_id, note).await; } @@ -1936,7 +1942,7 @@ async fn p2ide_transfer_consumed_by_sender() { .get_balance(faucet_account_id) .await .unwrap(); - assert_eq!(to_balance, 0); + assert_eq!(to_balance, AssetAmount::ZERO); // Check that the target can't consume the note anymore assert_note_cannot_be_consumed_twice( @@ -2032,7 +2038,7 @@ async fn p2ide_timelocked() { .get_balance(faucet_account_id) .await .unwrap(); - assert_eq!(target_balance, TRANSFER_AMOUNT); + assert_eq!(target_balance, AssetAmount::new(TRANSFER_AMOUNT).unwrap()); } #[tokio::test] @@ -2689,7 +2695,7 @@ async fn swap_chain_test() { .get_balance(account_pairs[0].1.id()) .await .unwrap(); - assert_eq!(last_wallet_balance, 1); + assert_eq!(last_wallet_balance, AssetAmount::new(1).unwrap()); } /// Tests that partial output notes (created when a SWAP note is consumed) are correctly included in @@ -2860,15 +2866,19 @@ async fn pswap_fill_test( alice_account .vault() .get_balance(AssetId::new_fungible(eth_faucet.id())) - .unwrap() - .as_u64(), - MINT_AMOUNT - offered_amount, + .unwrap(), + AssetAmount::new(MINT_AMOUNT - offered_amount).unwrap(), "Alice's ETH balance should decrease by the offered amount" ); // Step 2: Bob fills the PSWAP note. let consume_request = TransactionRequestBuilder::new() - .build_pswap_consume(&pswap_note, bob_wallet.id(), account_fill_amount, 0) + .build_pswap_consume( + &pswap_note, + bob_wallet.id(), + AssetAmount::new(account_fill_amount).unwrap(), + AssetAmount::ZERO, + ) .unwrap(); Box::pin(client.submit_new_transaction(bob_wallet.id(), consume_request)) @@ -2882,23 +2892,15 @@ async fn pswap_fill_test( // Bob spent exactly the fill amount — proves NOTE_ARGS were honored (a wrong layout would // fall back to the script's full-fill default path). assert_eq!( - bob_account - .vault() - .get_balance(AssetId::new_fungible(usd_faucet.id())) - .unwrap() - .as_u64(), - MINT_AMOUNT - account_fill_amount, + bob_account.vault().get_balance(AssetId::new_fungible(usd_faucet.id())).unwrap(), + AssetAmount::new(MINT_AMOUNT - account_fill_amount).unwrap(), "Bob's USD balance should decrease by exactly the fill amount" ); // Bob received the proportional payout. assert_eq!( - bob_account - .vault() - .get_balance(AssetId::new_fungible(eth_faucet.id())) - .unwrap() - .as_u64(), - expected_payout, + bob_account.vault().get_balance(AssetId::new_fungible(eth_faucet.id())).unwrap(), + AssetAmount::new(expected_payout).unwrap(), "Bob should have received the expected ETH payout" ); @@ -2981,9 +2983,8 @@ async fn pswap_cancel_test() { alice_account .vault() .get_balance(AssetId::new_fungible(eth_faucet.id())) - .unwrap() - .as_u64(), - MINT_AMOUNT - offered_amount, + .unwrap(), + AssetAmount::new(MINT_AMOUNT - offered_amount).unwrap(), "Alice's ETH balance should decrease by the offered amount" ); @@ -3004,9 +3005,8 @@ async fn pswap_cancel_test() { alice_account .vault() .get_balance(AssetId::new_fungible(eth_faucet.id())) - .unwrap() - .as_u64(), - MINT_AMOUNT, + .unwrap(), + AssetAmount::new(MINT_AMOUNT).unwrap(), "Alice's ETH balance should be fully restored after canceling the PSWAP note" ); } @@ -3148,7 +3148,12 @@ async fn pswap_chain_tracking_test(#[case] note_type: NoteType) { // ── Bob partial-fills: 25 ETH → 50 BTC payout, leaving 50 BTC / 25 ETH. ── let consume_request = TransactionRequestBuilder::new() - .build_pswap_consume(&pswap_note, bob_wallet.id(), 25, 0) + .build_pswap_consume( + &pswap_note, + bob_wallet.id(), + AssetAmount::new(25).unwrap(), + AssetAmount::ZERO, + ) .unwrap(); Box::pin(bob_client.submit_new_transaction(bob_wallet.id(), consume_request)) .await @@ -3218,9 +3223,8 @@ async fn pswap_chain_tracking_test(#[case] note_type: NoteType) { alice_account .vault() .get_balance(AssetId::new_fungible(eth_faucet.id())) - .unwrap() - .as_u64(), - 25, + .unwrap(), + AssetAmount::new(25).unwrap(), "Alice should have received 25 ETH from the fill" ); @@ -3230,9 +3234,8 @@ async fn pswap_chain_tracking_test(#[case] note_type: NoteType) { alice_account .vault() .get_balance(AssetId::new_fungible(btc_faucet.id())) - .unwrap() - .as_u64(), - MINT_AMOUNT - 50, + .unwrap(), + AssetAmount::new(MINT_AMOUNT - 50).unwrap(), "Alice's BTC should reflect 50 paid out and 50 reclaimed" ); } @@ -3315,7 +3318,12 @@ async fn pswap_full_fill_chain_tracking_test(#[case] note_type: NoteType) { // Bob full-fills: consumes the entire 50 ETH side → only a payback note is emitted. let consume_request = TransactionRequestBuilder::new() - .build_pswap_consume(&pswap_note, bob_wallet.id(), requested_amount, 0) + .build_pswap_consume( + &pswap_note, + bob_wallet.id(), + AssetAmount::new(requested_amount).unwrap(), + AssetAmount::ZERO, + ) .unwrap(); Box::pin(bob_client.submit_new_transaction(bob_wallet.id(), consume_request)) .await @@ -3356,18 +3364,16 @@ async fn pswap_full_fill_chain_tracking_test(#[case] note_type: NoteType) { alice_account .vault() .get_balance(AssetId::new_fungible(eth_faucet.id())) - .unwrap() - .as_u64(), - requested_amount, + .unwrap(), + AssetAmount::new(requested_amount).unwrap(), "Alice should have received the full 50 ETH" ); assert_eq!( alice_account .vault() .get_balance(AssetId::new_fungible(btc_faucet.id())) - .unwrap() - .as_u64(), - MINT_AMOUNT - offered_amount, + .unwrap(), + AssetAmount::new(MINT_AMOUNT - offered_amount).unwrap(), "Alice's BTC should reflect the full 100 paid out" ); } @@ -3439,7 +3445,12 @@ async fn pswap_multi_round_chain_tracking_test() { // ── Round 1: Bob fills 25 ETH → 50 BTC payout, leaving 50 BTC / 25 ETH. ── let consume_request = TransactionRequestBuilder::new() - .build_pswap_consume(&pswap_note, bob_wallet.id(), 25, 0) + .build_pswap_consume( + &pswap_note, + bob_wallet.id(), + AssetAmount::new(25).unwrap(), + AssetAmount::ZERO, + ) .unwrap(); Box::pin(bob_client.submit_new_transaction(bob_wallet.id(), consume_request)) .await @@ -3470,7 +3481,12 @@ async fn pswap_multi_round_chain_tracking_test() { // ── Round 2 (separate block): Bob consumes his own remainder, filling 10 ETH → 20 BTC, // leaving 30 BTC / 15 ETH. ── let consume_request = TransactionRequestBuilder::new() - .build_pswap_consume(&remainder_r1, bob_wallet.id(), 10, 0) + .build_pswap_consume( + &remainder_r1, + bob_wallet.id(), + AssetAmount::new(10).unwrap(), + AssetAmount::ZERO, + ) .unwrap(); Box::pin(bob_client.submit_new_transaction(bob_wallet.id(), consume_request)) .await @@ -3514,9 +3530,8 @@ async fn pswap_multi_round_chain_tracking_test() { alice_account .vault() .get_balance(AssetId::new_fungible(eth_faucet.id())) - .unwrap() - .as_u64(), - 35, + .unwrap(), + AssetAmount::new(35).unwrap(), "Alice should have received 25 + 10 = 35 ETH across both rounds" ); // BTC: 100 locked − 70 paid out (50 + 20) + 30 reclaimed = MINT − 70. @@ -3524,30 +3539,21 @@ async fn pswap_multi_round_chain_tracking_test() { alice_account .vault() .get_balance(AssetId::new_fungible(btc_faucet.id())) - .unwrap() - .as_u64(), - MINT_AMOUNT - 70, + .unwrap(), + AssetAmount::new(MINT_AMOUNT - 70).unwrap(), "Alice's BTC should reflect 70 paid out and 30 reclaimed" ); // Bob received 50 + 20 = 70 BTC and paid 25 + 10 = 35 ETH across both fills. let bob_account = bob_client.get_account(bob_wallet.id()).await.unwrap().unwrap(); assert_eq!( - bob_account - .vault() - .get_balance(AssetId::new_fungible(btc_faucet.id())) - .unwrap() - .as_u64(), - 70, + bob_account.vault().get_balance(AssetId::new_fungible(btc_faucet.id())).unwrap(), + AssetAmount::new(70).unwrap(), "Bob should have received 70 BTC across both fills" ); assert_eq!( - bob_account - .vault() - .get_balance(AssetId::new_fungible(eth_faucet.id())) - .unwrap() - .as_u64(), - MINT_AMOUNT - 35, + bob_account.vault().get_balance(AssetId::new_fungible(eth_faucet.id())).unwrap(), + AssetAmount::new(MINT_AMOUNT - 35).unwrap(), "Bob should have paid 35 ETH across both fills" ); } diff --git a/crates/testing/miden-client-tests/src/tests/batch.rs b/crates/testing/miden-client-tests/src/tests/batch.rs index e5d3133508..c7f94d171d 100644 --- a/crates/testing/miden-client-tests/src/tests/batch.rs +++ b/crates/testing/miden-client-tests/src/tests/batch.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use miden_client::ClientError; use miden_client::account::AccountType; -use miden_client::asset::{Asset, FungibleAsset}; +use miden_client::asset::{Asset, AssetAmount, FungibleAsset}; use miden_client::auth::RPO_FALCON_SCHEME_ID; use miden_client::builder::ClientBuilder; use miden_client::keystore::FilesystemKeyStore; @@ -607,8 +607,12 @@ async fn batch_builder_cross_account_note_flow() { .get_balance(faucet_account_id) .await .unwrap(); - assert_eq!(a_balance, 0, "A should have sent all its balance"); - assert_eq!(b_balance, 2 * MINT_AMOUNT, "B should hold its prior MINT_AMOUNT + A's transfer"); + assert_eq!(a_balance, AssetAmount::ZERO, "A should have sent all its balance"); + assert_eq!( + b_balance, + AssetAmount::new(2 * MINT_AMOUNT).unwrap(), + "B should hold its prior MINT_AMOUNT + A's transfer" + ); } /// The duplicate-input-note check is global to the batch: a note consumed by `tx_a` (account A) diff --git a/scripts/start-test-node.sh b/scripts/start-test-node.sh index 8be2690871..9b66724b32 100755 --- a/scripts/start-test-node.sh +++ b/scripts/start-test-node.sh @@ -54,7 +54,7 @@ else NODE_SOURCE="registry" NODE_VERSION="$(awk -F'"' '/^name = "miden-node-proto-build"$/ { getline; print $2; exit }' "$ROOT/Cargo.lock")" [ -n "$NODE_VERSION" ] || { - echo "error: no 0xMiden/miden-node git source and no miden-node-proto-build version in Cargo.lock" >&2 + echo "error: no 0xMiden/node git source and no miden-node-proto-build version in Cargo.lock" >&2 exit 1 } NODE_REV="v$NODE_VERSION" From a302d7cc22a3bb79c94d8c37b750eb742719d8ad Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Thu, 16 Jul 2026 16:13:54 -0300 Subject: [PATCH 13/20] fix: failing integration test --- Cargo.lock | 1 + .../src/asm/PASS_THROUGH.masm | 55 +++++++++++-------- .../src/asm/custom_p2id.masm | 4 +- bin/integration-tests/src/tests/client.rs | 5 +- .../src/tests/custom_transaction.rs | 6 +- bin/integration-tests/src/tests/fpi.rs | 21 +++++-- .../src/tests/network_fpi.rs | 1 + .../src/tests/network_transaction.rs | 9 ++- .../src/tests/pass_through.rs | 25 ++++++--- bin/miden-cli/Cargo.toml | 15 ++--- bin/miden-cli/src/commands/call.rs | 2 +- bin/miden-cli/tests/cli.rs | 15 ++++- ...test_cli_advice_inputs_expect_failure.masm | 3 +- ...test_cli_advice_inputs_expect_success.masm | 3 +- 14 files changed, 110 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 39dc8f992e..5aec6c67c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2066,6 +2066,7 @@ dependencies = [ "miden-client", "miden-client-sqlite-store", "miden-debug", + "miden-mast-package", "midenc-hir-type", "miette", "predicates", diff --git a/bin/integration-tests/src/asm/PASS_THROUGH.masm b/bin/integration-tests/src/asm/PASS_THROUGH.masm index d543a63615..65d5e18840 100644 --- a/bin/integration-tests/src/asm/PASS_THROUGH.masm +++ b/bin/integration-tests/src/asm/PASS_THROUGH.masm @@ -1,5 +1,6 @@ use miden::protocol::active_note -use miden::protocol::output_note +use miden::standards::wallets::basic as basic_wallet +use miden::standards::note::note_creator # ERRORS # ================================================================================================= @@ -10,6 +11,10 @@ const ERR_PASS_THROUGH_WRONG_NUMBER_OF_STORAGE_ITEMS="wrong number of storage it #! PASS_THROUGH script: #! Creates a note consumable by pass-through, containing the same asset. #! +#! Requires that the account exposes: +#! - miden::standards::note::note_creator::create_note procedure. +#! - miden::standards::wallets::basic::move_asset_to_note procedure. +#! #! Inputs: [] #! Outputs: [] #! @@ -30,37 +35,43 @@ pub proc main # make sure the number of storage items is 14 eq.14 assert.err=ERR_PASS_THROUGH_WRONG_NUMBER_OF_STORAGE_ITEMS # => [storage_ptr] - - # Load in reverse order so after output_note::create we get [note_idx, ASSET_KEY, ASSET_VALUE] - # load ASSET_VALUE (deepest on stack) drop - padw mem_loadw_le.4 - # => [ASSET_VALUE] + # => [] - # load ASSET_KEY on top - padw mem_loadw_le.0 - # => [ASSET_KEY, ASSET_VALUE] + # move the incoming note's assets into the account vault so they can be + # moved into the output note from the account context + exec.basic_wallet::add_assets_to_account + # => [] + + # pad the stack for the create_note call + padw padw push.0.0 + # => [pad(10)] - # load RECIPIENT on top + # load RECIPIENT padw mem_loadw_le.8 - # => [RECIPIENT, ASSET_KEY, ASSET_VALUE] + # => [RECIPIENT, pad(10)] # load note_type and tag from memory mem_load.12 mem_load.13 - # => [tag, note_type, RECIPIENT, ASSET_KEY, ASSET_VALUE] + # => [tag, note_type, RECIPIENT, pad(10)] + + # create a note using storage (note creation must originate from the account context) + call.note_creator::create_note + # => [note_idx, pad(15)] - # create a note using storage - # Inputs: [tag, note_type, RECIPIENT] - exec.output_note::create - # => [note_idx, ASSET_KEY, ASSET_VALUE] + # keep note_idx, drop 8 pad elements above it + movdn.8 dropw dropw + # => [note_idx, pad(7)] - # rearrange stack for add_asset which expects [ASSET_KEY, ASSET_VALUE, note_idx] - movdn.8 - # => [ASSET_KEY, ASSET_VALUE, note_idx] + # load ASSET_VALUE and ASSET_KEY + padw mem_loadw_le.4 + # => [ASSET_VALUE, note_idx, pad(7)] + padw mem_loadw_le.0 + # => [ASSET_KEY, ASSET_VALUE, note_idx, pad(7)] - # add asset to the note - exec.output_note::add_asset - # => [...] + # move the asset from the account vault into the created note + call.basic_wallet::move_asset_to_note + # => [pad(16)] # clean stack dropw dropw dropw dropw diff --git a/bin/integration-tests/src/asm/custom_p2id.masm b/bin/integration-tests/src/asm/custom_p2id.masm index 3759df0eba..8e7c7be7d5 100644 --- a/bin/integration-tests/src/asm/custom_p2id.masm +++ b/bin/integration-tests/src/asm/custom_p2id.masm @@ -10,7 +10,7 @@ use miden::protocol::active_account use miden::protocol::account_id use miden::protocol::active_note -use miden::standards::wallets::basic->basic_wallet +use miden::standards::wallets::basic as basic_wallet use miden::core::mem @note_script @@ -64,7 +64,7 @@ pub proc main # => [account_id_suffix, account_id_prefix, target_account_id_suffix, target_account_id_prefix] # ensure account_id = target_account_id, fails otherwise - exec.account_id::is_equal assert.err="P2ID's target account address and transaction address do not match" + exec.account_id::eq assert.err="P2ID's target account address and transaction address do not match" # => [] exec.basic_wallet::add_assets_to_account diff --git a/bin/integration-tests/src/tests/client.rs b/bin/integration-tests/src/tests/client.rs index 2c5afe492f..d1a2fb6f22 100644 --- a/bin/integration-tests/src/tests/client.rs +++ b/bin/integration-tests/src/tests/client.rs @@ -1500,6 +1500,7 @@ pub async fn test_unused_rpc_api(client_config: ClientConfig) -> Result<()> { const MAP_SLOT = word("miden::testing::client::map") + @account_procedure pub proc update_map push.1.2.3.4 # => [VALUE] @@ -1539,7 +1540,9 @@ pub async fn test_unused_rpc_api(client_config: ClientConfig) -> Result<()> { " use custom_library::set_map_item_library - begin + @transaction_script + + pub proc main call.set_map_item_library::update_map end ", diff --git a/bin/integration-tests/src/tests/custom_transaction.rs b/bin/integration-tests/src/tests/custom_transaction.rs index 4adff47c4b..7e2b258f80 100644 --- a/bin/integration-tests/src/tests/custom_transaction.rs +++ b/bin/integration-tests/src/tests/custom_transaction.rs @@ -85,7 +85,8 @@ pub async fn test_transaction_request(client_config: ClientConfig) -> Result<()> advice_map.insert(note_args_commitment, NOTE_ARGS.to_vec()); let code = " - begin + @transaction_script + pub proc main # We use the script argument to store the expected value to be compared push.1.2.3.4 # => [[1,2,3,4], TX_SCRIPT_ARG] @@ -195,7 +196,8 @@ pub async fn test_merkle_store(client_config: ClientConfig) -> Result<()> { let mut code = format!( " - begin + @transaction_script + pub proc main # leaf count -> mem[4000][0] push.{num_leaves} push.4000 mem_store diff --git a/bin/integration-tests/src/tests/fpi.rs b/bin/integration-tests/src/tests/fpi.rs index 93aceb9f2b..81d9ef0e8b 100644 --- a/bin/integration-tests/src/tests/fpi.rs +++ b/bin/integration-tests/src/tests/fpi.rs @@ -67,6 +67,7 @@ pub async fn test_fpi_execute_program(client_config: ClientConfig) -> Result<()> AccountType::Public, " use miden::protocol::active_account + @account_procedure pub proc get_fpi_map_item # inputs are passed as foreign_procedure_inputs: # [slot_id_prefix, slot_id_suffix, KEY, pad(10)] @@ -82,7 +83,8 @@ pub async fn test_fpi_execute_program(client_config: ClientConfig) -> Result<()> use miden::protocol::tx use miden::core::sys const MAP_STORAGE_SLOT = word(\"{MAP_SLOT_NAME}\") - begin + @transaction_script + pub proc main # pad the stack for the foreign procedure inputs padw padw push.0.0 @@ -159,6 +161,7 @@ pub async fn test_nested_fpi_calls(client_config: ClientConfig) -> Result<()> { AccountType::Public, " use miden::protocol::active_account + @account_procedure pub proc get_fpi_map_item # inputs are passed as foreign_procedure_inputs: # [slot_id_prefix, slot_id_suffix, KEY, pad(10)] @@ -179,6 +182,7 @@ pub async fn test_nested_fpi_calls(client_config: ClientConfig) -> Result<()> { use miden::protocol::tx use miden::core::sys const STORAGE_MAP_SLOT = word(\"{MAP_SLOT_NAME}\") + @account_procedure pub proc get_fpi_map_item # The outer foreign procedure receives foreign_procedure_inputs(16) on the stack. # We need to set up the inner FPI call with map key and slot as inputs. @@ -225,7 +229,8 @@ pub async fn test_nested_fpi_calls(client_config: ClientConfig) -> Result<()> { " use miden::protocol::tx use miden::core::sys - begin + @transaction_script + pub proc main # pad the stack for the outer foreign procedure inputs (it doesn't use inputs directly) padw padw padw push.0.0.0.0 @@ -298,6 +303,7 @@ pub async fn test_lazy_fpi_loading(client_config: ClientConfig) -> Result<()> { AccountType::Public, format!( r#" + @account_procedure pub proc get_constant push.{constant_value} swapw dropw @@ -312,7 +318,8 @@ pub async fn test_lazy_fpi_loading(client_config: ClientConfig) -> Result<()> { let tx_script = format!( " use miden::protocol::tx - begin + @transaction_script + pub proc main push.{proc_root} push.{account_id_prefix} push.{account_id_suffix} exec.tx::execute_foreign_procedure @@ -373,6 +380,7 @@ pub async fn test_lazy_fpi_loading_with_storage_map(client_config: ClientConfig) format!( r#" const STORAGE_MAP_SLOT = word("{MAP_SLOT_NAME}") + @account_procedure pub proc get_fpi_map_item push.{map_key} push.STORAGE_MAP_SLOT[0..2] @@ -390,7 +398,8 @@ pub async fn test_lazy_fpi_loading_with_storage_map(client_config: ClientConfig) let tx_script = format!( " use miden::protocol::tx - begin + @transaction_script + pub proc main push.{proc_root} push.{account_id_prefix} push.{account_id_suffix} exec.tx::execute_foreign_procedure @@ -449,6 +458,7 @@ async fn standard_fpi( account_type, " use miden::protocol::active_account + @account_procedure pub proc get_fpi_map_item # inputs are passed as foreign_procedure_inputs: # [slot_id_prefix, slot_id_suffix, KEY, pad(10)] @@ -468,7 +478,8 @@ async fn standard_fpi( use miden::protocol::tx use miden::core::sys const STORAGE_MAP_SLOT = word(\"{MAP_SLOT_NAME}\") - begin + @transaction_script + pub proc main # pad the stack for the foreign procedure inputs padw padw push.0.0 diff --git a/bin/integration-tests/src/tests/network_fpi.rs b/bin/integration-tests/src/tests/network_fpi.rs index 30b04fd643..666689d024 100644 --- a/bin/integration-tests/src/tests/network_fpi.rs +++ b/bin/integration-tests/src/tests/network_fpi.rs @@ -42,6 +42,7 @@ pub async fn test_network_fpi(client_config: ClientConfig) -> Result<()> { #! Inputs: [pad(16)] #! Outputs: [VALUE, pad(12)] + @account_procedure pub proc get_fpi_map_item # map key push.{map_key} diff --git a/bin/integration-tests/src/tests/network_transaction.rs b/bin/integration-tests/src/tests/network_transaction.rs index eed7f327f9..beffbaffef 100644 --- a/bin/integration-tests/src/tests/network_transaction.rs +++ b/bin/integration-tests/src/tests/network_transaction.rs @@ -81,12 +81,14 @@ const COUNTER_CONTRACT: &str = r#" const COUNTER_SLOT = word("miden::testing::counter_contract::counter") # => [] + @account_procedure pub proc get_count push.COUNTER_SLOT[0..2] exec.active_account::get_item exec.sys::truncate_stack end # => [] + @account_procedure pub proc increment_count push.COUNTER_SLOT[0..2] exec.active_account::get_item # => [count] @@ -120,7 +122,8 @@ const INCR_NOTE_SCRIPT_CODE: &str = " // procedure already increments the nonce, so the script itself needs // only to satisfy the builder's requirement that _some_ user code runs. const NOOP_TX_SCRIPT: &str = " - begin + @transaction_script + pub proc main push.0 drop end "; @@ -136,7 +139,7 @@ const NON_STANDARD_CLAIM_NOTE_SCRIPT: &str = r#" use miden::protocol::active_account use miden::protocol::account_id use miden::protocol::active_note - use miden::standards::wallets::basic->basic_wallet + use miden::standards::wallets::basic as basic_wallet @note_script pub proc main @@ -162,7 +165,7 @@ const NON_STANDARD_CLAIM_NOTE_SCRIPT: &str = r#" exec.active_account::get_id # => [account_id_suffix, account_id_prefix, target_account_id_suffix, target_account_id_prefix] - exec.account_id::is_equal assert.err="consumer is not the note's target account" + exec.account_id::eq assert.err="consumer is not the note's target account" # => [] # move all of the note's assets into the consuming account's vault diff --git a/bin/integration-tests/src/tests/pass_through.rs b/bin/integration-tests/src/tests/pass_through.rs index fb8cfe7bf2..05de62596b 100644 --- a/bin/integration-tests/src/tests/pass_through.rs +++ b/bin/integration-tests/src/tests/pass_through.rs @@ -14,12 +14,12 @@ use miden_client::asset::{Asset, FungibleAsset}; use miden_client::auth::{ Approver, AuthSchemeId, + AuthSecretKey, AuthSingleSigAcl, AuthSingleSigAclConfig, TransactionAuthenticator, }; use miden_client::crypto::FeltRng; -use miden_client::crypto::rpo_falcon512::SecretKey; use miden_client::note::{ Note, NoteAssets, @@ -82,7 +82,7 @@ pub async fn test_pass_through(client_config: ClientConfig) -> Result<()> { ) .await?; - let pass_through_account = create_pass_through_account(&mut client).await?; + let pass_through_account = create_pass_through_account(&mut client, &authenticator_1).await?; // Create client with faucets BTC faucet let (btc_faucet_account, ..) = insert_new_fungible_faucet( @@ -201,19 +201,25 @@ pub async fn test_pass_through(client_config: ClientConfig) -> Result<()> { async fn create_pass_through_account( client: &mut Client, + keystore: &FilesystemKeyStore, ) -> Result { let mut init_seed = [0u8; 32]; client.rng().fill_bytes(&mut init_seed); - let key_pair = SecretKey::with_rng(client.rng()); + let key_pair = AuthSecretKey::new_falcon512_poseidon2(); let pub_key = key_pair.public_key().to_commitment(); - let acl_config = AuthSingleSigAclConfig::new(BTreeSet::new()).unwrap(); - - let auth_component = AuthSingleSigAcl::new( - Approver::new(pub_key.into(), AuthSchemeId::Falcon512Poseidon2), - acl_config, - ); + // The pass-through consumption must not change the account commitment, so the wallet + // procedures invoked by the PASS_THROUGH note script are exempt from signature checks. + let exempt_procedures = BTreeSet::from([ + BasicWallet::receive_asset_root(), + BasicWallet::move_asset_to_note_root(), + BasicWallet::create_note_root(), + ]); + let acl_config = AuthSingleSigAclConfig::new(exempt_procedures).unwrap(); + + let auth_component = + AuthSingleSigAcl::new(Approver::new(pub_key, AuthSchemeId::Falcon512Poseidon2), acl_config); let account = AccountBuilder::new(init_seed) .account_type(AccountType::Private) .with_auth_component(auth_component) @@ -221,6 +227,7 @@ async fn create_pass_through_account( .build_with_schema_commitment() .unwrap(); + keystore.add_key(&key_pair, account.id()).await?; client.add_account(&account, false).await?; Ok(account) } diff --git a/bin/miden-cli/Cargo.toml b/bin/miden-cli/Cargo.toml index 18b3134223..92c8192200 100644 --- a/bin/miden-cli/Cargo.toml +++ b/bin/miden-cli/Cargo.toml @@ -45,13 +45,14 @@ tracing-subscriber = { default-features = true, workspace = true } miden-client = { features = ["std"], workspace = true } [dev-dependencies] -anyhow = { workspace = true } -assert_cmd = { workspace = true } -miden-client = { features = ["testing"], workspace = true } -midenc-hir-type = { version = "0.9" } -predicates = { workspace = true } -regex = { workspace = true } -serial_test = { features = ["file_locks"], version = "3.0" } +anyhow = { workspace = true } +assert_cmd = { workspace = true } +miden-client = { features = ["testing"], workspace = true } +miden-mast-package = { version = "0.25" } +midenc-hir-type = { version = "0.9" } +predicates = { workspace = true } +regex = { workspace = true } +serial_test = { features = ["file_locks"], version = "3.0" } [lints] workspace = true diff --git a/bin/miden-cli/src/commands/call.rs b/bin/miden-cli/src/commands/call.rs index e0ead5e86e..9cbe1eea71 100644 --- a/bin/miden-cli/src/commands/call.rs +++ b/bin/miden-cli/src/commands/call.rs @@ -280,7 +280,7 @@ fn generate_tx_script( ))); } - let mut script = String::from("begin\n"); + let mut script = String::from("@transaction_script\npub proc main\n"); // Push args in reverse so the first arg ends up on top. for arg in args.iter().rev() { diff --git a/bin/miden-cli/tests/cli.rs b/bin/miden-cli/tests/cli.rs index ba10d65c84..fa5973a770 100644 --- a/bin/miden-cli/tests/cli.rs +++ b/bin/miden-cli/tests/cli.rs @@ -1598,10 +1598,12 @@ fn build_call_test_masp(out_path: &Path) { const STORED_VALUE = word("miden::testing::call_test::stored_value") + @account_procedure pub proc add add end + @account_procedure pub proc set_value push.STORED_VALUE[0..2] exec.native_account::set_item @@ -1609,6 +1611,7 @@ fn build_call_test_masp(out_path: &Path) { exec.sys::truncate_stack end + @account_procedure pub proc read_advice # Look up a fixed key in the advice map and return the sum of its two values. push.268435456.0.0.0 @@ -1644,14 +1647,24 @@ fn build_call_test_masp(out_path: &Path) { let metadata = AccountComponentMetadata::new("call-test").with_storage_schema(storage_schema); let exports = call_test_exports(&library); + let modules = library.module_infos().map(|module_info| { + miden_mast_package::PackageModule::new( + std::sync::Arc::from(module_info.path().to_path_buf().into_boxed_path()), + module_info + .submodules() + .iter() + .map(|submodule| miden_mast_package::PackageSubmodule::new(submodule.name.clone())), + ) + }); let section = Section::new(SectionId::ACCOUNT_COMPONENT_METADATA, metadata.to_bytes()); - let mut package = Package::create( + let mut package = Package::create_with_modules( metadata.name().to_string().into(), metadata.version().clone(), TargetType::AccountComponent, library.mast_forest().clone(), exports, + modules, [], ) .expect("failed to create call-test package"); diff --git a/bin/miden-cli/tests/files/test_cli_advice_inputs_expect_failure.masm b/bin/miden-cli/tests/files/test_cli_advice_inputs_expect_failure.masm index 64f95df70d..2299f57df4 100644 --- a/bin/miden-cli/tests/files/test_cli_advice_inputs_expect_failure.masm +++ b/bin/miden-cli/tests/files/test_cli_advice_inputs_expect_failure.masm @@ -2,7 +2,8 @@ # This file is identical to `success.masm` except for the item being accessed in the input file. # This script should fail. -begin +@transaction_script +pub proc main # push the key for the advice map onto the stack ("0x0000000000000000000000000000000000000000000000000000000000000000") push.0.0.0.0 diff --git a/bin/miden-cli/tests/files/test_cli_advice_inputs_expect_success.masm b/bin/miden-cli/tests/files/test_cli_advice_inputs_expect_success.masm index 214b575e83..bf4d4bd8d8 100644 --- a/bin/miden-cli/tests/files/test_cli_advice_inputs_expect_success.masm +++ b/bin/miden-cli/tests/files/test_cli_advice_inputs_expect_success.masm @@ -3,7 +3,8 @@ # This script should succeed. -begin +@transaction_script +pub proc main # push the key for the advice map onto the stack ("0x0000000000000000000000000000000000000000000000000000001000000000") push.268435456.0.0.0 From 95910fbd11448611f32faa93cf4381cece89325d Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Thu, 16 Jul 2026 16:42:25 -0300 Subject: [PATCH 14/20] chore: switch back to crates.io dependencies --- Cargo.lock | 36 ++++++++++++++++++++++-------------- Cargo.toml | 14 +++++++------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5aec6c67c3..5d25f12fe1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1058,7 +1058,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1903,7 +1903,8 @@ dependencies = [ [[package]] name = "miden-agglayer" version = "0.16.0-alpha.3" -source = "git+https://github.com/0xMiden/protocol.git?tag=v0.16.0-alpha.3#47112cd2539b14e1d8a5955612dc833b4348b8f3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91b2483bab6c96fe7116318928c46266fc28bf321da104fcf341d31c94908266" dependencies = [ "alloy-sol-types", "fs-err", @@ -1993,7 +1994,8 @@ dependencies = [ [[package]] name = "miden-block-prover" version = "0.16.0-alpha.3" -source = "git+https://github.com/0xMiden/protocol.git?tag=v0.16.0-alpha.3#47112cd2539b14e1d8a5955612dc833b4348b8f3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6d62529604f897a5abc0f07f1fcb3c5a9a4d70a417a3f95882eebfbfe23ab9" dependencies = [ "miden-protocol", "thiserror", @@ -2428,8 +2430,9 @@ dependencies = [ [[package]] name = "miden-node-proto-build" -version = "0.16.0-alpha.1" -source = "git+https://github.com/0xMiden/node.git?tag=v0.16.0-alpha.1#895091d36d4bfdb7ee8040dad449bb5db5219272" +version = "0.16.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1027cbad81e3f7a59b50be02cd27c22766573388fce96a807615f2bec0d5fc" dependencies = [ "build-rs", "codegen", @@ -2506,7 +2509,8 @@ dependencies = [ [[package]] name = "miden-protocol" version = "0.16.0-alpha.3" -source = "git+https://github.com/0xMiden/protocol.git?tag=v0.16.0-alpha.3#47112cd2539b14e1d8a5955612dc833b4348b8f3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27ab6386191d7c22a19ca892947dfe317d0df424d456383892108070776896e" dependencies = [ "bech32", "fs-err", @@ -2572,7 +2576,8 @@ dependencies = [ [[package]] name = "miden-standards" version = "0.16.0-alpha.3" -source = "git+https://github.com/0xMiden/protocol.git?tag=v0.16.0-alpha.3#47112cd2539b14e1d8a5955612dc833b4348b8f3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eec9815e6dcb10fe7b1f9cfc0bd0807e50d8c58602c927523ed85ce6c4d8a841" dependencies = [ "bon", "fs-err", @@ -2612,8 +2617,9 @@ dependencies = [ [[package]] name = "miden-testing" -version = "0.16.0-alpha.3" -source = "git+https://github.com/0xMiden/protocol.git?tag=v0.16.0-alpha.3#47112cd2539b14e1d8a5955612dc833b4348b8f3" +version = "0.16.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610ba28ab9d73da0fcde6aa2389b2029e096df0c7248fde558d7d44be67ec8f7" dependencies = [ "anyhow", "itertools", @@ -2633,7 +2639,8 @@ dependencies = [ [[package]] name = "miden-tx" version = "0.16.0-alpha.3" -source = "git+https://github.com/0xMiden/protocol.git?tag=v0.16.0-alpha.3#47112cd2539b14e1d8a5955612dc833b4348b8f3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c199d85857144615ce44e73c3dadb57f248b5e46e0cbabd315f8f40a901de34b" dependencies = [ "miden-processor", "miden-protocol", @@ -2645,7 +2652,8 @@ dependencies = [ [[package]] name = "miden-tx-batch" version = "0.16.0-alpha.3" -source = "git+https://github.com/0xMiden/protocol.git?tag=v0.16.0-alpha.3#47112cd2539b14e1d8a5955612dc833b4348b8f3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e3cb08d677481bd84cecef8dc33a9433b1017d55662128717654779cf5bb000" dependencies = [ "miden-processor", "miden-protocol", @@ -3911,7 +3919,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4434,7 +4442,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5238,7 +5246,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7028f3754a..b9d2667a16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,15 +40,15 @@ miden-client = { default-features = false, path = "crates/rust-clie miden-client-sqlite-store = { default-features = false, path = "crates/sqlite-store", version = "0.16.0-alpha.1" } # Miden protocol dependencies -miden-agglayer = { default-features = false, git = "https://github.com/0xMiden/protocol.git", tag = "v0.16.0-alpha.3" } # TODO: replace with crates.io once protocol has proper release -miden-protocol = { default-features = false, git = "https://github.com/0xMiden/protocol.git", tag = "v0.16.0-alpha.3" } # TODO: replace with crates.io once protocol has proper release -miden-standards = { default-features = false, git = "https://github.com/0xMiden/protocol.git", tag = "v0.16.0-alpha.3" } # TODO: replace with crates.io once protocol has proper release -miden-testing = { default-features = false, git = "https://github.com/0xMiden/protocol.git", tag = "v0.16.0-alpha.3" } # TODO: replace with crates.io once protocol has proper release -miden-tx = { default-features = false, git = "https://github.com/0xMiden/protocol.git", tag = "v0.16.0-alpha.3" } # TODO: replace with crates.io once protocol has proper release -miden-tx-batch = { default-features = false, git = "https://github.com/0xMiden/protocol.git", tag = "v0.16.0-alpha.3" } # TODO: replace with crates.io once protocol has proper release +miden-agglayer = { default-features = false, version = "0.16.0-alpha.3" } +miden-protocol = { default-features = false, version = "0.16.0-alpha.3" } +miden-standards = { default-features = false, version = "0.16.0-alpha.3" } +miden-testing = { default-features = false, version = "0.16.0-alpha.2" } +miden-tx = { default-features = false, version = "0.16.0-alpha.3" } +miden-tx-batch = { default-features = false, version = "0.16.0-alpha.3" } # Miden node dependencies -miden-node-proto-build = { default-features = false, git = "https://github.com/0xMiden/node.git", tag = "v0.16.0-alpha.1" } # TODO: replace with crates.io once node has proper release +miden-node-proto-build = { default-features = false, version = "0.16.0-alpha.2" } miden-note-transport-proto-build = { default-features = false, version = "0.4.1" } # Miden debug dependency From be381b3357ea12e51e58baa2320575bd02c52a37 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Thu, 16 Jul 2026 17:06:04 -0300 Subject: [PATCH 15/20] fix: miden-bench tests --- bin/miden-bench/src/generators/large_account.rs | 2 ++ bin/miden-bench/src/masm.rs | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bin/miden-bench/src/generators/large_account.rs b/bin/miden-bench/src/generators/large_account.rs index 9cfcea3ce1..deb6355dae 100644 --- a/bin/miden-bench/src/generators/large_account.rs +++ b/bin/miden-bench/src/generators/large_account.rs @@ -39,6 +39,7 @@ pub fn generate_reader_component_code(slots: &[SlotDescriptor]) -> String { # Reads an item from map storage slot {i}. # Stack input: [KEY] # Stack output: [VALUE] +@account_procedure pub proc get_map_item_slot_{i} push.MAP_SLOT_{i}[0..2] exec.::miden::protocol::active_account::get_map_item @@ -55,6 +56,7 @@ end # Reads the value from storage slot {i}. # Stack input: [] # Stack output: [VALUE] +@account_procedure pub proc get_value_slot_{i} push.SLOT_{i}[0..2] exec.::miden::protocol::active_account::get_item diff --git a/bin/miden-bench/src/masm.rs b/bin/miden-bench/src/masm.rs index ef55b8cd14..fb3b9648a6 100644 --- a/bin/miden-bench/src/masm.rs +++ b/bin/miden-bench/src/masm.rs @@ -43,7 +43,9 @@ fn write_read_op_instructions(script: &mut String, op: &ReadOp) { fn generate_storage_read_script(read_ops: &[ReadOp]) -> String { let mut script = String::from( "use bench_reader::storage_reader -begin + +@transaction_script +pub proc main ", ); @@ -105,6 +107,7 @@ pub fn generate_expansion_component_code(num_slots: usize) -> String { # Sets an item in storage slot {i}. # Stack input: [KEY, VALUE, ...] # Stack output: [...] +@account_procedure pub proc set_item_slot_{i} push.MAP_SLOT_{i}[0..2] # Stack: [slot_suffix, slot_prefix, KEY, VALUE, ...] @@ -125,7 +128,8 @@ end /// Generates MASM transaction script code that writes entries into a single storage map slot. pub fn generate_expansion_tx_script(slot_idx: usize, entries: &[([Felt; 4], [Felt; 4])]) -> String { - let mut script = String::from("use expander::storage_expander\n\nbegin\n"); + let mut script = + String::from("use expander::storage_expander\n\n@transaction_script\npub proc main\n"); let procedure_name = format!("set_item_slot_{slot_idx}"); for (key, value) in entries { From b813d10734bc6c8a92f5729d80af0fde020f3984 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Thu, 16 Jul 2026 18:41:37 -0300 Subject: [PATCH 16/20] fix: failing ntx test --- CHANGELOG.md | 4 ++++ bin/integration-tests/src/tests/network_transaction.rs | 8 +++++--- crates/rust-client/src/transaction/mod.rs | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42ae5258ba..3a601dc742 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ * [BREAKING][param][store] `Store::insert_block_header` now takes a `nodes` argument and persists the header with its MMR authentication nodes in a single transaction; the standalone `Store::insert_partial_blockchain_nodes` is removed. Header-only inserts (e.g. genesis) pass an empty slice ([#2294](https://github.com/0xMiden/rust-sdk/pull/2294)). * [BREAKING][behavior][store] The `ConsumedExternal` note-metadata layout added in [#2308](https://github.com/0xMiden/rust-sdk/pull/2308) is now the only supported serialized format. The backward-compatible decoding of the older metadata-less layout is removed, so existing stores are not compatible and must be recreated ([#2313](https://github.com/0xMiden/rust-sdk/pull/2313)). +### Changes + +* [rust] Re-exported `ExpirationTransactionScript` from `miden_client::transaction`. Network accounts must allowlist its script root (via `AuthNetworkAccount::with_allowed_tx_scripts`) for the node's network transaction builder to execute transactions against them ([#2290](https://github.com/0xMiden/rust-sdk/pull/2290)). + ### Fixes * [FIX][store] The SQLite store now honors `StorageMapPatch` create/remove semantics: a `Create` patch on an existing map slot clears the prior entries before writing (so its root reflects only the created entries) and a `Remove` patch drops the slot's entries and collapses its root to the empty-map root ([#2290](https://github.com/0xMiden/rust-sdk/pull/2290)). diff --git a/bin/integration-tests/src/tests/network_transaction.rs b/bin/integration-tests/src/tests/network_transaction.rs index beffbaffef..ba8991ffcf 100644 --- a/bin/integration-tests/src/tests/network_transaction.rs +++ b/bin/integration-tests/src/tests/network_transaction.rs @@ -59,7 +59,7 @@ use miden_client::testing::common::{ wait_for_blocks, wait_for_tx, }; -use miden_client::transaction::TransactionRequestBuilder; +use miden_client::transaction::{ExpirationTransactionScript, TransactionRequestBuilder}; use miden_client::{Felt, Word, ZERO}; use rand::{Rng, RngExt}; @@ -185,7 +185,8 @@ pub(crate) async fn deploy_network_counter_contract( let roots = allowed_note_script_roots.iter().copied().collect::>(); let auth = AuthNetworkAccount::with_allowed_notes(roots) .map_err(|err| anyhow::anyhow!(err)) - .context("failed to build network account auth component")?; + .context("failed to build network account auth component")? + .with_allowed_tx_scripts(BTreeSet::from([ExpirationTransactionScript::script_root()])); deploy_counter_with_auth(client, auth).await } @@ -257,7 +258,8 @@ async fn deploy_network_fungible_faucet( // with no tx script. The scriptless deploy transaction below is authorized by this same auth. let allowed_roots = [MintNote::script_root()].into_iter().collect::>(); let network_auth = AuthNetworkAccount::with_allowed_notes(allowed_roots) - .map_err(|err| anyhow!("failed to build faucet network-account auth: {err}"))?; + .map_err(|err| anyhow!("failed to build faucet network-account auth: {err}"))? + .with_allowed_tx_scripts(BTreeSet::from([ExpirationTransactionScript::script_root()])); let mut init_seed = [0u8; 32]; client.rng().fill_bytes(&mut init_seed); diff --git a/crates/rust-client/src/transaction/mod.rs b/crates/rust-client/src/transaction/mod.rs index d4c25435db..e716417f8f 100644 --- a/crates/rust-client/src/transaction/mod.rs +++ b/crates/rust-client/src/transaction/mod.rs @@ -172,6 +172,7 @@ pub use miden_protocol::transaction::{ }; pub use miden_protocol::vm::{AdviceInputs, AdviceMap}; pub use miden_standards::account::interface::{AccountComponentInterface, AccountInterface}; +pub use miden_standards::tx_script::ExpirationTransactionScript; pub use miden_tx::auth::TransactionAuthenticator; pub use miden_tx::{ DataStoreError, From b855ef56d614f0ab864106231abe0f4a2b66dfe6 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jul 2026 10:33:16 -0300 Subject: [PATCH 17/20] chore: switch dependency source temporarily --- Cargo.lock | 35 ++++++++++++++--------------------- Cargo.toml | 12 ++++++------ 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d25f12fe1..32fb927338 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1902,9 +1902,8 @@ dependencies = [ [[package]] name = "miden-agglayer" -version = "0.16.0-alpha.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91b2483bab6c96fe7116318928c46266fc28bf321da104fcf341d31c94908266" +version = "0.16.0-alpha.4" +source = "git+https://github.com/0xMiden/protocol.git?branch=release%2Fv0.16.0-alpha#77a30927fb376abdb7d1481cbb94423af81d7fb0" dependencies = [ "alloy-sol-types", "fs-err", @@ -1993,9 +1992,8 @@ dependencies = [ [[package]] name = "miden-block-prover" -version = "0.16.0-alpha.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6d62529604f897a5abc0f07f1fcb3c5a9a4d70a417a3f95882eebfbfe23ab9" +version = "0.16.0-alpha.4" +source = "git+https://github.com/0xMiden/protocol.git?branch=release%2Fv0.16.0-alpha#77a30927fb376abdb7d1481cbb94423af81d7fb0" dependencies = [ "miden-protocol", "thiserror", @@ -2508,9 +2506,8 @@ dependencies = [ [[package]] name = "miden-protocol" -version = "0.16.0-alpha.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27ab6386191d7c22a19ca892947dfe317d0df424d456383892108070776896e" +version = "0.16.0-alpha.4" +source = "git+https://github.com/0xMiden/protocol.git?branch=release%2Fv0.16.0-alpha#77a30927fb376abdb7d1481cbb94423af81d7fb0" dependencies = [ "bech32", "fs-err", @@ -2575,9 +2572,8 @@ dependencies = [ [[package]] name = "miden-standards" -version = "0.16.0-alpha.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eec9815e6dcb10fe7b1f9cfc0bd0807e50d8c58602c927523ed85ce6c4d8a841" +version = "0.16.0-alpha.4" +source = "git+https://github.com/0xMiden/protocol.git?branch=release%2Fv0.16.0-alpha#77a30927fb376abdb7d1481cbb94423af81d7fb0" dependencies = [ "bon", "fs-err", @@ -2617,9 +2613,8 @@ dependencies = [ [[package]] name = "miden-testing" -version = "0.16.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "610ba28ab9d73da0fcde6aa2389b2029e096df0c7248fde558d7d44be67ec8f7" +version = "0.16.0-alpha.4" +source = "git+https://github.com/0xMiden/protocol.git?branch=release%2Fv0.16.0-alpha#77a30927fb376abdb7d1481cbb94423af81d7fb0" dependencies = [ "anyhow", "itertools", @@ -2638,9 +2633,8 @@ dependencies = [ [[package]] name = "miden-tx" -version = "0.16.0-alpha.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c199d85857144615ce44e73c3dadb57f248b5e46e0cbabd315f8f40a901de34b" +version = "0.16.0-alpha.4" +source = "git+https://github.com/0xMiden/protocol.git?branch=release%2Fv0.16.0-alpha#77a30927fb376abdb7d1481cbb94423af81d7fb0" dependencies = [ "miden-processor", "miden-protocol", @@ -2651,9 +2645,8 @@ dependencies = [ [[package]] name = "miden-tx-batch" -version = "0.16.0-alpha.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3cb08d677481bd84cecef8dc33a9433b1017d55662128717654779cf5bb000" +version = "0.16.0-alpha.4" +source = "git+https://github.com/0xMiden/protocol.git?branch=release%2Fv0.16.0-alpha#77a30927fb376abdb7d1481cbb94423af81d7fb0" dependencies = [ "miden-processor", "miden-protocol", diff --git a/Cargo.toml b/Cargo.toml index b9d2667a16..d7fb541b8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,12 +40,12 @@ miden-client = { default-features = false, path = "crates/rust-clie miden-client-sqlite-store = { default-features = false, path = "crates/sqlite-store", version = "0.16.0-alpha.1" } # Miden protocol dependencies -miden-agglayer = { default-features = false, version = "0.16.0-alpha.3" } -miden-protocol = { default-features = false, version = "0.16.0-alpha.3" } -miden-standards = { default-features = false, version = "0.16.0-alpha.3" } -miden-testing = { default-features = false, version = "0.16.0-alpha.2" } -miden-tx = { default-features = false, version = "0.16.0-alpha.3" } -miden-tx-batch = { default-features = false, version = "0.16.0-alpha.3" } +miden-agglayer = { branch = "release/v0.16.0-alpha", default-features = false, git = "https://github.com/0xMiden/protocol.git" } # TODO: switch back to crates.io when released +miden-protocol = { branch = "release/v0.16.0-alpha", default-features = false, git = "https://github.com/0xMiden/protocol.git" } # TODO: switch back to crates.io when released +miden-standards = { branch = "release/v0.16.0-alpha", default-features = false, git = "https://github.com/0xMiden/protocol.git" } # TODO: switch back to crates.io when released +miden-testing = { branch = "release/v0.16.0-alpha", default-features = false, git = "https://github.com/0xMiden/protocol.git" } # TODO: switch back to crates.io when released +miden-tx = { branch = "release/v0.16.0-alpha", default-features = false, git = "https://github.com/0xMiden/protocol.git" } # TODO: switch back to crates.io when released +miden-tx-batch = { branch = "release/v0.16.0-alpha", default-features = false, git = "https://github.com/0xMiden/protocol.git" } # TODO: switch back to crates.io when released # Miden node dependencies miden-node-proto-build = { default-features = false, version = "0.16.0-alpha.2" } From ed9ee4ac17ea035bc716dc6c6d49ea3f1a8df4e1 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jul 2026 11:21:56 -0300 Subject: [PATCH 18/20] refactor: make use of NetworkAccount::builder --- CHANGELOG.md | 2 +- bin/integration-tests/src/tests/network_transaction.rs | 8 ++------ docs/external/src/rust-client/library.md | 8 ++------ 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a601dc742..1bb70f4d6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ ### Changes -* [rust] Re-exported `ExpirationTransactionScript` from `miden_client::transaction`. Network accounts must allowlist its script root (via `AuthNetworkAccount::with_allowed_tx_scripts`) for the node's network transaction builder to execute transactions against them ([#2290](https://github.com/0xMiden/rust-sdk/pull/2290)). +* [rust] Re-exported `ExpirationTransactionScript` from `miden_client::transaction`. ([#2290](https://github.com/0xMiden/rust-sdk/pull/2290)). ### Fixes diff --git a/bin/integration-tests/src/tests/network_transaction.rs b/bin/integration-tests/src/tests/network_transaction.rs index ba8991ffcf..4cb77ce207 100644 --- a/bin/integration-tests/src/tests/network_transaction.rs +++ b/bin/integration-tests/src/tests/network_transaction.rs @@ -11,6 +11,7 @@ use miden_client::account::component::{ BurnPolicy, FungibleFaucet, MintPolicy, + NetworkAccount, PausableManager, TokenName, TokenPolicyManager, @@ -257,9 +258,6 @@ async fn deploy_network_fungible_faucet( // the node uses to route MINT notes to it and enforces that only allowlisted notes are consumed // with no tx script. The scriptless deploy transaction below is authorized by this same auth. let allowed_roots = [MintNote::script_root()].into_iter().collect::>(); - let network_auth = AuthNetworkAccount::with_allowed_notes(allowed_roots) - .map_err(|err| anyhow!("failed to build faucet network-account auth: {err}"))? - .with_allowed_tx_scripts(BTreeSet::from([ExpirationTransactionScript::script_root()])); let mut init_seed = [0u8; 32]; client.rng().fill_bytes(&mut init_seed); @@ -276,9 +274,7 @@ async fn deploy_network_fungible_faucet( .active_mint_policy(MintPolicy::owner_only()) .active_burn_policy(BurnPolicy::allow_all()) .build(); - let faucet = AccountBuilder::new(init_seed) - .account_type(AccountType::Public) - .with_auth_component(network_auth) + let faucet = NetworkAccount::builder(init_seed, allowed_roots)? .with_component(faucet_component) .with_components(AccessControl::Ownable2Step { owner: owner_id }) .with_components(policy_manager) diff --git a/docs/external/src/rust-client/library.md b/docs/external/src/rust-client/library.md index 82ecd7c40a..59db50f9a3 100644 --- a/docs/external/src/rust-client/library.md +++ b/docs/external/src/rust-client/library.md @@ -104,14 +104,10 @@ The account's state is also tracked locally, but during sync the client updates ### Network accounts -A network account is a public account that the node drives automatically: it consumes matching notes on the network's behalf via network transactions (NTX). An account becomes a network account by using the `AuthNetworkAccount` auth component, which carries a standardized allowlist of note script roots. The node uses that allowlist to identify the account and route only allowlisted notes to it; the auth procedure additionally enforces that consumed notes are allowlisted and that no transaction script runs. +A network account is a public account that the node drives automatically: it consumes matching notes on the network's behalf via network transactions (NTX). A network account is built by using the `NetworkAccount::builder` method, which takes a standardized allowlist of note script roots. The node uses that allowlist to identify the account and route only allowlisted notes to it; the auth procedure additionally enforces that consumed notes are allowlisted and that no transaction script runs. ```rust -let auth = AuthNetworkAccount::with_allowlist(allowed_note_script_roots)?; - -let network_account = AccountBuilder::new(init_seed) - .account_type(AccountType::Public) // network accounts must be public - .with_auth_component(auth) +let network_account = NetworkAccount::builder(init_seed, allowed_note_script_roots)? .with_component(/* your contract component */) .build_with_schema_commitment()?; client.add_account(&network_account, false).await?; From 823ae9ecb8c84543160ee737b97518e6b04eb43a Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jul 2026 12:56:15 -0300 Subject: [PATCH 19/20] docs: set alfa release date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bb70f4d6f..8099d13ade 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.16.0-alpha.1 (TBD) +## 0.16.0-alpha.1 (2026-07-17) ### Breaking Changes From e45fb4931b3f1330236bfa48a8b63a68f4320399 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jul 2026 14:47:19 -0300 Subject: [PATCH 20/20] chore: add re-exports --- CHANGELOG.md | 2 +- crates/rust-client/src/account/mod.rs | 2 ++ crates/rust-client/src/lib.rs | 2 +- crates/rust-client/src/note/mod.rs | 1 + crates/rust-client/src/transaction/mod.rs | 5 ++++- 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8099d13ade..5da69c204f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ ### Changes -* [rust] Re-exported `ExpirationTransactionScript` from `miden_client::transaction`. ([#2290](https://github.com/0xMiden/rust-sdk/pull/2290)). +* [rust] Re-exported new upstream types reachable from the public API: `ExpirationTransactionScript` and `SendNotesTransactionScriptError` from `miden_client::transaction`, `NoteSyncHint` from `miden_client::note`, `StorageValuePatch` and `StorageMapPatchEntries` from `miden_client::account`, and `FeeParameters` and `ValidatorKeys` from `miden_client::block`. ([#2290](https://github.com/0xMiden/rust-sdk/pull/2290)). ### Fixes diff --git a/crates/rust-client/src/account/mod.rs b/crates/rust-client/src/account/mod.rs index 2217e56c89..6b934c59c7 100644 --- a/crates/rust-client/src/account/mod.rs +++ b/crates/rust-client/src/account/mod.rs @@ -66,6 +66,7 @@ pub use miden_protocol::account::{ StorageMapKey, StorageMapKeyHash, StorageMapPatch, + StorageMapPatchEntries, StorageMapWitness, StorageSlot, StorageSlotContent, @@ -73,6 +74,7 @@ pub use miden_protocol::account::{ StorageSlotName, StorageSlotPatch, StorageSlotType, + StorageValuePatch, }; pub use miden_protocol::address::{Address, AddressInterface, AddressType, NetworkId}; use miden_protocol::asset::AssetVault; diff --git a/crates/rust-client/src/lib.rs b/crates/rust-client/src/lib.rs index 25115019ce..1e244e03ea 100644 --- a/crates/rust-client/src/lib.rs +++ b/crates/rust-client/src/lib.rs @@ -232,7 +232,7 @@ pub mod auth { /// Provides types for working with blocks within the Miden network. pub mod block { - pub use miden_protocol::block::{BlockHeader, BlockNumber}; + pub use miden_protocol::block::{BlockHeader, BlockNumber, FeeParameters, ValidatorKeys}; } /// Provides cryptographic types and utilities used within the Miden rollup diff --git a/crates/rust-client/src/note/mod.rs b/crates/rust-client/src/note/mod.rs index 96a361cf88..83c4b42458 100644 --- a/crates/rust-client/src/note/mod.rs +++ b/crates/rust-client/src/note/mod.rs @@ -113,6 +113,7 @@ pub use miden_standards::note::{ NoteConsumptionStatus, NoteExecutionHint, NoteFile, + NoteSyncHint, P2idNote, P2idNoteStorage, P2ideNote, diff --git a/crates/rust-client/src/transaction/mod.rs b/crates/rust-client/src/transaction/mod.rs index e716417f8f..048e7a3300 100644 --- a/crates/rust-client/src/transaction/mod.rs +++ b/crates/rust-client/src/transaction/mod.rs @@ -172,7 +172,10 @@ pub use miden_protocol::transaction::{ }; pub use miden_protocol::vm::{AdviceInputs, AdviceMap}; pub use miden_standards::account::interface::{AccountComponentInterface, AccountInterface}; -pub use miden_standards::tx_script::ExpirationTransactionScript; +pub use miden_standards::tx_script::{ + ExpirationTransactionScript, + SendNotesTransactionScriptError, +}; pub use miden_tx::auth::TransactionAuthenticator; pub use miden_tx::{ DataStoreError,